浏览代码

Merge remote-tracking branch 'origin/master'

吴树波 1 周之前
父节点
当前提交
3792813047
共有 37 个文件被更改,包括 1510 次插入312 次删除
  1. 2 4
      fs-admin/src/main/java/com/fs/course/controller/FsCourseQuestionBankController.java
  2. 93 0
      fs-admin/src/main/resources/logback.xml
  3. 93 0
      fs-company-app/src/main/resources/logback.xml
  4. 93 0
      fs-company/src/main/resources/logback.xml
  5. 93 0
      fs-qw-api/src/main/resources/logback.xml
  6. 1 0
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  7. 93 0
      fs-qw-task/src/main/resources/logback.xml
  8. 93 0
      fs-qwhook-msg/src/main/resources/logback.xml
  9. 4 16
      fs-qwhook-sop/src/main/java/com/fs/app/controller/testController.java
  10. 93 0
      fs-qwhook-sop/src/main/resources/logback.xml
  11. 93 0
      fs-qwhook/src/main/resources/logback.xml
  12. 5 3
      fs-service-system/src/main/java/com/fs/company/mapper/CompanyUserMapper.java
  13. 23 11
      fs-service-system/src/main/java/com/fs/course/dto/FsCourseQuestionBankImportDTO.java
  14. 11 4
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseCategoryMapper.java
  15. 8 2
      fs-service-system/src/main/java/com/fs/course/service/IFsCourseQuestionBankService.java
  16. 324 102
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java
  17. 2 2
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  18. 5 0
      fs-service-system/src/main/java/com/fs/sop/domain/QwSopLogs.java
  19. 1 1
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java
  20. 1 0
      fs-service-system/src/main/java/com/fs/sop/mapper/QwSopMapper.java
  21. 3 0
      fs-service-system/src/main/java/com/fs/sop/mapper/SopUserLogsMapper.java
  22. 2 0
      fs-service-system/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  23. 2 2
      fs-service-system/src/main/java/com/fs/statis/domain/FsStatisSalerWatch.java
  24. 17 8
      fs-service-system/src/main/java/com/fs/statis/service/impl/FsStatisSalerWatchServiceImpl.java
  25. 4 4
      fs-service-system/src/main/java/com/fs/store/service/impl/FsStorePaymentServiceImpl.java
  26. 4 4
      fs-service-system/src/main/resources/mapper/company/CompanyUserMapper.xml
  27. 1 0
      fs-service-system/src/main/resources/mapper/sop/SopUserLogsInfoMapper.xml
  28. 9 0
      fs-service-system/src/main/resources/mapper/sop/SopUserLogsMapper.xml
  29. 1 1
      fs-service-system/src/main/resources/mapper/statis/FsStatisSalerWatchMapper.xml
  30. 6 4
      fs-user-app/src/main/java/com/fs/app/controller/WxPayController.java
  31. 14 0
      fs-user-app/src/main/java/com/fs/core/aspectj/DataScopeAspect.java
  32. 117 116
      fs-user-app/src/main/java/com/fs/core/aspectj/RateLimiterAspect.java
  33. 1 2
      fs-user-app/src/main/java/com/fs/core/config/DataSourceConfig.java
  34. 67 23
      fs-user-app/src/main/java/com/fs/core/config/MyBatisConfig.java
  35. 29 0
      fs-user-app/src/main/java/com/fs/core/config/RedisConfig.java
  36. 9 3
      fs-user-app/src/main/java/com/fs/core/datasource/DynamicDataSource.java
  37. 93 0
      fs-user-app/src/main/resources/logback.xml

+ 2 - 4
fs-admin/src/main/java/com/fs/course/controller/FsCourseQuestionBankController.java

@@ -3,7 +3,6 @@ package com.fs.course.controller;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
-
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
@@ -13,7 +12,6 @@ import com.fs.core.web.service.TokenService;
 import com.fs.course.domain.FsCourseQuestionBank;
 import com.fs.course.dto.FsCourseQuestionBankImportDTO;
 import com.fs.course.service.IFsCourseQuestionBankService;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -57,8 +55,8 @@ public class FsCourseQuestionBankController extends BaseController
     @GetMapping("/export")
     public AjaxResult export(FsCourseQuestionBank fsCourseQuestionBank)
     {
-        List<FsCourseQuestionBank> list = fsCourseQuestionBankService.selectFsCourseQuestionBankListExport(fsCourseQuestionBank);
-        ExcelUtil<FsCourseQuestionBank> util = new ExcelUtil<FsCourseQuestionBank>(FsCourseQuestionBank.class);
+        List<FsCourseQuestionBankImportDTO> list = fsCourseQuestionBankService.exportData(fsCourseQuestionBank);
+        ExcelUtil<FsCourseQuestionBankImportDTO> util = new ExcelUtil<>(FsCourseQuestionBankImportDTO.class);
         return util.exportExcel(list, "题库数据");
     }
 

+ 93 - 0
fs-admin/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-admin/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>

+ 93 - 0
fs-company-app/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-company-app/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>

+ 93 - 0
fs-company/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-company/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>

+ 93 - 0
fs-qw-api/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-qw-api/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>

+ 1 - 0
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -724,6 +724,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         sopLogs.setExternalUserId(externalId);
         sopLogs.setExternalUserName(externalUserName);
         sopLogs.setFsUserId(fsUserId);
+        sopLogs.setUserLogsId(logVo.getId());
 
         return sopLogs;
     }

+ 93 - 0
fs-qw-task/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-qw-task/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>

+ 93 - 0
fs-qwhook-msg/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-qwhook-msg/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>

+ 4 - 16
fs-qwhook-sop/src/main/java/com/fs/app/controller/testController.java

@@ -8,6 +8,7 @@ import com.fs.course.mapper.FsCourseSopMapper;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.service.IFsCourseLinkService;
 import com.fs.crm.mapper.CrmCustomerMapper;
+import com.fs.sop.service.ISopUserLogsInfoService;
 import com.fs.store.mapper.FsUserMapper;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUser;
@@ -57,27 +58,14 @@ public class testController {
     testService testService;
     @Autowired
     private RedisCache redisCache;
+    @Autowired
+    private ISopUserLogsInfoService iSopUserLogsInfoService;
 
     @GetMapping("/qwHookNotify")
     public R qwHookNotify() {
 
-//        redisCache.setCacheObject("qwUserRd:"+12313+":"+12313 ,JSON.toJSONString(null),1, TimeUnit.HOURS);
-
-//
-//        List<QwUser> qwUserAllKey = qwUserMapper.getQwUserAllKey();
-//
-//
-//        int i=1;
-//
-//        for (QwUser qwUser : qwUserAllKey) {
-//            System.out.println(qwUser);
-//            i++;
-//            System.out.println("执行到第:"+i);
-//            testService.add(qwUser);
-//
-//        }
-
 
+        iSopUserLogsInfoService.updateSopUserInfoByExternalId(218L,123456L);
         return  R.ok();
     }
 

+ 93 - 0
fs-qwhook-sop/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-qwhook-sop/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>

+ 93 - 0
fs-qwhook/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-qwhook/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>

+ 5 - 3
fs-service-system/src/main/java/com/fs/company/mapper/CompanyUserMapper.java

@@ -16,6 +16,7 @@ import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 
+import java.time.LocalDate;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -268,10 +269,11 @@ public interface CompanyUserMapper
      * 获取对应的销售观看记录数
      *
      * @param companyUserId
-     * @param periodId
      * @return
      */
-    Long queryCompanyUserWatchCount(@Param("companyUserId") Long companyUserId, @Param("periodId") Long periodId);
+    Long queryCompanyUserWatchCount(@Param("companyUserId") Long companyUserId,
+                                    @Param("previousDay") LocalDate previousDay);
 
-    Long queryCompanyUserWatchCountCompleted(@Param("companyUserId") Long companyUserId,@Param("periodId") Long periodId);
+    Long queryCompanyUserWatchCountCompleted(@Param("companyUserId") Long companyUserId,
+                                             @Param("previousDay") LocalDate previousDay);
 }

