Jelajahi Sumber

Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java

Guos 6 hari lalu
induk
melakukan
d157bff9c0
36 mengubah file dengan 678 tambahan dan 83 penghapusan
  1. 3 1
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  2. 19 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwDeptController.java
  3. 16 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java
  4. 10 10
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  5. 1 0
      fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
  6. 2 1
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  7. 1 0
      fs-service/src/main/java/com/fs/course/config/CourseConfig.java
  8. 4 2
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  9. 1 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCoursePeriodDaysMapper.java
  10. 1 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java
  11. 2 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  12. 78 42
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  13. 5 0
      fs-service/src/main/java/com/fs/his/domain/FsUser.java
  14. 12 8
      fs-service/src/main/java/com/fs/his/mapper/FsStorePaymentMapper.java
  15. 19 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  16. 147 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  17. 1 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  18. 47 0
      fs-service/src/main/java/com/fs/qw/domain/QwDeptTreeSelect.java
  19. 1 1
      fs-service/src/main/java/com/fs/qw/mapper/QwTagMapper.java
  20. 7 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  21. 5 0
      fs-service/src/main/java/com/fs/qw/param/QwUserListParam.java
  22. 3 0
      fs-service/src/main/java/com/fs/qw/service/IQwDeptService.java
  23. 1 0
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  24. 69 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwDeptServiceImpl.java
  25. 11 2
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  26. 1 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwTagGroupServiceImpl.java
  27. 12 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  28. 5 2
      fs-service/src/main/java/com/fs/sop/mapper/SopUserLogsMapper.java
  29. 2 2
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  30. 18 3
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  31. 5 5
      fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  32. 6 0
      fs-service/src/main/resources/application-config-druid-fby.yml
  33. 133 1
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  34. 14 0
      fs-service/src/main/resources/mapper/qw/QwUserMapper.xml
  35. 14 0
      fs-user-app/src/main/java/com/fs/app/controller/CourseController.java
  36. 2 0
      fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

+ 3 - 1
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -225,8 +225,10 @@ public class FsUserCourseVideoController extends AppBaseController {
     public ResponseResult<PageInfo<FsUserCourseVideoPageListVO>> todayCourseList(@RequestParam(defaultValue = "1") Integer pageNum,
                                                                                  @RequestParam(defaultValue = "10") Integer pageSize, String keyword) {
         Long companyId = getCompanyId();
+        log.info("销售小程序-今日课程,获取公司id:{}", companyId);
         if (Objects.isNull(companyId)) {
-            ResponseResult.fail(400, "未获取到公司ID,请重新登录后再试");
+            log.error("未获取到公司id,进入提示逻辑。。。。。。。。");
+            return ResponseResult.fail(400, "未获取到公司ID,请重新登录后再试");
         }
 
         Map<String, Object> params = new HashMap<>();

+ 19 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwDeptController.java

@@ -7,6 +7,7 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
@@ -120,4 +121,22 @@ public class QwDeptController extends BaseController
     {
         return toAjax(qwDeptService.deleteQwDeptByIds(ids));
     }
+
+    /**
+     * @Description: 获取企微部门 按Treeselect返回 每一个企微主体有自己的部门,按企微主体查询
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/30 9:33
+     */
+    @GetMapping("/treeselect")
+    public AjaxResult treeselect(QwDept qwDept)
+    {
+        if(StringUtils.isEmpty(qwDept.getCorpId())){
+            return AjaxResult.error("请选择企微主体");
+        }
+        List<QwDept> depts = qwDeptService.selectQwDeptList(qwDept);
+        return AjaxResult.success(qwDeptService.buildDeptTreeSelect(depts));
+    }
+
 }

+ 16 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java

@@ -116,6 +116,22 @@ public class QwUserController extends BaseController
         startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUser.setCompanyId(loginUser.getCompany().getCompanyId());
+        // 添加部门查询条件
+        //本部门
+        Long deptId = qwUser.getDeptId();
+        if(deptId!=null){
+            List<Long> qwDeptIdList = new ArrayList<>();
+            if (deptId!=null){
+                qwDeptIdList.add(deptId);
+            }
+            // 本部门的下级部门
+            List<Long> deptList = qwUserService.selectDeptByParentId(deptId,qwUser.getCorpId());
+            if (!deptList.isEmpty()){
+                qwDeptIdList.addAll(deptList);
+            }
+            qwUser.setQwDeptIdList(qwDeptIdList);
+        }
+
         List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
         return getDataTable(list);
     }

+ 10 - 10
fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java

@@ -264,16 +264,16 @@ public class SendMsg {
                 }
             }
             // 推送 APP
-//            if (!setting.getSetting().isEmpty()) {
-//                new Thread(() -> {
-//                    try {
-//                        List<QwSopTempSetting.Content.Setting> settings = JSON.parseArray(JSON.toJSONString(setting.getSetting()), QwSopTempSetting.Content.Setting.class).stream().filter(e -> "9".equals(e.getContentType())).collect(Collectors.toList());
-//                        asyncSopTestService.asyncSendMsgBySopAppLinkNormalIM(settings, qwSopLogs.getCorpId(), user.getCompanyUserId(), qwSopLogs.getFsUserId());
-//                    } catch (Exception e) {
-//                        log.error("推送APP失败", e);
-//                    }
-//                }).start();
-//            }
+            if (!setting.getSetting().isEmpty()) {
+                new Thread(() -> {
+                    try {
+                        List<QwSopTempSetting.Content.Setting> settings = JSON.parseArray(JSON.toJSONString(setting.getSetting()), QwSopTempSetting.Content.Setting.class).stream().filter(e -> "9".equals(e.getContentType())).collect(Collectors.toList());
+                        asyncSopTestService.asyncSendMsgBySopAppLinkNormalIM(settings, qwSopLogs.getCorpId(), user.getCompanyUserId(), qwSopLogs.getFsUserId());
+                    } catch (Exception e) {
+                        log.error("推送APP失败", e);
+                    }
+                }).start();
+            }
             qwSopLogs.setSend(true);
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             QwSopLogs updateQwSop = new QwSopLogs();

+ 1 - 0
fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -2,6 +2,7 @@ package com.fs.app.controller;
 
 
 import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson.JSON;
 import com.fs.app.taskService.*;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;

+ 2 - 1
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -434,7 +434,8 @@ public class CompanyServiceImpl implements ICompanyService
                 orderMap.setOrderId(order.getId());
                 orderMap.setTuiMoneyStatus(1);
                 storeOrderMapper.updateFsStoreOrder(orderMap);
-                BigDecimal money = order.getPayMoney().add(order.getPayRemain());
+                // order.getPayRemain() 数据库实际没有这个字段了 直接使用 应付金额
+                BigDecimal money = order.getPayPrice();
                 company.setMoney(company.getMoney().add(money));
                 companyMapper.updateCompany(company);
                 CompanyMoneyLogs log=new CompanyMoneyLogs();

+ 1 - 0
fs-service/src/main/java/com/fs/course/config/CourseConfig.java

@@ -16,6 +16,7 @@ public class CourseConfig implements Serializable {
     private Integer maxBufferLength;//最大缓冲时长
     private Integer videoIntegral;//每十分钟获取多少积分
     private Integer answerIntegral;//答题获得积分
+    private Integer appAnswerIntegral; //app答题积分
     private Integer defaultLine;//默认看课线路
     private String realLinkDomainName;//真链域名
     private String authDomainName;//网页授权域名

+ 4 - 2
fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -246,12 +246,14 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             " ,count(o.log_id) send_number" +
             " ,sum(if((o.user_id is not null or o.user_id>0) and o.log_type=3,1,0)) is_user_wait_number" +
             " ,sum(if((o.user_id is null or o.user_id=0) and o.log_type=3,1,0)) no_user_wait_number" +
-            " ,sum(ifnull(fcr.amount,0)) red_amount" +
+//            " ,sum(ifnull(fcr.amount,0)) red_amount" +
+            ",(SELECT SUM(amount) FROM fs_course_red_packet_log \n" +
+            "     WHERE user_id = o.user_id AND video_id = o.video_id) as red_amount " +
             "</if> " +
             "FROM fs_course_watch_log o " +
             "<if test= 'sendType != 1 '> " +
             " LEFT JOIN qw_user qu on qu.id=o.qw_user_id " +
-            " LEFT JOIN fs_course_red_packet_log fcr on o.user_id = fcr.user_id and fcr.video_id = o.video_id" +
+//            " LEFT JOIN fs_course_red_packet_log fcr on o.user_id = fcr.user_id and fcr.video_id = o.video_id" + //会有笛卡尔积问题
             "</if>\n" +
             "LEFT JOIN fs_user_course_video v on v.video_id=o.video_id \n" +
             "LEFT JOIN fs_user_course uc on uc.course_id=v.course_id\n" +

+ 1 - 1
fs-service/src/main/java/com/fs/course/mapper/FsUserCoursePeriodDaysMapper.java

@@ -120,6 +120,6 @@ public interface FsUserCoursePeriodDaysMapper extends BaseMapper<FsUserCoursePer
 
     Long selectFsUserCoursePeriodDaysCount(FsUserCoursePeriodDays fsUserCoursePeriodDays);
 
-    @Select("SELECT distinct period_id from fs_user_course_period_days  where start_date_time >=#{periodSTime} and end_date_time <=#{periodETime} ")
+    @Select("SELECT distinct period_id from fs_user_course_period_days  where day_date >=#{periodSTime} and day_date <=#{periodETime} ")
     List<Long> selectFsUserCoursePeriodDaysByTime(@Param("periodSTime") String periodSTime,@Param("periodETime") String periodETime);
 }

+ 1 - 1
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java

@@ -145,7 +145,7 @@ public interface FsUserCourseVideoMapper
     Long selectFsUserCourseVideoByCourseSort(@Param("courseId")Long courseId, @Param("courseSort")Long courseSort);
 
 