+ 23 - 11
fs-service-system/src/main/java/com/fs/course/dto/FsCourseQuestionBankImportDTO.java

@@ -6,24 +6,36 @@ import lombok.Data;
 @Data
 public class FsCourseQuestionBankImportDTO {
 
-    @Excel(name = "标题")
+    @Excel(name = "标题(必填)")
     private String title;
 
-    @Excel(name = "问题类别")
+    @Excel(name = "序号(必填)")
+    private Long sort;
+
+    @Excel(name = "类别(必填)")
+    private String type;
+
+    @Excel(name = "题目类别(非必填)")
     private String questionType;
 
-    @Excel(name = "题目子类别")
+    @Excel(name = "题目子类别(非必填)")
     private String questionSubTyp;
 
-    @Excel(name = "题目类型: 单选 多选")
-    private String type;
+    @Excel(name = "状态(非必填)")
+    private String status;
 
-    @Excel(name = "选项: 多个用 | 隔开")
-    private String question;
+    @Excel(name = "选项A(必填)")
+    private String questionA;
 
-    @Excel(name = "答案: 多个用 | 隔开")
-    private String answer;
+    @Excel(name = "选项B(必填)")
+    private String questionB;
+
+    @Excel(name = "选项C(必填)")
+    private String questionC;
 
-    @Excel(name = "序号")
-    private long sort;
+    @Excel(name = "选项D(非必填)")
+    private String questionD;
+
+    @Excel(name = "答案(必填)")
+    private String answer;
 }

+ 11 - 4
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseCategoryMapper.java

@@ -1,14 +1,14 @@
 package com.fs.course.mapper;
 
-import java.util.List;
-import java.util.Map;
-
 import com.fs.course.domain.FsUserCourseCategory;
 import com.fs.his.vo.OptionsVO;
 import org.apache.ibatis.annotations.MapKey;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 课堂分类Mapper接口
  *
@@ -86,7 +86,14 @@ public interface FsUserCourseCategoryMapper
      * 查询所有分类
      * @return
      */
-    @Select("select cate_id,cate_name from fs_user_course_category where is_del=0")
+    @Select("select cate_id,cate_name, pid from fs_user_course_category where is_del=0")
     @MapKey("cateName")
     Map<String,FsUserCourseCategory> queryAllCategoryData();
+
+    /**
+     * 查询所有分类
+     */
+    @Select("select cate_id as cateId,cate_name as cateName from fs_user_course_category where is_del = 0")
+    @MapKey("cateId")
+    Map<Long, FsUserCourseCategory> queryAllIdKeyCategoryData();
 }

+ 8 - 2
fs-service-system/src/main/java/com/fs/course/service/IFsCourseQuestionBankService.java

@@ -5,7 +5,6 @@ import com.fs.course.domain.FsCourseQuestionBank;
 import com.fs.course.dto.FsCourseQuestionBankImportDTO;
 import com.fs.course.param.FsCourseQuestionAnswerUParam;
 
-import javax.validation.constraints.Size;
 import java.util.List;
 
 /**
@@ -82,7 +81,7 @@ public interface IFsCourseQuestionBankService
      * @param nickName 昵称
      * @return String
      */
-    String importData(List<FsCourseQuestionBankImportDTO> list, @Size String nickName);
+    String importData(List<FsCourseQuestionBankImportDTO> list, String nickName);
 
     /**
      * 根据ID查询题目
@@ -94,4 +93,11 @@ public interface IFsCourseQuestionBankService
     List<FsCourseQuestionBank> selectFsCourseQuestionBankListExport(FsCourseQuestionBank fsCourseQuestionBank);
 
     String selectNameByCateId(Long cateId);
+
+    /**
+     * 题目导出
+     * @param fsCourseQuestionBank  参数
+     * @return  list
+     */
+    List<FsCourseQuestionBankImportDTO> exportData(FsCourseQuestionBank fsCourseQuestionBank);
 }

+ 324 - 102
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java

@@ -1,6 +1,7 @@
 package com.fs.course.service.impl;
 
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
@@ -10,7 +11,10 @@ import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.course.cache.FsUserCourseCategoryCacheService;
 import com.fs.course.config.CourseConfig;
-import com.fs.course.domain.*;
+import com.fs.course.domain.FsCourseAnswerLogs;
+import com.fs.course.domain.FsCourseQuestionBank;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.domain.FsUserCourseCategory;
 import com.fs.course.dto.FsCourseQuestionBankImportDTO;
 import com.fs.course.mapper.*;
 import com.fs.course.param.FsCourseQuestionAnswerUParam;
@@ -21,15 +25,15 @@ import com.fs.store.mapper.FsUserMapper;
 import com.fs.store.service.IFsStorePaymentService;
 import com.fs.system.service.ISysConfigService;
 import com.google.gson.JsonParser;
-import com.hc.openapi.tool.fastjson.JSON;
 import jodd.util.StringUtil;
-import org.apache.commons.collections4.CollectionUtils;
+import lombok.Getter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import javax.validation.constraints.Size;
 import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -360,134 +364,297 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
      * @return String
      */
     @Override