-    @Select("select video_id dict_value, title dict_label  from fs_user_course_video where course_id=#{id} and is_del = 0 order by course_sort")
+    @Select("select video_id dict_value, title dict_label , thumbnail  dict_img_url from fs_user_course_video where course_id=#{id} and is_del = 0 order by course_sort")
     List<OptionsVO> selectFsUserCourseVodeAllList(Long id);
 
     @Select({"<script> " +

+ 2 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -208,4 +208,6 @@ public interface IFsUserCourseVideoService
      * 查询选择使用的视频列表
      */
     List<FsUserCourseVideoChooseVO> getChooseCourseVideoListByMap(Map<String, Object> params);
+
+    R sendAppReward(FsCourseSendRewardUParam param);
 }

+ 78 - 42
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -48,6 +48,7 @@ import com.fs.his.mapper.FsUserIntegralLogsMapper;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.param.WxSendRedPacketParam;
 import com.fs.his.service.IFsStorePaymentService;
+import com.fs.his.service.IFsUserIntegralLogsService;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.service.IFsUserWxService;
 import com.fs.his.utils.ConfigUtil;
@@ -253,6 +254,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     @Autowired
     private BalanceRollbackErrorMapper balanceRollbackErrorMapper;
 
+    @Autowired
+    private IFsUserIntegralLogsService iFsUserIntegralLogsService;
+
 
 
 
@@ -1253,7 +1257,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         try {
             // 尝试获取锁,等待时间5秒,锁过期时间30秒
-            boolean isLocked = lock.tryLock(5, 60, TimeUnit.SECONDS);
+            boolean isLocked = lock.tryLock(5, 300, TimeUnit.SECONDS);
             if (!isLocked) {
                 logger.warn("获取锁失败,用户ID:{},视频ID:{}", param.getUserId(), param.getVideoId());
                 return R.error("操作频繁,请稍后再试!");
@@ -1378,45 +1382,19 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         // 准备发送红包参数
         WxSendRedPacketParam packetParam = new WxSendRedPacketParam();
 
+        //判断是否走服务号openId发红包
         if (user.getMpOpenId()!=null&&!isNewWxMerchant){
             packetParam.setOpenId(user.getMpOpenId());
         }else {
-            //修复数据
+            //查询是否绑定小程序
             FsUserWx fsUserWx = fsUserWxService.selectByAppIdAndUserId(param.getAppId(),user.getUserId(),1);
             if (fsUserWx ==null){
-                if (user.getCourseMaOpenId()==null){
-                    logger.error(" 【转账openId参数错误】:{}", user.getUserId());
-                    return R.error("openId参数错误,请清理缓存后重新授权!");
-                }
-                packetParam.setOpenId(user.getCourseMaOpenId());
-                try {
-                    handleFsUserWx(user,param.getAppId());
-                }catch (Exception e){
-                    logger.error(" 【更新或插入用户与小程序的绑定关系失败】:{}", user.getUserId(),e);
-                }
-
+                return R.error("openId参数错误,请清理缓存重新授权");
             }else {
                 packetParam.setOpenId(fsUserWx.getOpenId());
             }
         }
 
-//        packetParam.setOpenId(user.getMpOpenId());
-//        // 来源是小程序切换openId
-//        if (param.getSource() == 2) {
-//            //处理多小程序问题
-//            FsUserWx fsUserWx = fsUserWxService.selectByAppIdAndUserId(param.getAppId(),user.getUserId(),1);
-//            if (fsUserWx ==null){
-//                try {
-//                    handleFsUserWx(user,param.getAppId());
-//                }catch (Exception e){
-//                    logger.error("【更新或插入用户与小程序的绑定关系失败】:{}", user.getUserId());
-//                }
-//            }else {
-//                packetParam.setOpenId(fsUserWx.getOpenId());
-//            }
-//            //查出公司绑定openid并赋值
-//        }
-
         //判断服务号配置是否存在
         if (StringUtils.isNotEmpty(config.getMpAppId())){
             packetParam.setMpAppId(config.getMpAppId());
@@ -1564,20 +1542,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             if (user.getMpOpenId()!=null&&!isNewWxMerchant){
                 packetParam.setOpenId(user.getMpOpenId());
             }else {
-                //修复数据
+                //查询是否绑定小程序
                 FsUserWx fsUserWx = fsUserWxService.selectByAppIdAndUserId(param.getAppId(),user.getUserId(),1);
                 if (fsUserWx ==null){
-                    if (user.getCourseMaOpenId()==null){
-                        logger.error(" 【转账openId参数错误】:{}", user.getUserId());
-                        return R.error("openId参数错误,请清理缓存后重新授权!");
-                    }
-                    packetParam.setOpenId(user.getCourseMaOpenId());
-                    try {
-                        handleFsUserWx(user,param.getAppId());
-                    }catch (Exception e){
-                        logger.error(" 【更新或插入用户与小程序的绑定关系失败】:{}", user.getUserId(),e);
-                    }
-
+                    return R.error("openId参数错误,请清理缓存重新授权");
                 }else {
                     packetParam.setOpenId(fsUserWx.getOpenId());
                 }
@@ -3244,5 +3212,73 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         return fsUserCourseVideoMapper.getChooseCourseVideoListByMap(params);
     }
 
+    @Override
+    public R sendAppReward(FsCourseSendRewardUParam param) {
+        // 获取用户信息
+        FsUser user = fsUserMapper.selectFsUserByUserId(param.getUserId());
+        if (user==null){
+            return R.error("会员被停用,无权限,请联系客服!");
+        }
+        FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideo(param.getUserId(), param.getVideoId(), param.getQwUserId(), param.getQwExternalId());
+        if (log == null) {
+            return R.error("无记录");
+        }
+        if (log.getRewardType() != null) {
+            return R.error("奖励已发放");
+        }
+
+        // 获取配置信息
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+
+        // 更新用户积分
+        FsUser userMap=new FsUser();
+        userMap.setUserId(user.getUserId());
+        Integer appAnswerIntegral = config.getAppAnswerIntegral();
+        if (appAnswerIntegral == null ){
+            appAnswerIntegral = config.getAnswerIntegral();
+        }
+        userMap.setIntegral(user.getIntegral()+(appAnswerIntegral==null?0:appAnswerIntegral));
+        fsUserMapper.updateFsUser(userMap);
+        CompletableFuture.runAsync(() -> {
+            FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
+            integralLogs.setIntegral(config.getAppAnswerIntegral().longValue());
+            integralLogs.setUserId(user.getUserId());
+            integralLogs.setBalance(userMap.getIntegral());
+            integralLogs.setLogType(17);
+            integralLogs.setBusinessId(StringUtils.isNotEmpty(log.getLogId().toString()) ? log.getLogId().toString() : null);
+            integralLogs.setCreateTime(new Date());
+//            integralLogs.setNickName(user.getNickName());
+//            integralLogs.setPhone(user.getPhone());
+            //integralLogs.setId(integralLogsService.getFsUserIntegralLogsInsertId());
+//        fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
+//            iFsUserIntegralLogsService.insertFsUserIntegralLogsMySql(integralLogs);
+            iFsUserIntegralLogsService.insertFsUserIntegralLogs(integralLogs);
+            //asyncAddIntegralLogs.saveLogAsync(integralLogs);
+
+            // 更新观看记录的奖励类型
+            log.setRewardType(2);
+            courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+
+            //转换红包
+            FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+            redPacketLog.setCourseId(param.getCourseId());
+            redPacketLog.setOutBatchNo(integralLogs.getId().toString());
+            redPacketLog.setCompanyId(param.getCompanyId());
+            redPacketLog.setUserId(param.getUserId());
+            redPacketLog.setVideoId(param.getVideoId());
+            redPacketLog.setStatus(1);
+            redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+            redPacketLog.setCompanyUserId(param.getCompanyUserId());
+            redPacketLog.setCreateTime(new Date());
+            redPacketLog.setAmount(BigDecimal.valueOf(config.getAppAnswerIntegral()).divide(BigDecimal.valueOf(1000)));
+            redPacketLog.setRemark("点播答题领取积分转");
+            redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+            redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+        });
+        return R.ok("奖励发放成功");
+    }
+
 }
 

+ 5 - 0
fs-service/src/main/java/com/fs/his/domain/FsUser.java

@@ -122,6 +122,11 @@ public class FsUser extends BaseEntity
     private Date  vipStartDate;
     private Date  vipEndDate;
     private Integer vipLevel;
+
+    /**
+     * 会员等级
+     */
+    private Integer level;
     private Integer vipStatus;
 
     private Integer sex;

+ 12 - 8
fs-service/src/main/java/com/fs/his/mapper/FsStorePaymentMapper.java

@@ -183,14 +183,18 @@ public interface FsStorePaymentMapper
     @Select({"<script> " +
             " SELECT " +
             " CASE " +
-            "   WHEN sp.business_type = 1 THEN CONCAT('inquiry-', sp.pay_code) " +
-            "   WHEN sp.business_type = 2 THEN CONCAT('store-', sp.pay_code) " +
-            "   WHEN sp.business_type = 3 THEN CONCAT('package-', sp.pay_code) " +
-            "   WHEN sp.business_type = 4 THEN CONCAT('course-', sp.pay_code) " +
-            "   WHEN sp.business_type = 5 THEN CONCAT('appvip-', sp.pay_code) " +
-            "   WHEN sp.business_type = 6 THEN CONCAT('integral-', sp.pay_code) " +
-            "   WHEN sp.business_type = 7 THEN CONCAT('payment-', sp.pay_code) " +
-            "   ELSE sp.pay_code " +
+            "   WHEN sp.status = -1 THEN CONCAT('refund-', sp.pay_code) " +
+            "   ELSE " +
+            "     CASE " +
+            "       WHEN sp.business_type = 1 THEN CONCAT('inquiry-', sp.pay_code) " +
+            "       WHEN sp.business_type = 2 THEN CONCAT('store-', sp.pay_code) " +
+            "       WHEN sp.business_type = 3 THEN CONCAT('package-', sp.pay_code) " +
+            "       WHEN sp.business_type = 4 THEN CONCAT('course-', sp.pay_code) " +
+            "       WHEN sp.business_type = 5 THEN CONCAT('appvip-', sp.pay_code) " +
+            "       WHEN sp.business_type = 6 THEN CONCAT('integral-', sp.pay_code) " +
+            "       WHEN sp.business_type = 7 THEN CONCAT('payment-', sp.pay_code) " +
+            "       ELSE sp.pay_code " +
+            "     END " +
             " END AS pay_code, " +
             "sp.*,u.nick_name,u.phone,s.store_name,c.company_name,cu.nick_name as companyUserName,fso.delivery_name,fso.package_name,fso.package_second_name ,csc.name miniProgramName " +
             " FROM fs_store_payment sp " +

+ 19 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java

@@ -315,6 +315,11 @@ public interface FsUserMapper
 
     FsUserSummaryCountVO countUserSummary(@Param("userId") Long userId, @Param("companyId") Long companyId);
 
+    Integer getUserTotal(@Param("userId") Long userId, @Param("companyId") Long companyId);
+
+    Integer getTodayNewUser(@Param("userId") Long userId, @Param("companyId") Long companyId);
+
+
     List<FsUserSummaryCountTagVO> countTag(@Param("userId") Long userId, @Param("companyId") Long companyId);
 
     Map<String, Long> countUserCourse(UserStatisticsCommonParam param);
@@ -410,6 +415,20 @@ public interface FsUserMapper
     Map<String, Object> countUserStats(
             UserStatisticsCommonParam param);
 
+    /**
+     * 查询观看和完成人数
+     */
+    Map<String, Object> countUserWatchStats(UserStatisticsCommonParam param);
+
+    /**
+     * 统计用户答题数据(答题次数、正确次数)
+     */
+    Map<String, Object> countUserAnswerStats(UserStatisticsCommonParam param);
+
+
+    /**查询红包数量和红包金额 */
+    Map<String, Object> countUserRedPacketStats(UserStatisticsCommonParam param);
+
     /**
      * 统计用户领取红包数据(红包个数、红包金额)
      * */

+ 147 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -846,8 +846,52 @@ public class FsUserServiceImpl implements IFsUserService {
         return fsUserMapper.selectFsUserByUserIds(userIds, companyUserId);
     }
 
+
     @Override
     public FsUserSummaryCountVO userSummaryCount(Long userId) {
+        // 判断是否是管理员
+        Long companyId;
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(userId);
+        if (companyUser != null && companyUser.isAdmin()) {
+            userId = 0L;
+            companyId = companyUser.getCompanyId();
+        } else {
+            companyId = null;
+        }
+
+        // 使用 CompletableFuture 异步执行两个查询
+
+        Long finalUserId = userId;
+        CompletableFuture<Integer> userTotalFuture = CompletableFuture.supplyAsync(() ->
+                fsUserMapper.getUserTotal(finalUserId, companyId));
+
+        CompletableFuture<Integer> todayNewUserFuture = CompletableFuture.supplyAsync(() ->
+                fsUserMapper.getTodayNewUser(finalUserId, companyId));
+
+        CompletableFuture<List<FsUserSummaryCountTagVO>> countTagList = CompletableFuture.supplyAsync(() ->
+                fsUserMapper.countTag(finalUserId, companyId));
+
+        // 等待查询结果并构建返回对象
+        FsUserSummaryCountVO fsUserSummaryCountVO = new FsUserSummaryCountVO();
+        try {
+            fsUserSummaryCountVO.setUserTotal(userTotalFuture.get());
+            fsUserSummaryCountVO.setTodayNewUser(todayNewUserFuture.get());
+            fsUserSummaryCountVO.setTagList(countTagList.get());
+
+        } catch (InterruptedException | ExecutionException e) {
+            logger.error("异步查询用户统计数据失败", e);
+            // 设置默认值
+            fsUserSummaryCountVO.setUserTotal(0);
+            fsUserSummaryCountVO.setTodayNewUser(0);
+            fsUserSummaryCountVO.setTagList(new ArrayList<>());
+        }
+
+        return fsUserSummaryCountVO;
+    }
+
+    public FsUserSummaryCountVO userSummaryCountOld(Long userId) {
+        // 开始时间
+        long start = System.currentTimeMillis();
         // 判断是否是管理员
         Long companyId = null;
         CompanyUser companyUser = companyUserMapper.selectCompanyUserById(userId);
@@ -855,9 +899,24 @@ public class FsUserServiceImpl implements IFsUserService {
             userId = 0L;
             companyId = companyUser.getCompanyId();
         }
+        long start2 = System.currentTimeMillis();
+        logger.info("countUserSummary 执行耗时: {}ms", start2 - start);
+
+        //     Integer getUserTotal(@Param("userId") Long userId, @Param("companyId") Long companyId);
+        //
+        //    Integer getTodayNewUser(@Param("userId") Long userId, @Param("companyId") Long companyId);
+
+
         FsUserSummaryCountVO fsUserSummaryCountVO = fsUserMapper.countUserSummary(userId, companyId);
+        long start3 =System.currentTimeMillis();
+        logger.info("countTag 执行耗时: {}ms", start3 - start2);
         List<FsUserSummaryCountTagVO> countTagList = fsUserMapper.countTag(userId, companyId);
         fsUserSummaryCountVO.setTagList(countTagList);
+
+        // 结束时间
+        long end = System.currentTimeMillis();
+        logger.info("countUserAnswerStats 执行耗时: {}ms", end - start3);
+
         return fsUserSummaryCountVO;
     }
 
@@ -1201,7 +1260,95 @@ public class FsUserServiceImpl implements IFsUserService {
         return fsUserMapper.selectFsUserListByJointUserNameKey(userNameKey);
     }
 
+
+    /**
+     * @Description: 该方法原方法是 getUserStatisticsOld
+     * 目的为了优化sql查询 fsUserMapper.countUserStats 现拆分成三次异步查询
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/29 15:34
+     */
     private FsUserStatisticsVO getUserStatistics(UserStatisticsCommonParam param) {
+
+        FsUserStatisticsVO fsUserStatisticsVO = new FsUserStatisticsVO();
+
+        // 判断是否是管理员
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
+        if (companyUser != null && companyUser.isAdmin()){
+            param.setUserId(0L);
+            param.setCompanyId(companyUser.getCompanyId());
+        }
+
+        // 异步调用三个查询方法
+        CompletableFuture<Map<String, Object>> watchStatsFuture = CompletableFuture.supplyAsync(() ->
+                fsUserMapper.countUserWatchStats(param));
+
+        CompletableFuture<Map<String, Object>> answerStatsFuture = CompletableFuture.supplyAsync(() ->
+                fsUserMapper.countUserAnswerStats(param));
+
+        CompletableFuture<Map<String, Object>> redPacketStatsFuture = CompletableFuture.supplyAsync(() ->
+                fsUserMapper.countUserRedPacketStats(param));
+
+        try {
+            // 等待所有查询完成
+            Map<String, Object> watchMap = watchStatsFuture.get();
+            Map<String, Object> answerMap = answerStatsFuture.get();
+            Map<String, Object> redPacketMap = redPacketStatsFuture.get();
+
+            // 处理观看和完成人数统计
+            if (watchMap != null) {
+                fsUserStatisticsVO.setCourseWatchNum(Integer.valueOf(watchMap.getOrDefault("courseWatchNum", 0L).toString()))
+                        .setCourseCompleteNum(Integer.valueOf(watchMap.getOrDefault("courseCompleteNum", 0L).toString()));
+
+                Long completeNum = (Long) watchMap.get("courseCompleteNum");
+                Long watchNum = (Long) watchMap.get("courseWatchNum");
+                if (completeNum != null && watchNum != null && watchNum > 0) {
+                    int courseCompleteRate = BigDecimal.valueOf(completeNum)
+                            .divide(BigDecimal.valueOf(watchNum), 2, RoundingMode.HALF_UP)
+                            .multiply(BigDecimal.valueOf(100))
+                            .intValue();
+                    fsUserStatisticsVO.setCourseCompleteRate(courseCompleteRate);
+                }
+            }
+
+            // 处理答题统计
+            if (answerMap != null) {
+                Long answerRightNum = (Long) answerMap.get("answerRightNum");
+                Long answerNum = (Long) answerMap.get("answerNum");
+
+                fsUserStatisticsVO.setAnswerNum(answerNum.intValue()).setAnswerRightNum(answerRightNum.intValue());
+
+                if (answerRightNum != null && answerNum != null && answerNum > 0) {
+                    int answerCompleteRate = BigDecimal.valueOf(answerRightNum)
+                            .divide(BigDecimal.valueOf(answerNum), 2, RoundingMode.HALF_UP)
+                            .multiply(BigDecimal.valueOf(100))
+                            .intValue();
+                    fsUserStatisticsVO.setAnswerRightRate(answerCompleteRate);
+                }
+            }
+
+            // 处理红包统计
+            if (redPacketMap != null) {
+                Object numObj = redPacketMap.get("redPacketNum");
+                Object amtObj = redPacketMap.get("redPacketAmount");
+                if (numObj != null && amtObj != null) {
+                    fsUserStatisticsVO.setRedPacketNum(Integer.parseInt(numObj.toString()))
+                            .setRedPacketAmount(new BigDecimal(amtObj.toString()));
+                }
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            // 异常处理
+            logger.error("异步查询用户统计数据失败", e);
+        }
+
+        return fsUserStatisticsVO;
+    }
+
+
+
+
+    private FsUserStatisticsVO getUserStatisticsNew(UserStatisticsCommonParam param) {
         FsUserStatisticsVO fsUserStatisticsVO = new FsUserStatisticsVO();
 
         // 判断是否是管理员

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -743,7 +743,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             //绑定销售
             FsUserScrm fsuser = userService.selectFsUserById(userId);
             if (param.getCompanyUserId() != null) {
-                if (ObjectUtil.isNotEmpty(fsuser.getCompanyUserId()) && !Objects.equals(fsuser.getCompanyUserId(), param.getCompanyUserId())) {
+                if (!CloudHostUtils.hasCloudHostName("鸿森堂") && ObjectUtil.isNotEmpty(fsuser.getCompanyUserId()) && !Objects.equals(fsuser.getCompanyUserId(), param.getCompanyUserId())) {
                     CompanyUser companyUser = companyUserService.selectCompanyUserById(fsuser.getCompanyUserId());
                     return R.error(String.format("请联系%s销售进行购买商品!", companyUser.getNickName()));
                 } else {

+ 47 - 0
fs-service/src/main/java/com/fs/qw/domain/QwDeptTreeSelect.java

@@ -0,0 +1,47 @@
+package com.fs.qw.domain;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+public class QwDeptTreeSelect implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+    private String label;
+    private List<QwDeptTreeSelect> children;
+
+    // 构造方法
+    public QwDeptTreeSelect() {
+    }
+
+    public QwDeptTreeSelect(Long id, String label) {
+        this.id = id;
+        this.label = label;
+    }
+
+    // getter和setter
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public List<QwDeptTreeSelect> getChildren() {
+        return children;
+    }
+
+    public void setChildren(List<QwDeptTreeSelect> children) {
+        this.children = children;
+    }
+}

+ 1 - 1
fs-service/src/main/java/com/fs/qw/mapper/QwTagMapper.java

@@ -86,7 +86,7 @@ public interface QwTagMapper
     QwTagVO selectQwTagByName(@Param("trimTag") String trimTag, @Param("corpId")String corpId );
 
     @Select("<script>" +
-            "SELECT name FROM qw_tag WHERE tag_id IN " +
+            "SELECT distinct name FROM qw_tag WHERE tag_id IN " +
             "<foreach collection='date.tagIds' item='tagId' open='(' separator=',' close=')'>" +
             "#{tagId}" +
             "</foreach>" +

+ 7 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java

@@ -163,6 +163,12 @@ public interface QwUserMapper extends BaseMapper<QwUser>
             "                       #{item} " +
             "                   </foreach> " +
             "            </if>" +
+            "            <if test=\"qwDeptIdList != null and !qwDeptIdList.isEmpty() and  userType != '00' \">" +
+            "               AND qd.dept_id IN " +
+            "                   <foreach collection='qwDeptIdList' item='item' open='(' separator=',' close=')'> " +
+            "                       #{item} " +
+            "                   </foreach> " +
+            "            </if>" +
             "ORDER BY  qu.login_status asc,qu.tool_status desc " +
             "</script>"})
     List<QwUserVO> selectQwUserListStaffVO(QwUserListParam qwUser);
@@ -442,4 +448,5 @@ public interface QwUserMapper extends BaseMapper<QwUser>
 
     List<QwOptionsVO> selectQwCompanyListOptionsVOBySys();
 
+    List<Long> selectDeptByParentId(@Param("deptId")Long deptId,@Param("corpId") String corpId);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/qw/param/QwUserListParam.java

@@ -57,6 +57,11 @@ public class QwUserListParam {
      * 销售部门
      */
     private List<Long> cuDeptIdList;
+    /**
+     * 企微部门
+     */
+    private List<Long> qwDeptIdList;
+
 
     /**
      * 部门类型 00 管理员 01 员工

+ 3 - 0
fs-service/src/main/java/com/fs/qw/service/IQwDeptService.java

@@ -2,6 +2,7 @@ package com.fs.qw.service;
 
 import com.fs.common.core.domain.R;
 import com.fs.qw.domain.QwDept;
+import com.fs.qw.domain.QwDeptTreeSelect;
 
 import java.util.List;
 
@@ -69,4 +70,6 @@ public interface IQwDeptService
      * @return 结果
      */
     public int deleteQwDeptById(Long id);
+
+    List<QwDeptTreeSelect> buildDeptTreeSelect(List<QwDept> depts);
 }

+ 1 - 0
fs-service/src/main/java/com/fs/qw/service/IQwUserService.java

@@ -199,4 +199,5 @@ public interface IQwUserService
 
     List<QwOptionsVO> selectQwCompanyListOptionsVOBySys();
 
+    List<Long> selectDeptByParentId(Long deptId,String cropId);
 }

+ 69 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwDeptServiceImpl.java

@@ -2,6 +2,7 @@ package com.fs.qw.service.impl;
 
 import com.fs.common.core.domain.R;
 import com.fs.qw.domain.QwDept;
+import com.fs.qw.domain.QwDeptTreeSelect;
 import com.fs.qw.mapper.QwDeptMapper;
 import com.fs.qw.service.IQwDeptService;
 import com.fs.qwApi.domain.QwDeptResult;
@@ -10,7 +11,11 @@ import com.fs.qwApi.service.QwApiService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 企业微信部门Service业务层处理
@@ -129,4 +134,68 @@ public class QwDeptServiceImpl implements IQwDeptService
     {
         return qwDeptMapper.deleteQwDeptById(id);
     }
+
+    /**
+     * @Description: 按TreeSelect结构返回部门
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/30 9:37
+     */
+    @Override
+    /**
+     * 将部门列表转换为TreeSelect树形结构
+     */
+    public List<QwDeptTreeSelect> buildDeptTreeSelect(List<QwDept> deptList) {
+        if (deptList == null || deptList.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // 按父部门ID分组
+        Map<Long, List<QwDept>> deptMap = deptList.stream()
+                .collect(Collectors.groupingBy(QwDept::getParentid));
+
+        // 获取所有根部门(parentId = 0)
+        List<QwDept> rootDepts = deptMap.getOrDefault(0L, new ArrayList<>());
+
+        // 构建树形结构
+        return rootDepts.stream()
+                .map(dept -> buildTreeSelect(dept, deptMap))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 递归构建TreeSelect节点
+     */
+    private QwDeptTreeSelect buildTreeSelect(QwDept dept, Map<Long, List<QwDept>> deptMap) {
+        return buildTreeSelect(dept, deptMap, 0);
+    }
+
+    /**
+     * 递归构建TreeSelect节点,最多5级
+     */
+    private QwDeptTreeSelect buildTreeSelect(QwDept dept, Map<Long, List<QwDept>> deptMap, int depth) {
+        // 限制最多5级,避免死循环
+        if (depth > 5) {
+            return new QwDeptTreeSelect(dept.getDeptId(), dept.getDeptName());
+        }
+
+        QwDeptTreeSelect treeSelect = new QwDeptTreeSelect(dept.getDeptId(), dept.getDeptName());
+
+        // 获取当前部门的子部门
+        List<QwDept> children = deptMap.get(dept.getDeptId());
+        if (children != null && !children.isEmpty()) {
+            // 递归构建子节点
+            List<QwDeptTreeSelect> childNodes = children.stream()
+                    .map(child -> buildTreeSelect(child, deptMap, depth + 1))
+                    .collect(Collectors.toList());
+            treeSelect.setChildren(childNodes);
+        }
+
+        return treeSelect;
+    }
+
+
+
+
 }

+ 11 - 2
fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java

@@ -3929,9 +3929,10 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
                         qw.setDescription(followUser.getDescription()); // 设置描述信息
                         List<Tag> tags = followUser.getTags();
 
+                        List<String> tagArr = new ArrayList<>();
+
                         if (tags != null && tags.size() > 0) {
 
-                            List<String> tagArr = new ArrayList<>();
                             for (Tag tag : tags) {
                                 if (tag != null && tag.getTag_id() != null) {
                                     tagArr.add(tag.getTag_id());
@@ -3944,10 +3945,18 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
 
                             //新客对话
 //                            processTagsAllByAiChat(qwExternalContact1,corpId,tagArr);
+                        }else {
 
-                            qw.setTagIds(JSON.toJSONString(tagArr)); // 设置标签ID
+                            //客户自带标签空干净了
+                            logger.info("客户自带标签空干净了:"+qwExternalContact1.getName()+"|公司"+qwExternalContact1.getCorpId()+"|员工"+qwExternalContact1.getUserId()+"|总标签"+tagArr+"|原标签"+qwExternalContact1.getTagIds());
+
+                            //销售删除客户-找这个销售的sop任务中所有的营期里有没有这个客户,删了
+                            sopUserLogsInfoMapper.deleteByQwUserIdAndCorpIdToContactId(userID,corpId, externalUserID);
+                            sopUserLogsInfoMapper.deleteByQwUserIdAndCorpIdToContactIdByChat(userID,corpId, externalUserID);
 
                         }
+
+                        qw.setTagIds(JSON.toJSONString(tagArr)); // 设置标签ID
                         if (followUser.getRemark_mobiles() != null && followUser.getRemark_mobiles().size() > 0) {
                             List<String> remarkMobiles = followUser.getRemark_mobiles();
                             qw.setRemarkMobiles(JSON.toJSONString(remarkMobiles));

+ 1 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwTagGroupServiceImpl.java

@@ -159,6 +159,7 @@ public class QwTagGroupServiceImpl implements IQwTagGroupService {
     public R deleteQwTagGroupByIds(Long[] ids) {
         for (Long id : ids) {
             QwTagGroup qwTagGroup = qwTagGroupMapper.selectQwTagGroupById(id);
+            log.info("删除标签组-"+qwTagGroup);
             if (qwTagGroup.getGroupFrom() != null && qwTagGroup.getGroupFrom() == 1) {
                 QwTagParam qwTagParam = new QwTagParam();
                 qwTagParam.setGroup_id(Arrays.asList(qwTagGroup.getGroupId()));

+ 12 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -1559,6 +1559,18 @@ public class QwUserServiceImpl implements IQwUserService
         return qwUserMapper.selectQwCompanyListOptionsVOBySys();
     }
 
+    /**
+     * @Description: 根据企微部门查询下级部门id
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/30 14:27
+     */
+    @Override
+    public List<Long> selectDeptByParentId(Long deptId,String cropId) {
+        return qwUserMapper.selectDeptByParentId(deptId,cropId);
+    }
+
 
     /**
      * 构建查询条件

+ 5 - 2
fs-service/src/main/java/com/fs/sop/mapper/SopUserLogsMapper.java

@@ -389,6 +389,9 @@ List<SopUserLogsVO> selectSopUserLogsGroupListByParam(@Param("maps") SopUserLogs
     void replaceUser(@Param("vo") ReplaceUserDto vo);
 
     @DataSource(DataSourceType.SOP)
-    @Select("SELECT * FROM sop_user_logs WHERE sop_id = #{sopId} AND qw_user_id = #{qwUserId}")
-    SopUserLogs queryUserBySopId(@Param("qwUserId")String qwUserId,@Param("sopId") String sopId);
+    @Select("<script>" +"SELECT * FROM sop_user_logs WHERE sop_id = #{sopId} AND qw_user_id = #{qwUserId} " +
+            "<if test=\"chatId != null and chatId != ''\"> and chat_id = #{chatId} </if>" +
+            " limit 1 " +
+            "</script>")
+    SopUserLogs queryUserBySopId(@Param("qwUserId")String qwUserId,@Param("sopId") String sopId,@Param("chatId") String chatId);
 }

+ 2 - 2
fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java

@@ -1099,8 +1099,8 @@ public class QwSopLogsServiceImpl extends ServiceImpl<QwSopLogsMapper, QwSopLogs
 
                                 //有app的异步推送消息
                                 if (!setting.isEmpty()) {
-                                    asyncSopTestService.asyncSendMsgBySopAppLinkNormal(setting, param.getExternalId());
-                                    //asyncSopTestService.asyncSendMsgBySopAppLinkNormalIM(setting, param.getCorpId(),qwUser.getCompanyUserId(),log.getFsUserId());
+//                                    asyncSopTestService.asyncSendMsgBySopAppLinkNormal(setting, param.getExternalId());
+                                    asyncSopTestService.asyncSendMsgBySopAppLinkNormalIM(setting, param.getCorpId(),qwUser.getCompanyUserId(),log.getFsUserId());
                                 }
 
                                 if (log.getExpiryTime() == null) {

+ 18 - 3
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -7,6 +7,7 @@ import com.fs.common.annotation.DataSource;
 import com.fs.common.enums.DataSourceType;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.PubFun;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsUserCourse;
 import com.fs.course.domain.FsUserCourseVideo;
@@ -34,6 +35,8 @@ import lombok.AllArgsConstructor;
 import org.apache.commons.beanutils.ConvertUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
@@ -55,6 +58,10 @@ import java.util.stream.Collectors;
 @AllArgsConstructor
 public class QwSopTempServiceImpl implements IQwSopTempService
 {
+
+    @Autowired
+    private CloudHostProper cloudHostProper;
+
     private final QwSopTempMapper qwSopTempMapper;
     private final FastGptChatReplaceWordsMapper fastGptChatReplaceWordsMapper;
     private final IQwSopTempRulesService qwSopTempRulesService;
@@ -69,7 +76,6 @@ public class QwSopTempServiceImpl implements IQwSopTempService
     private final IQwSopService qwSopService;
     private final IQwUserService qwUserService;
 
-
     /**
      * 查询sop模板
      *
@@ -460,10 +466,19 @@ public class QwSopTempServiceImpl implements IQwSopTempService
                 QwSopTempSetting2.Content.Setting setting = new QwSopTempSetting2.Content.Setting();
                 setting.setLinkTitle(e.getTitle());
                 setting.setMiniprogramTitle(e.getTitle());
-                setting.setMiniprogramPicUrl(fsUserCourse.getImgUrl());
+
+                //用课节图片做封面
+                if("今正科技".equals(cloudHostProper.getCompanyName())){
+                    setting.setMiniprogramPicUrl(!StringUtil.isNullOrEmpty(e.getThumbnail())?e.getThumbnail():fsUserCourse.getImgUrl());
+                    setting.setLinkImageUrl(!StringUtil.isNullOrEmpty(e.getThumbnail())?e.getThumbnail():fsUserCourse.getImgUrl());
+                }else {
+                    setting.setMiniprogramPicUrl(fsUserCourse.getImgUrl());
+                    setting.setLinkImageUrl(fsUserCourse.getImgUrl());
+
+                }
+
                 setting.setIsBindUrl(1);
                 setting.setLinkDescribe(e.getTitle());
-                setting.setLinkImageUrl(fsUserCourse.getImgUrl());
                 setting.setContentType("4");
                 content.setContent(JSON.toJSONString(setting));
                 content.setIsBindUrl(1);

+ 5 - 5
fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java

@@ -558,7 +558,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     sopLogs.setQwUserKey(qwUser.getId());
 
                     // 设置实际发送人
-                    updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId());
+                    updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId(),groupUser.getChatId());
                     //域名
                     String companyUserId = qwUser.getCompanyUserId().toString();
                     String domainName = companyUserMapper.selectDomainByUserId(Long.parseLong(companyUserId));
@@ -696,7 +696,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     sopLogs.setExternalUserName(groupChat.getName());
                     sopLogs.setQwUserKey(qwUser.getId());
                     // 设置实际发送人
-                    updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId());
+                    updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId(),groupChat.getChatId());
 
                     QwSopCourseFinishTempSetting setting = new QwSopCourseFinishTempSetting();
 
@@ -1009,7 +1009,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 }
                 sopLogs.setContentJson(JSON.toJSONString(setting));
                 // 设置实际发送人
-                updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId());
+                updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId(),null);
 
                 sopLogsList.add(sopLogs);
             });
@@ -1023,9 +1023,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
         return R.ok();
     }
 
-    private void updateQwUserKey(QwSopLogs sopLogs, String qwUserId, String sopId) {
+    private void updateQwUserKey(QwSopLogs sopLogs, String qwUserId, String sopId,String chatId) {
         // 设置实际发送人
-        SopUserLogs qwSopLogs = sopUserLogsMapper.queryUserBySopId(qwUserId,sopId);
+        SopUserLogs qwSopLogs = sopUserLogsMapper.queryUserBySopId(qwUserId,sopId,chatId);
         if (qwSopLogs != null && ObjectUtil.isNotEmpty(qwSopLogs.getActualQwId())){
             // sopLogs.setQwUserid(qwUser.getQwUserId());
             sopLogs.setQwUserKey(qwSopLogs.getActualQwId());

+ 6 - 0
fs-service/src/main/resources/application-config-druid-fby.yml

@@ -81,6 +81,12 @@ fs :
 nuonuo:
   key: 10924508
   secret: A2EB20764D304D16
+  jwt:
+    # 加密秘钥
+    secret: f4e2e52034348f86b67cde581c0f9eb5
+    # token有效时长,7天,单位秒
+    expire: 31536000
+    header: AppToken
 
 # 存储捅配置
 tencent_cloud_config:

+ 133 - 1
fs-service/src/main/resources/mapper/his/FsUserMapper.xml

@@ -48,10 +48,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="courseMaOpenId"    column="course_ma_open_id"    />
         <result property="qwExtId"    column="qw_ext_id"    />
         <result property="qwUserId"    column="qw_user_id"    />
+        <result property="level" column="level"/>
     </resultMap>
 
     <sql id="selectFsUserVo">
-        select user_id,qw_ext_id,sex,is_buy,course_ma_open_id,is_push,is_add_qw,source,login_device,is_individuation_push,store_open_id,password,jpush_id, is_vip,vip_start_date,vip_end_date,vip_level,vip_status,nick_name,integral_status, avatar, phone, integral,sign_num, status, tui_user_id, tui_time, tui_user_count, ma_open_id, mp_open_id, union_id, is_del, user_code, remark, create_time, update_time, last_ip, balance,is_weixin_auth,parent_id,qw_user_id,company_id,company_user_id from fs_user
+        select user_id,qw_ext_id,sex,is_buy,`level`,course_ma_open_id,is_push,is_add_qw,source,login_device,is_individuation_push,store_open_id,password,jpush_id, is_vip,vip_start_date,vip_end_date,vip_level,vip_status,nick_name,integral_status, avatar, phone, integral,sign_num, status, tui_user_id, tui_time, tui_user_count, ma_open_id, mp_open_id, union_id, is_del, user_code, remark, create_time, update_time, last_ip, balance,is_weixin_auth,parent_id,qw_user_id,company_id,company_user_id from fs_user
     </sql>
 
     <select id="selectFsUserList" parameterType="FsUser" resultMap="FsUserResult">
@@ -557,6 +558,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="orderCount != null">order_count,</if>
             <if test="companyId != null">company_id,</if>
             <if test="companyUserId != null">company_user_id,</if>
+            <if test="level != null">`level`,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="nickName != null">#{nickName},</if>
@@ -600,6 +602,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="orderCount != null">#{orderCount},</if>
             <if test="companyId != null">#{companyId},</if>
             <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="level != null">#{level},</if>
          </trim>
     </insert>
 
@@ -891,6 +894,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         fs_user.user_id
     </select>
 
+    <select id="getUserTotal" resultType="java.lang.Integer">
+        SELECT count(fs_user.user_id) as userTotal
+        FROM fs_user
+        left join fs_user_company_user ucu on ucu.user_id = fs_user.user_id
+        LEFT JOIN company_user ON ucu.company_user_id = company_user.user_id
+        WHERE fs_user.is_del = 0
+        <if test="userId != null and userId != 0 ">
+            and (ucu.company_user_id = #{userId} OR company_user.parent_id = #{userId} )
+        </if>
+        <if test="companyId != null ">
+            and ucu.company_id = #{companyId}
+        </if>
+    </select>
+
+    <select id="getTodayNewUser" resultType="java.lang.Integer">
+        SELECT count(fs_user.user_id) as todayNewUser
+        FROM fs_user
+        left join fs_user_company_user ucu on ucu.user_id = fs_user.user_id
+        LEFT JOIN company_user ON ucu.company_user_id = company_user.user_id
+        WHERE fs_user.is_del = 0
+        <if test="userId != null and userId != 0 ">
+            and (ucu.company_user_id = #{userId} OR company_user.parent_id = #{userId} )
+        </if>
+        <if test="companyId != null ">
+            and ucu.company_id = #{companyId}
+        </if>
+        AND to_days(ucu.create_time) = to_days(now())
+    </select>
+
     <select id="countUserSummary" resultType="com.fs.store.vo.h5.FsUserSummaryCountVO">
         SELECT (SELECT count(fs_user.user_id)
         FROM fs_user
@@ -1206,6 +1238,106 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </select>
 
 
+    <!-- 查询观看和完成人数 -->
+    <select id="countUserWatchStats" resultType="Map">
+        SELECT
+        COUNT(DISTINCT CASE WHEN l.log_type != 3 AND l.send_type = 1 THEN l.user_id END) AS courseWatchNum,
+        COUNT(DISTINCT CASE WHEN l.log_type = 2 AND l.send_type = 1 THEN l.user_id END) AS courseCompleteNum
+        FROM  fs_course_watch_log l
+        LEFT JOIN fs_user u  ON u.user_id = l.user_id
+        LEFT JOIN company_user cu_l ON l.company_user_id = cu_l.user_id
+        <where>
+            <if test="userId != null and userId != 0">
+                AND (cu_l.user_id = #{userId} OR cu_l.parent_id = #{userId})
+            </if>
+            <if test="userId != null and userId == 0">
+                AND l.company_id = #{companyId}
+            </if>
+            <if test="periodId != null and periodId != ''">
+                AND l.period_id = #{periodId}
+            </if>
+            <if test="videoId != null and videoId != ''">
+                AND l.video_id = #{videoId}
+            </if>
+            <if test="startTime != null and startTime != ''">
+                AND l.create_time &gt;= #{startTime}
+            </if>
+            <if test="endTime != null and endTime != ''">
+                AND l.create_time &lt;= #{endTime}
+            </if>
+            <if test="companyUserId != null and companyUserId != ''">
+                AND l.company_user_id = #{companyUserId}
+            </if>
+        </where>
+    </select>
+
+    <!-- 查询答题人数和答对人数 -->
+    <select id="countUserAnswerStats" resultType="Map">
+        SELECT
+        COUNT(DISTINCT CASE WHEN a.user_id THEN a.user_id END) AS answerNum,
+        COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END) AS answerRightNum
+        FROM  fs_course_answer_logs a
+        LEFT JOIN fs_user u  ON u.user_id = a.user_id
+        LEFT JOIN company_user cu_a ON a.company_user_id = cu_a.user_id
+        <where>
+            <if test="userId != null and userId != 0">
+                AND (cu_a.user_id = #{userId} OR cu_a.parent_id = #{userId})
+            </if>
+            <if test="userId != null and userId == 0">
+                AND a.company_id = #{companyId}
+            </if>
+            <if test="periodId != null and periodId != ''">
+                AND a.period_id = #{periodId}
+            </if>
+            <if test="videoId != null and videoId != ''">
+                AND a.video_id = #{videoId}
+            </if>
+            <if test="startTime != null and startTime != ''">
+                AND a.create_time &gt;= #{startTime}
+            </if>
+            <if test="endTime != null and endTime != ''">
+                AND a.create_time &lt;= #{endTime}
+            </if>
+            <if test="companyUserId != null and companyUserId != ''">
+                AND a.company_user_id = #{companyUserId}
+            </if>
+        </where>
+    </select>
+
+    <!-- 查询红包数量和红包金额 -->
+    <select id="countUserRedPacketStats" resultType="Map">
+        SELECT
+        COUNT(CASE WHEN flog.status = 1 THEN flog.log_id END) AS redPacketNum,
+        IFNULL(SUM(CASE WHEN flog.status = 1 THEN flog.amount END), 0) AS redPacketAmount
+        FROM  fs_course_red_packet_log flog
+        LEFT JOIN fs_user u  ON u.user_id = flog.user_id
+        LEFT JOIN company_user cu_flog ON flog.company_user_id = cu_flog.user_id
+        <where>
+            <if test="userId != null and userId != 0">
+                AND (cu_flog.user_id = #{userId} OR cu_flog.parent_id = #{userId})
+            </if>
+            <if test="userId != null and userId == 0">
+                AND flog.company_id = #{companyId}
+            </if>
+            <if test="periodId != null and periodId != ''">
+                AND flog.period_id = #{periodId}
+            </if>
+            <if test="videoId != null and videoId != ''">
+                AND flog.video_id = #{videoId}
+            </if>
+            <if test="startTime != null and startTime != ''">
+                AND flog.create_time &gt;= #{startTime}
+            </if>
+            <if test="endTime != null and endTime != ''">
+                AND flog.create_time &lt;= #{endTime}
+            </if>
+            <if test="companyUserId != null and companyUserId != ''">
+                AND flog.company_user_id = #{companyUserId}
+            </if>
+        </where>
+    </select>
+
+
     <select id="countUserAnswer" resultType="Map">
         SELECT
         (

+ 14 - 0
fs-service/src/main/resources/mapper/qw/QwUserMapper.xml

@@ -289,5 +289,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             and qu.qw_user_name like concat('%', #{nickName}, '%')
         </if>
     </select>
+    <!--  递归查询子部门,不包括自己,深度5   -->
+    <select id="selectDeptByParentId" resultType="java.lang.Long">
+        WITH RECURSIVE sub_dept AS (
+        SELECT dept_id, parentid, 1 as depth
+        FROM qw_dept
+        WHERE parentid = #{deptId} and corp_id=#{corpId}
+        UNION ALL
+        SELECT qd.dept_id, qd.parentid, sd.depth + 1
+        FROM qw_dept qd
+        INNER JOIN sub_dept sd ON qd.parentid = sd.dept_id
+        WHERE sd.depth &lt; 5 and qd.corp_id=#{corpId}
+        )
+        SELECT dept_id FROM sub_dept
+    </select>
 
 </mapper>

+ 14 - 0
fs-user-app/src/main/java/com/fs/app/controller/CourseController.java

@@ -2,6 +2,7 @@ package com.fs.app.controller;
 
 
 import com.fs.app.annotation.Login;
+import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.ServletUtils;
@@ -17,6 +18,7 @@ import io.jsonwebtoken.Claims;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.Synchronized;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.transaction.annotation.Transactional;
@@ -26,6 +28,7 @@ import javax.servlet.http.HttpServletRequest;
 import java.util.*;
 
 @Api("课堂接口")
+@Slf4j
 @RestController
 @RequestMapping(value="/app/course")
 public class CourseController extends  AppBaseController{
@@ -341,4 +344,15 @@ public class CourseController extends  AppBaseController{
     public void updateUrl(){
         tencentCloudCosService.updateUrl();
     }
+
+    @Login
+    @ApiOperation("发放奖励App")
+    @PostMapping("/sendAppReward")
+    @RepeatSubmit
+    public R sendAppReward(@RequestBody FsCourseSendRewardUParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        log.info("zyp \n【发放APP奖励】:{}",param);
+        return courseVideoService.sendAppReward(param);
+    }
 }

+ 2 - 0
fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

@@ -95,6 +95,7 @@ public class CourseFsUserController extends AppBaseController {
 
     @ApiOperation("更新看课时长")
     @PostMapping("/updateWatchDuration")
+    @Login
     public R updateWatchDuration(@RequestBody FsUserCourseVideoUParam param)
     {
         param.setUserId(Long.parseLong(getUserId()));
@@ -104,6 +105,7 @@ public class CourseFsUserController extends AppBaseController {
 
     @ApiOperation("获取缓冲流量")
     @PostMapping("/getInternetTraffic")
+    @Login
     public R getInternetTraffic(@RequestBody FsUserCourseVideoFinishUParam param) {
         param.setUserId(Long.parseLong(getUserId()));
         return courseVideoService.getInternetTraffic(param);