-    public String importData(List<FsCourseQuestionBankImportDTO> list, @Size String nickName) {
+    public String importData(List<FsCourseQuestionBankImportDTO> list, String nickName) {
         if (Objects.isNull(list) || list.isEmpty()) {
             throw new ServiceException("导入数据不能为空");
         }
 
-        int successNum = 0;
-        int failureNum = 0;
-        StringBuilder importSuccessMsg = new StringBuilder();
-        StringBuilder importErrorMsg = new StringBuilder();
-        StringBuilder importMsg = new StringBuilder();
-
+        ImportResult result = new ImportResult();
         List<FsCourseQuestionBank> importData = new ArrayList<>();
-
-        Map<String,FsUserCourseCategory> categoryData = courseCategoryMapper.queryAllCategoryData();
+        Map<String, FsUserCourseCategory> categoryData = courseCategoryMapper.queryAllCategoryData();
 
         for (FsCourseQuestionBankImportDTO importDTO : list) {
             try {
-                String title = importDTO.getTitle();
-                String type = importDTO.getType();
-                String questionType = importDTO.getQuestionType();
-                String questionSubTyp = importDTO.getQuestionSubTyp();
-
-                String question = importDTO.getQuestion();
-                String answer = importDTO.getAnswer();
-
-                if (StringUtils.isBlank(title) || StringUtils.isBlank(type) || StringUtils.isBlank(question) || StringUtils.isBlank(answer)) {
-                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:信息不完整";
-                    importErrorMsg.append(msg);
-                    failureNum++;
+                // 数据验证
+                ValidationResult validation = validateImportData(importDTO);
+                if (!validation.isValid()) {
+                    result.addFailure(importDTO.getTitle(), validation.getErrorMessage());
                     continue;
                 }
 
-                if (!type.contains("单") && !type.contains("多")) {
-                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:题目类型不正确";
-                    importErrorMsg.append(msg);
-                    failureNum++;
-                    continue;
-                }
+                // 构建题目对象
+                FsCourseQuestionBank questionBank = buildQuestionBank(importDTO, categoryData, nickName);
+                importData.add(questionBank);
+                result.addSuccess(importDTO.getTitle());
 
-                // 判断答案是否在选项里
-                String[] questions = question.split("\\|");
-                String[] answers = answer.split("\\|");
+            } catch (Exception e) {
+                result.addFailure(importDTO.getTitle(), "导入异常: " + e.getMessage());
+            }
+        }
 
-                if (type.contains("多") && answers.length < 2) {
-                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:多选题答案不能少于2个";
-                    importErrorMsg.append(msg);
-                    failureNum++;
-                    continue;
-                }
+        // 批量保存
+        if (!importData.isEmpty()) {
+            fsCourseQuestionBankMapper.insertFsCourseQuestionBankBatch(importData);
+        }
 
-                List<String> optionsList = Arrays.asList(questions);
-                boolean allAnswersInOptions = true;
+        return result.buildResultMessage();
+    }
 
-                // 遍历每一个正确答案文本
-                for (String correctAnswer : answers) {
-                    // 检查当前正确答案文本是否存在于选项列表中
-                    if (!optionsList.contains(correctAnswer.trim())) {
-                        allAnswersInOptions = false; // 发现一个不在选项里的答案
-                        break;
-                    }
-                }
+    /**
+     * 验证导入数据
+     */
+    private ValidationResult validateImportData(FsCourseQuestionBankImportDTO importDTO) {
+        // 基础字段验证
+        if (StringUtils.isBlank(importDTO.getTitle())) {
+            return ValidationResult.fail("标题不能为空");
+        }
+        if (StringUtils.isBlank(importDTO.getType())) {
+            return ValidationResult.fail("类别不能为空");
+        }
+        if (Objects.isNull(importDTO.getSort())) {
+            return ValidationResult.fail("排序不能为空");
+        }
+        if (StringUtils.isBlank(importDTO.getAnswer())) {
+            return ValidationResult.fail("答案不能为空");
+        }
 
-                if (!allAnswersInOptions) {
-                    String msg = "<br/>" + failureNum + "、题目 " + title + " 导入失败:答案不在选项中";
-                    importErrorMsg.append(msg);
-                    failureNum++;
-                    continue;
-                }
+        // 选项验证
+        ValidationResult optionValidation = validateOptions(importDTO);
+        if (!optionValidation.isValid()) {
+            return optionValidation;
+        }
 
-                JSONArray questionArray = new JSONArray();
-                optionsList = Arrays.asList(answers);
-                for (int i = 0; i < questions.length; i++) {
-                    JSONObject optionObj = new JSONObject();
-                    optionObj.put("name", questions[i]);
-                    optionObj.put("isAnswer", optionsList.contains(questions[i].trim()) ? 1 : 0);
-                    optionObj.put("indexId", i);
-                    questionArray.add(optionObj);
-                }
+        // 题目类型验证
+        if (!isValidQuestionType(importDTO.getType())) {
+            return ValidationResult.fail("题目类型不正确,仅支持单选题和多选题");
+        }
 
-                // 分类
-                FsUserCourseCategory fsUserCourseCategory = categoryData.get(questionType);
-                Long questionTypeId = null;
-                if(fsUserCourseCategory != null) {
-                    questionTypeId = fsUserCourseCategory.getCateId();
-                }
+        // 答案验证
+        return validateAnswers(importDTO);
+    }
 
-                // 题目子分类
-                fsUserCourseCategory = categoryData.get(questionSubTyp);
-                Long questionSubTypeId = null;
-                if(fsUserCourseCategory != null) {
-                    questionSubTypeId = fsUserCourseCategory.getCateId();
-                }
+    /**
+     * 验证选项
+     */
+    private ValidationResult validateOptions(FsCourseQuestionBankImportDTO importDTO) {
+        if (StringUtils.isBlank(importDTO.getQuestionA()) ||
+                StringUtils.isBlank(importDTO.getQuestionB()) ||
+                StringUtils.isBlank(importDTO.getQuestionC())) {
+            return ValidationResult.fail("选项A、B、C不能为空");
+        }
+        return ValidationResult.success();
+    }
 
-                FsCourseQuestionBank questionBank = new FsCourseQuestionBank();
-                questionBank.setTitle(title);
-                questionBank.setType(type.contains("单") ? 1L : 2L);
-                questionBank.setQuestionType(questionTypeId);
-                questionBank.setStatus(1L);
-                questionBank.setSort(importDTO.getSort());
-                questionBank.setQuestion(JSON.toJSONString(questionArray));
-                questionBank.setAnswer(answers.length > 1 ? JSON.toJSONString(answers) : answer);
-                questionBank.setCreateTime(new Date());
-                questionBank.setCreateBy(nickName);
-                questionBank.setQuestionSubType(questionSubTypeId);
+    /**
+     * 验证题目类型
+     */
+    private boolean isValidQuestionType(String type) {
+        return type.contains("单") || type.contains("多");
+    }
 
-                importData.add(questionBank);
+    /**
+     * 验证答案
+     */
+    private ValidationResult validateAnswers(FsCourseQuestionBankImportDTO importDTO) {
+        String[] answers = importDTO.getAnswer().trim().toUpperCase().split("");
 
+        // 多选题答案数量验证
+        if (importDTO.getType().contains("多") && answers.length < 2) {
+            return ValidationResult.fail("多选题答案不能少于2个");
+        }
 
-                importSuccessMsg.append("<br/>").append(successNum).append("、题目 ").append(title).append(" 导入成功");
-                successNum++;
-            } catch (Exception e) {
-                String msg = "<br/>" + failureNum + "、题目 " + importDTO.getTitle() + " 导入异常:";
-                importErrorMsg.append(msg).append(e.getMessage());
-                failureNum++;
+        // 答案选项验证
+        List<String> validOptions = getValidOptions(importDTO);
+        for (String answer : answers) {
+            if (!validOptions.contains(answer.trim())) {
+                return ValidationResult.fail("答案不在选项中");
             }
         }
 
-        if(CollectionUtils.isNotEmpty(importData)) {
-            fsCourseQuestionBankMapper.insertFsCourseQuestionBankBatch(importData);
+        return ValidationResult.success();
+    }
+
+    /**
+     * 获取有效选项列表
+     */
+    private List<String> getValidOptions(FsCourseQuestionBankImportDTO importDTO) {
+        List<String> options = new ArrayList<>(Arrays.asList("A", "B", "C"));
+        if (StringUtils.isNotBlank(importDTO.getQuestionD())) {
+            options.add("D");
         }
+        return options;
+    }
+
+    /**
+     * 构建题目对象
+     */
+    private FsCourseQuestionBank buildQuestionBank(FsCourseQuestionBankImportDTO importDTO,
+                                                   Map<String, FsUserCourseCategory> categoryData,
+                                                   String nickName) {
+        FsCourseQuestionBank questionBank = new FsCourseQuestionBank();
+
+        // 基础信息
+        questionBank.setTitle(importDTO.getTitle());
+        questionBank.setType(getQuestionTypeCode(importDTO.getType()));
+        questionBank.setSort(importDTO.getSort());
+        questionBank.setStatus(parseStatus(importDTO.getStatus()));
+        questionBank.setCreateTime(new Date());
+        questionBank.setCreateBy(nickName);
+
+        // 分类信息
+        setQuestionCategories(questionBank, importDTO, categoryData);
+
+        // 选项和答案
+        questionBank.setQuestion(buildQuestionOptions(importDTO));
+        questionBank.setAnswer(buildAnswer(importDTO));
+
+        return questionBank;
+    }
 
-        // 在所有导入处理完成后,构建最终的导入结果消息
-        importMsg.insert(0, "导入完成!成功" + successNum + " 条,失败" + failureNum + "条。");
-        importMsg.append(importErrorMsg);
-        importMsg.append(importSuccessMsg);
-        return importMsg.toString();
+    /**
+     * 获取题目类型编码
+     */
+    private Long getQuestionTypeCode(String type) {
+        return type.contains("单") ? 1L : 2L;
     }
 
+    /**
+     * 解析状态
+     */
+    private Long parseStatus(String statusStr) {
+        if (StringUtils.isBlank(statusStr)) {
+            return 1L;
+        }
+        try {
+            return statusStr.contains("正常") ? 1L : 0L;
+        } catch (Exception e) {
+            return 1L;
+        }
+    }
+
+    /**
+     * 设置题目分类
+     */
+    private void setQuestionCategories(FsCourseQuestionBank questionBank,
+                                       FsCourseQuestionBankImportDTO importDTO,
+                                       Map<String, FsUserCourseCategory> categoryData) {
+        if (StringUtils.isBlank(importDTO.getQuestionType())) {
+            return;
+        }
+
+        FsUserCourseCategory category = categoryData.get(importDTO.getQuestionType().trim());
+        if (category != null) {
+            questionBank.setQuestionType(category.getCateId());
+
+            // 子分类
+            if (StringUtils.isNotBlank(importDTO.getQuestionSubTyp())) {
+                FsUserCourseCategory subCategory = categoryData.get(importDTO.getQuestionSubTyp().trim());
+                if (subCategory != null && Objects.equals(subCategory.getPid(), category.getCateId())) {
+                    questionBank.setQuestionSubType(subCategory.getCateId());
+                }
+            }
+        }
+    }
+
+    /**
+     * 构建选项JSON
+     */
+    private String buildQuestionOptions(FsCourseQuestionBankImportDTO importDTO) {
+        JSONArray questionArray = new JSONArray();
+        String[] answers = importDTO.getAnswer().trim().toUpperCase().split("");
+        List<String> answerList = Arrays.asList(answers);
+
+        // 构建选项
+        addOption(questionArray, importDTO.getQuestionA(), answerList.contains("A"), 0);
+        addOption(questionArray, importDTO.getQuestionB(), answerList.contains("B"), 1);
+        addOption(questionArray, importDTO.getQuestionC(), answerList.contains("C"), 2);
+
+        if (StringUtils.isNotBlank(importDTO.getQuestionD())) {
+            addOption(questionArray, importDTO.getQuestionD(), answerList.contains("D"), 3);
+        }
+
+        return JSON.toJSONString(questionArray);
+    }
+
+    /**
+     * 添加选项
+     */
+    private void addOption(JSONArray questionArray, String optionText,
+                           boolean isAnswer, int indexId) {
+        JSONObject option = new JSONObject();
+        option.put("name", optionText.trim());
+        option.put("isAnswer", isAnswer ? 1 : 0);
+        option.put("indexId", indexId);
+        questionArray.add(option);
+    }
+
+    /**
+     * 构建答案
+     */
+    private String buildAnswer(FsCourseQuestionBankImportDTO importDTO) {
+        String[] answers = importDTO.getAnswer().trim().toUpperCase().split("");
+        List<String> answerList = Arrays.asList(answers);
+
+        Map<String, String> questionMap = new HashMap<>();
+        questionMap.put("A", importDTO.getQuestionA().trim());
+        questionMap.put("B", importDTO.getQuestionB().trim());
+        questionMap.put("C", importDTO.getQuestionC().trim());
+        questionMap.put("D", importDTO.getQuestionD().trim());
+
+        List<String> selectedAnswers = answerList.stream()
+                .map(questionMap::get)
+                .collect(Collectors.toList());
+
+        if (answerList.size() == 1) {
+            return selectedAnswers.get(0);
+        }
+
+        return JSON.toJSONString(selectedAnswers);
+    }
+
+    // 辅助类
+    @Getter
+    private static class ValidationResult {
+        private final boolean valid;
+        private final String errorMessage;
+
+        private ValidationResult(boolean valid, String errorMessage) {
+            this.valid = valid;
+            this.errorMessage = errorMessage;
+        }
+
+        public static ValidationResult success() {
+            return new ValidationResult(true, null);
+        }
+
+        public static ValidationResult fail(String message) {
+            return new ValidationResult(false, message);
+        }
+
+    }
+
+    private static class ImportResult {
+        private int successNum = 0;
+        private int failureNum = 0;
+        private final StringBuilder successMsg = new StringBuilder();
+        private final StringBuilder errorMsg = new StringBuilder();
+
+        public void addSuccess(String title) {
+            successNum++;
+            successMsg.append("<br/>").append(successNum).append("、题目 ").append(title).append(" 导入成功");
+        }
+
+        public void addFailure(String title, String error) {
+            failureNum++;
+            errorMsg.append("<br/>").append(failureNum).append("、题目 ").append(title).append(" 导入失败:").append(error);
+        }
+
+        public String buildResultMessage() {
+            return "导入完成!成功" + successNum + " 条,失败" + failureNum + "条。" + errorMsg + successMsg;
+        }
+    }
+
+
     /**
      * 根据ID查询题目
      * @param ids   ids
@@ -554,6 +721,61 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
         return fsCourseQuestionBankMapper.selectNameByCateId(cateId);
     }
 
+    /**
+     * 题目导出
+     * @param fsCourseQuestionBank  参数
+     * @return  list
+     */
+    @Override
+    public List<FsCourseQuestionBankImportDTO> exportData(FsCourseQuestionBank fsCourseQuestionBank) {
+        List<FsCourseQuestionBank> fsCourseQuestionBanks = fsCourseQuestionBankMapper.selectFsCourseQuestionBankList(fsCourseQuestionBank);
+        Map<Long, FsUserCourseCategory> categoryMap = courseCategoryMapper.queryAllIdKeyCategoryData();
+
+        // 辅助方法:根据ID从Map获取分类名称
+        Function<Long, String> getCategoryName = id -> Optional.ofNullable(categoryMap.get(id))
+                .map(FsUserCourseCategory::getCateName)
+                .orElse(null);
+
+        Map<Integer, BiConsumer<FsCourseQuestionBankImportDTO, String>> questionSetters = new HashMap<>();
+        questionSetters.put(0, FsCourseQuestionBankImportDTO::setQuestionA);
+        questionSetters.put(1, FsCourseQuestionBankImportDTO::setQuestionB);
+        questionSetters.put(2, FsCourseQuestionBankImportDTO::setQuestionC);
+        questionSetters.put(3, FsCourseQuestionBankImportDTO::setQuestionD);
+
+        Map<Integer, String> answerLettersMap = new HashMap<>();
+        answerLettersMap.put(0, "A");
+        answerLettersMap.put(1, "B");
+        answerLettersMap.put(2, "C");
+        answerLettersMap.put(3, "D");
+
+        return fsCourseQuestionBanks.stream().map(q -> {
+            FsCourseQuestionBankImportDTO dto = new FsCourseQuestionBankImportDTO();
+            dto.setTitle(q.getTitle());
+            dto.setSort(q.getSort());
+            dto.setType(q.getType() == 1 ? "单选" : "多选");
+            Optional.ofNullable(q.getQuestionType()).map(getCategoryName).ifPresent(dto::setQuestionType);
+            Optional.ofNullable(q.getQuestionSubType()).map(getCategoryName).ifPresent(dto::setQuestionSubTyp);
+            dto.setStatus(q.getStatus() == 1 ? "正常" : "停用");
+
+            StringBuilder answersBuilder = new StringBuilder();
+            JSONArray array = JSON.parseArray(q.getQuestion());
+            array.forEach(jsonObject -> {
+                JSONObject json = (JSONObject) jsonObject;
+                int index = json.getInteger("indexId");
+                String name = json.getString("name");
+                int isAnswer = json.getInteger("isAnswer");
+
+                questionSetters.get(index).accept(dto, name);
+                if (isAnswer == 1) {
+                    answersBuilder.append(answerLettersMap.get(index));
+                }
+
+            });
+            dto.setAnswer(answersBuilder .toString());
+            return dto;
+        }).collect(Collectors.toList());
+    }
+
 
     public static String[] convertStringToArray(String inputString) {
         String cleanString = inputString.replaceAll("[\\[\\]\"\\s]", "");

+ 2 - 2
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -475,7 +475,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             log.setUpdateTime(new Date());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
 
-//            iSopUserLogsInfoService.updateSopUserInfoByExternalId(externalContact.getId(),param.getUserId());
+            iSopUserLogsInfoService.updateSopUserInfoByExternalId(externalContact.getId(),param.getUserId());
 
             return R.ok();
 
@@ -491,7 +491,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             user.setQwExtId(param.getQwExternalId());
             fsUserMapper.updateFsUser(user);
 
-//            iSopUserLogsInfoService.updateSopUserInfoByExternalId(externalContact.getId(),param.getUserId());
+            iSopUserLogsInfoService.updateSopUserInfoByExternalId(externalContact.getId(),param.getUserId());
 
             //绑定上之后 更新观看记录
             //看课记录中userId为0绑定userId

+ 5 - 0
fs-service-system/src/main/java/com/fs/sop/domain/QwSopLogs.java

@@ -70,6 +70,11 @@ public class QwSopLogs implements Serializable {
     */
     private String sopId;
 
+    /**
+     * 营期主键
+     */
+    private String userLogsId;
+
     /**
      * 备注
      */

+ 1 - 1
fs-service-system/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java

@@ -291,7 +291,7 @@ public interface QwSopLogsMapper extends BaseMapper<QwSopLogs> {
 
     @DataSource(DataSourceType.SOP)
     Long selectQwSopLogsCountByQwUserId(@Param("data") List<String> qwUserIdList,
-                                        @Param("periodId") Long periodId,
+                                        @Param("periodId") String periodId,
                                         @Param("previousDay") LocalDate previousDay);
 
 }

+ 1 - 0
fs-service-system/src/main/java/com/fs/sop/mapper/QwSopMapper.java

@@ -374,4 +374,5 @@ public interface QwSopMapper extends BaseMapper<QwSop> {
 
     List<QwSop> selectQwSopAllList();
 
+    List<QwSop> selectQwSopListByQwUserIds(List<String> qwUserIdList);
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/sop/mapper/SopUserLogsMapper.java

@@ -172,4 +172,7 @@ public interface SopUserLogsMapper {
     List<SopUserLogs> selectSopUserLogByChatIds(@Param("ids") List<String> ids);
 
     List<SopUserLogs> queryExecuteLogBySopId(@Param("sopId") String id);
+
+    @DataSource(DataSourceType.SOP)
+    List<String> selectSopUserLogsByQwUserIds(@Param("qwUserIds") List<String> qwUserIdList);
 }

+ 2 - 0
fs-service-system/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java

@@ -2,7 +2,9 @@ package com.fs.sop.service.impl;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
+import com.fs.common.annotation.DataSource;
 import com.fs.common.core.domain.R;
+import com.fs.common.enums.DataSourceType;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.date.DateUtil;

+ 2 - 2
fs-service-system/src/main/java/com/fs/statis/domain/FsStatisSalerWatch.java

@@ -101,9 +101,9 @@ public class FsStatisSalerWatch {
     private Long onlineCompletePlayback;
 
     /**
-     * 训练营id
+     * 营id
      */
-    private Long periodId;
+    private String periodId;
 
     /**
      * 数据日期

+ 17 - 8
fs-service-system/src/main/java/com/fs/statis/service/impl/FsStatisSalerWatchServiceImpl.java

@@ -5,7 +5,10 @@ import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.mapper.FsUserCoursePeriodMapper;
 import com.fs.qw.mapper.QwUserMapper;
+import com.fs.sop.domain.QwSop;
 import com.fs.sop.mapper.QwSopLogsMapper;
+import com.fs.sop.mapper.QwSopMapper;
+import com.fs.sop.mapper.SopUserLogsMapper;
 import com.fs.statis.domain.FsStatisSalerWatch;
 import com.fs.statis.dto.StatsWatchLogPageListDTO;
 import com.fs.statis.mapper.FsStatisSalerWatchMapper;
@@ -37,13 +40,20 @@ public class FsStatisSalerWatchServiceImpl implements FsStatisSalerWatchService
 
     private final FsUserCoursePeriodMapper fsUserCoursePeriodMapper;
 
+    private final QwSopMapper qwSopMapper;
+
+    private final SopUserLogsMapper sopUserLogsMapper;
+
+
     @Autowired
-    public FsStatisSalerWatchServiceImpl(FsStatisSalerWatchMapper fsStatisSalerWatchMapper, CompanyUserMapper companyUserMapper, QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, FsUserCoursePeriodMapper fsUserCoursePeriodMapper) {
+    public FsStatisSalerWatchServiceImpl(FsStatisSalerWatchMapper fsStatisSalerWatchMapper, CompanyUserMapper companyUserMapper, QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, FsUserCoursePeriodMapper fsUserCoursePeriodMapper, QwSopMapper qwSopMapper, SopUserLogsMapper sopUserLogsMapper) {
         this.fsStatisSalerWatchMapper = fsStatisSalerWatchMapper;
         this.companyUserMapper = companyUserMapper;
         this.qwUserMapper = qwUserMapper;
         this.qwSopLogsMapper = qwSopLogsMapper;
         this.fsUserCoursePeriodMapper = fsUserCoursePeriodMapper;
+        this.qwSopMapper = qwSopMapper;
+        this.sopUserLogsMapper = sopUserLogsMapper;
     }
 
     /**
@@ -116,7 +126,6 @@ public class FsStatisSalerWatchServiceImpl implements FsStatisSalerWatchService
         // 获取前一天的时间
         List<CompanyUser> companyUserList = this.companyUserMapper.selectAllCompanyUserList();
 
-
         List<FsStatisSalerWatch> writeData = new ArrayList<>();
 
         LocalDate previousDay = LocalDate.now().minusDays(1);
@@ -126,18 +135,18 @@ public class FsStatisSalerWatchServiceImpl implements FsStatisSalerWatchService
                 log.info("销售{} 对应公司id {} 为空!",companyUser.getUserId(),companyUser.getCompanyId());
                 continue;
             }
-            // 确定当前销售对应的营期
-            List<Long> periodList = fsUserCoursePeriodMapper.queryPeriod(companyUser.getCompanyId(),previousDay);
-
             // 找到销售关联的企微账号
             List<String> qwUserIdList = qwUserMapper.findQwUserIdListByCompanyUserId(companyUser.getUserId());
 
-            for (Long periodId : periodList) {
+            // 确定当前销售对应的sop执行记录
+            List<String> periodList = sopUserLogsMapper.selectSopUserLogsByQwUserIds(qwUserIdList);
+
+            for (String periodId : periodList) {
                 // 去sop记录表找对应的SOP发送记录,记录数作为营期人数
                 Long periodCount = qwSopLogsMapper.selectQwSopLogsCountByQwUserId(qwUserIdList,periodId,previousDay);
                 // 再去course_watch_log找对应的销售观看记录作为已报名数
-                Long registerCount = companyUserMapper.queryCompanyUserWatchCount(companyUser.getUserId(),periodId);
-                Long completedCount = companyUserMapper.queryCompanyUserWatchCountCompleted(companyUser.getUserId(),periodId);
+                Long registerCount = companyUserMapper.queryCompanyUserWatchCount(companyUser.getUserId(),previousDay);
+                Long completedCount = companyUserMapper.queryCompanyUserWatchCountCompleted(companyUser.getUserId(),previousDay);
 
 
                 FsStatisSalerWatch fsStatisSalerWatch = new FsStatisSalerWatch();

+ 4 - 4
fs-service-system/src/main/java/com/fs/store/service/impl/FsStorePaymentServiceImpl.java

@@ -373,9 +373,9 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService
                 config = JSONUtil.toBean(json, RedPacketConfig.class);
                 break;
         }
-        //黑坑!!现在是云联融智优选的
+        //H5的用公众号的appid发,小程序的用小程序的appid来发
         if (param.getSource()==2){
-            config.setAppId("wxd70f99287830cb51");
+            config.setAppId(config.getMiniappId());
         }
         //组合返回参数
         R result = new R();
@@ -387,8 +387,8 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService
         }
         result.put("isNew",config.getIsNew());
 //        result.put("mchId",config.getMchId());
-        //黑坑!!现在是云联融智优选的
-        result.put("mchId","1708360153");
+
+        result.put("mchId",config.getMchId());
         System.out.println("发红包返回"+result);
         return result;
     }

+ 4 - 4
fs-service-system/src/main/resources/mapper/company/CompanyUserMapper.xml

@@ -508,8 +508,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null">
                 AND company_user_id = #{companyUserId}
             </if>
-            <if test="periodId != null">
-                AND period_id = #{periodId}
+            <if test="previousDay != null">
+                and camp_period_time = ${previousDay}
             </if>
         </where>
     </select>
@@ -520,8 +520,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null">
                 AND company_user_id = #{companyUserId}
             </if>
-            <if test="periodId != null">
-                AND period_id = #{periodId}
+            <if test="previousDay != null">
+                and camp_period_time = ${previousDay}
             </if>
         </where>
     </select>

+ 1 - 0
fs-service-system/src/main/resources/mapper/sop/SopUserLogsInfoMapper.xml

@@ -339,6 +339,7 @@
         </foreach>
     </update>
 
+
     <select id="selectEerGroup"  resultMap="SopUserLogsInfoResult">
         select s.user_logs_id,ANY_VALUE(s.sop_id) sop_id,ANY_VALUE(s.qw_user_id) qw_user_id,ANY_VALUE(s.corp_id) corp_id  from sop_user_logs_info s LEFT JOIN sop_user_logs ss ON s.user_logs_id=ss.id where create_time='2025-02-11' and ss.id is null GROUP BY s.user_logs_id
     </select>

+ 9 - 0
fs-service-system/src/main/resources/mapper/sop/SopUserLogsMapper.xml

@@ -292,5 +292,14 @@
 
          where sop_id=#{sopId}
     </select>
+    <select id="selectSopUserLogsByQwUserIds" resultType="java.lang.String">
+        select id from sop_user_logs
+        <where>
+            qw_user_id in
+              <foreach collection="qwUserIds" item="item" open="(" close=")" separator=",">
+                  #{item}
+              </foreach>
+        </where>
+    </select>
 
 </mapper>

+ 1 - 1
fs-service-system/src/main/resources/mapper/statis/FsStatisSalerWatchMapper.xml

@@ -33,7 +33,7 @@
     <select id="queryList" resultType="com.fs.statis.domain.FsStatisSalerWatch">
         select * from fs_statis_saler_watch
         <where>
-            <if test="userIds != null and userIds.length > 0">
+            <if test="userIds != null and userIds.size() > 0">
                 AND company_user_id IN
                  <foreach collection="userIds" open="(" close=")" separator="," item="item">
                      ${item}

+ 6 - 4
fs-user-app/src/main/java/com/fs/app/controller/WxPayController.java

@@ -7,6 +7,7 @@ import com.fs.common.core.domain.R;
 import com.fs.course.config.RedPacketConfig;
 import com.fs.course.service.IFsCourseRedPacketLogService;
 import com.fs.his.param.WxSendRedPacketParam;
+import com.fs.sop.service.ISopUserLogsInfoService;
 import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsUser;
 import com.fs.store.enums.*;
@@ -30,10 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.interceptor.TransactionAspectSupport;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -65,6 +63,9 @@ public class WxPayController {
     private IFsCourseRedPacketLogService redPacketLogService;
     @Autowired
     private ISysConfigService configService;
+    @Autowired
+    private ISopUserLogsInfoService iSopUserLogsInfoService;
+
     /**
      * 微信回调
      * 回调接口代码内部自动校验结果签名和业务代码
@@ -121,4 +122,5 @@ public class WxPayController {
         return storePaymentService.v3TransferNotify(notifyData,request);
     }
 
+
 }

+ 14 - 0
fs-user-app/src/main/java/com/fs/core/aspectj/DataScopeAspect.java

@@ -77,6 +77,7 @@ public class DataScopeAspect
     @Before("dataScopePointCut()")
     public void doBefore(JoinPoint point) throws Throwable
     {
+        clearDataScope(point);
         handleDataScope(point);
     }
 
@@ -185,4 +186,17 @@ public class DataScopeAspect
         }
         return null;
     }
+
+    /**
+     * 拼接权限sql前先清空params.dataScope参数防止注入
+     */
+    private void clearDataScope(final JoinPoint joinPoint)
+    {
+        Object params = joinPoint.getArgs()[0];
+        if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
+        {
+            BaseEntity baseEntity = (BaseEntity) params;
+            baseEntity.getParams().put(DATA_SCOPE, "");
+        }
+    }
 }

+ 117 - 116
fs-user-app/src/main/java/com/fs/core/aspectj/RateLimiterAspect.java

@@ -1,116 +1,117 @@
-//package com.fs.core.aspectj;
-//
-//import com.fs.common.annotation.RateLimiter;
-//import com.fs.common.enums.LimitType;
-//import com.fs.common.exception.ServiceException;
-//import com.fs.common.utils.ServletUtils;
-//import com.fs.common.utils.StringUtils;
-//import com.fs.common.utils.ip.IpUtils;
-//import org.aspectj.lang.JoinPoint;
-//import org.aspectj.lang.Signature;
-//import org.aspectj.lang.annotation.Aspect;
-//import org.aspectj.lang.annotation.Before;
-//import org.aspectj.lang.annotation.Pointcut;
-//import org.aspectj.lang.reflect.MethodSignature;
-//import org.slf4j.Logger;
-//import org.slf4j.LoggerFactory;
-//import org.springframework.beans.factory.annotation.Autowired;
-//import org.springframework.data.redis.core.RedisTemplate;
-//import org.springframework.data.redis.core.script.RedisScript;
-//import org.springframework.stereotype.Component;
-//
-//import java.lang.reflect.Method;
-//import java.util.Collections;
-//import java.util.List;
-//
-///**
-// * 限流处理
-// *
-//
-// */
-//@Aspect
-//@Component
-//public class RateLimiterAspect
-//{
-//    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
-//
-//    private RedisTemplate<Object, Object> redisTemplate;
-//
-//    private RedisScript<Long> limitScript;
-//
-//    @Autowired
-//    public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
-//    {
-//        this.redisTemplate = redisTemplate;
-//    }
-//
-//    @Autowired
-//    public void setLimitScript(RedisScript<Long> limitScript)
-//    {
-//        this.limitScript = limitScript;
-//    }
-//    // 配置织入点
-//    @Pointcut("@annotation(com.fs.common.annotation.RateLimiter)")
-//    public void rateLimiterPointCut()
-//    {
-//    }
-//
-//    @Before("rateLimiterPointCut()")
-//    public void doBefore(JoinPoint point) throws Throwable
-//    {
-//        RateLimiter rateLimiter = getAnnotationRateLimiter(point);
-//        String key = rateLimiter.key();
-//        int time = rateLimiter.time();
-//        int count = rateLimiter.count();
-//
-//        String combineKey = getCombineKey(rateLimiter, point);
-//        List<Object> keys = Collections.singletonList(combineKey);
-//        try
-//        {
-//            Long number = redisTemplate.execute(limitScript, keys, count, time);
-//            if (StringUtils.isNull(number) || number.intValue() > count)
-//            {
-//                throw new ServiceException("访问过于频繁,请稍后再试");
-//            }
-//            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key);
-//        }
-//        catch (ServiceException e)
-//        {
-//            throw e;
-//        }
-//        catch (Exception e)
-//        {
-//            throw new RuntimeException("服务器限流异常,请稍后再试");
-//        }
-//    }
-//
-//    /**
-//     * 是否存在注解,如果存在就获取
-//     */
-//    private RateLimiter getAnnotationRateLimiter(JoinPoint joinPoint)
-//    {
-//        Signature signature = joinPoint.getSignature();
-//        MethodSignature methodSignature = (MethodSignature) signature;
-//        Method method = methodSignature.getMethod();
-//
-//        if (method != null)
-//        {
-//            return method.getAnnotation(RateLimiter.class);
-//        }
-//        return null;
-//    }
-//
-//    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
-//    {
-//        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
-//        if (rateLimiter.limitType() == LimitType.IP)
-//        {
-//            stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest()));
-//        }
-//        MethodSignature signature = (MethodSignature) point.getSignature();
-//        Method method = signature.getMethod();
-//        Class<?> targetClass = method.getDeclaringClass();
-//        stringBuffer.append("-").append(targetClass.getName()).append("- ").append(method.getName());
-//        return stringBuffer.toString();
-//    }
-//}
+package com.fs.framework.aspectj;
+
+import com.fs.common.annotation.RateLimiter;
+import com.fs.common.enums.LimitType;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.ip.IpUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 限流处理
+ *
+
+ */
+@Aspect
+@Component
+public class RateLimiterAspect
+{
+    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
+
+    private RedisTemplate<Object, Object> redisTemplate;
+
+    private RedisScript<Long> limitScript;
+
+    @Autowired
+    public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
+    {
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Autowired
+    public void setLimitScript(RedisScript<Long> limitScript)
+    {
+        this.limitScript = limitScript;
+    }
+
+    // 配置织入点
+    @Pointcut("@annotation(com.fs.common.annotation.RateLimiter)")
+    public void rateLimiterPointCut()
+    {
+    }
+
+    @Before("rateLimiterPointCut()")
+    public void doBefore(JoinPoint point) throws Throwable
+    {
+        RateLimiter rateLimiter = getAnnotationRateLimiter(point);
+        String key = rateLimiter.key();
+        int time = rateLimiter.time();
+        int count = rateLimiter.count();
+
+        String combineKey = getCombineKey(rateLimiter, point);
+        List<Object> keys = Collections.singletonList(combineKey);
+        try
+        {
+            Long number = redisTemplate.execute(limitScript, keys, count, time);
+            if (StringUtils.isNull(number) || number.intValue() > count)
+            {
+                throw new ServiceException("访问过于频繁,请稍后再试");
+            }
+            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key);
+        }
+        catch (ServiceException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException("服务器限流异常,请稍后再试");
+        }
+    }
+
+    /**
+     * 是否存在注解,如果存在就获取
+     */
+    private RateLimiter getAnnotationRateLimiter(JoinPoint joinPoint)
+    {
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+
+        if (method != null)
+        {
+            return method.getAnnotation(RateLimiter.class);
+        }
+        return null;
+    }
+
+    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
+    {
+        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
+        if (rateLimiter.limitType() == LimitType.IP)
+        {
+            stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest()));
+        }
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Method method = signature.getMethod();
+        Class<?> targetClass = method.getDeclaringClass();
+        stringBuffer.append("-").append(targetClass.getName()).append("- ").append(method.getName());
+        return stringBuffer.toString();
+    }
+}

+ 1 - 2
fs-user-app/src/main/java/com/fs/core/config/DataSourceConfig.java

@@ -41,8 +41,7 @@ public class DataSourceConfig {
     public DynamicDataSource dataSource(@Qualifier("sopDataSource") DataSource sopDataSource,
                                         @Qualifier("masterDataSource") DataSource masterDataSource) {
         Map<Object, Object> targetDataSources = new HashMap<>();
-        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
-        targetDataSources.put(DataSourceType.SOP, sopDataSource);
+        targetDataSources.put(DataSourceType.SOP.name(), sopDataSource); // Ensure matching key
         return new DynamicDataSource(masterDataSource, targetDataSources);
     }
 

+ 67 - 23
fs-user-app/src/main/java/com/fs/core/config/MyBatisConfig.java

@@ -1,12 +1,7 @@
-package com.fs.core.config;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import javax.sql.DataSource;
+package com.fs.framework.config;
 
 import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
+import com.fs.common.utils.StringUtils;
 import org.apache.ibatis.io.VFS;
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.mybatis.spring.SqlSessionFactoryBean;
@@ -24,56 +19,103 @@ import org.springframework.core.type.classreading.MetadataReader;
 import org.springframework.core.type.classreading.MetadataReaderFactory;
 import org.springframework.util.ClassUtils;
 
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
 /**
  * Mybatis支持*匹配扫描包
+ *
+
  */
 @Configuration
-public class MyBatisConfig {
+public class MyBatisConfig
+{
     @Autowired
     private Environment env;
 
     static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
 
-    public static String setTypeAliasesPackage(String typeAliasesPackage) {
+    public static String setTypeAliasesPackage(String typeAliasesPackage)
+    {
         ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
         MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
         List<String> allResult = new ArrayList<String>();
-        try {
-            for (String aliasesPackage : typeAliasesPackage.split(",")) {
+        try
+        {
+            for (String aliasesPackage : typeAliasesPackage.split(","))
+            {
                 List<String> result = new ArrayList<String>();
                 aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                         + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
                 Resource[] resources = resolver.getResources(aliasesPackage);
-                if (resources != null && resources.length > 0) {
+                if (resources != null && resources.length > 0)
+                {
                     MetadataReader metadataReader = null;
-                    for (Resource resource : resources) {
-                        if (resource.isReadable()) {
+                    for (Resource resource : resources)
+                    {
+                        if (resource.isReadable())
+                        {
                             metadataReader = metadataReaderFactory.getMetadataReader(resource);
-                            try {
+                            try
+                            {
                                 result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
-                            } catch (ClassNotFoundException e) {
+                            }
+                            catch (ClassNotFoundException e)
+                            {
                                 e.printStackTrace();
                             }
                         }
                     }
                 }
-                if (result.size() > 0) {
+                if (result.size() > 0)
+                {
                     HashSet<String> hashResult = new HashSet<String>(result);
                     allResult.addAll(hashResult);
                 }
             }
-            if (allResult.size() > 0) {
+            if (allResult.size() > 0)
+            {
                 typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
-            } else {
+            }
+            else
+            {
                 throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
             }
-        } catch (IOException e) {
+        }
+        catch (IOException e)
+        {
             e.printStackTrace();
         }
         return typeAliasesPackage;
     }
 
-    //    @Bean
+    public Resource[] resolveMapperLocations(String[] mapperLocations)
+    {
+        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
+        List<Resource> resources = new ArrayList<Resource>();
+        if (mapperLocations != null)
+        {
+            for (String mapperLocation : mapperLocations)
+            {
+                try
+                {
+                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
+                    resources.addAll(Arrays.asList(mappers));
+                }
+                catch (IOException e)
+                {
+                    // ignore
+                }
+            }
+        }
+        return resources.toArray(new Resource[resources.size()]);
+    }
+
+//    @Bean
 //    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
 //    {
 //        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
@@ -85,12 +127,14 @@ public class MyBatisConfig {
 //        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
 //        sessionFactory.setDataSource(dataSource);
 //        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
-//        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
+//        sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
 //        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
 //        return sessionFactory.getObject();
 //    }
+
     @Bean
-    public SqlSessionFactory sqlSessionFactorys(DataSource dataSource) throws Exception {
+    public SqlSessionFactory sqlSessionFactorys(DataSource dataSource) throws Exception
+    {
         String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage");
         String mapperLocations = env.getProperty("mybatis-plus.mapperLocations");
         String configLocation = env.getProperty("mybatis-plus.configLocation");

+ 29 - 0
fs-user-app/src/main/java/com/fs/core/config/RedisConfig.java

@@ -9,6 +9,7 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
@@ -82,4 +83,32 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+
+    @Bean
+    public DefaultRedisScript<Long> limitScript()
+    {
+        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
+        redisScript.setScriptText(limitScriptText());
+        redisScript.setResultType(Long.class);
+        return redisScript;
+    }
+
+    /**
+     * 限流脚本
+     */
+    private String limitScriptText()
+    {
+        return "local key = KEYS[1]\n" +
+                "local count = tonumber(ARGV[1])\n" +
+                "local time = tonumber(ARGV[2])\n" +
+                "local current = redis.call('get', key);\n" +
+                "if current and tonumber(current) > count then\n" +
+                "    return current;\n" +
+                "end\n" +
+                "current = redis.call('incr', key)\n" +
+                "if tonumber(current) == 1 then\n" +
+                "    redis.call('expire', key, time)\n" +
+                "end\n" +
+                "return current;";
+    }
 }

+ 9 - 3
fs-user-app/src/main/java/com/fs/core/datasource/DynamicDataSource.java

@@ -1,5 +1,6 @@
 package com.fs.core.datasource;
 
+import com.fs.core.datasource.DynamicDataSourceContextHolder;
 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
 import javax.sql.DataSource;
@@ -7,8 +8,8 @@ import java.util.Map;
 
 /**
  * 动态数据源
- * 
- 
+ *
+
  */
 public class DynamicDataSource extends AbstractRoutingDataSource
 {
@@ -19,9 +20,14 @@ public class DynamicDataSource extends AbstractRoutingDataSource
         super.afterPropertiesSet();
     }
 
+    public static void setDataSource(String dataSourceType) {
+        // 确保使用数据源类型的字符串值
+        DynamicDataSourceContextHolder.setDataSourceType(dataSourceType);
+    }
+
     @Override
     protected Object determineCurrentLookupKey()
     {
         return DynamicDataSourceContextHolder.getDataSourceType();
     }
-}
+}

+ 93 - 0
fs-user-app/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="/home/fs-user-app/logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 30 -->
+			<maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 30 -->
+            <maxHistory>30</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.fs" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+
+	<!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+    </root>
+
+	<!--系统用户操作日志-->
+    <logger name="sys-user" level="info">
+        <appender-ref ref="sys-user"/>
+    </logger>
+</configuration>