13 次代码提交 a10e6ad9e9 ... eb78e3c665

作者 SHA1 备注 提交日期
  yjwang eb78e3c665 Merge remote-tracking branch 'origin/master' into ScrmStore 2 天之前
  yjwang a92b047baa 小程序H5授权登录、销售课程没有项目问题 2 天之前
  三七 0fbbfb8b2c AB 主备小程序 3 天之前
  吴树波 10168859bc ab小程序代码 3 天之前
  吴树波 02f5ceeb8d ab小程序代码 3 天之前
  jzp 89a513d37e 1.公开课只查询公域上架推荐未删除课程 3 天之前
  ct 88f7065231 Merge remote-tracking branch 'origin/master' 3 天之前
  ct bee574ed1e add:admin和相关角色看手机全号 3 天之前
  三七 69d0ba91d3 这些项目没有 SopREAD 读库 3 天之前
  zyp 0b7a340547 zyp 3 天之前
  zyp fd86a9ff52 Merge remote-tracking branch 'origin/master' 3 天之前
  zyp 572e33b133 zyp 3 天之前
  三七 ec22dfc84f 定时获取 通过调用 企业微信接口 发送的 SOP 客户群发消息 的反馈结果(新版) 3 天之前
共有 61 个文件被更改,包括 2342 次插入306 次删除
  1. 24 2
      fs-admin/src/main/java/com/fs/crm/controller/CrmCustomerController.java
  2. 51 1
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  3. 37 6
      fs-admin/src/main/java/com/fs/his/controller/FsUserController.java
  4. 3 1
      fs-admin/src/main/resources/application.yml
  5. 6 2
      fs-common/src/main/java/com/fs/common/annotation/Excel.java
  6. 14 2
      fs-common/src/main/java/com/fs/common/core/domain/entity/SysRole.java
  7. 63 0
      fs-common/src/main/java/com/fs/common/utils/ExcelUtils.java
  8. 66 45
      fs-common/src/main/java/com/fs/common/utils/poi/ExcelUtil.java
  9. 13 2
      fs-company/src/main/java/com/fs/company/controller/store/FsStoreOrderController.java
  10. 40 5
      fs-qw-task/src/main/java/com/fs/app/task/qwTask.java
  11. 10 0
      fs-qw-task/src/main/java/com/fs/app/taskService/QwExternalContactRatingMoreSevenDaysService.java
  12. 9 0
      fs-qw-task/src/main/java/com/fs/app/taskService/SopUserLogsInfoByIsDaysNotStudy.java
  13. 333 0
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/QwExternalContactRatingMoreSevenDaysServiceImpl.java
  14. 50 60
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  15. 262 0
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopUserLogsInfoByIsDaysNotStudyImpl.java
  16. 13 0
      fs-qw-task/src/main/java/com/fs/framework/config/ThreadPoolConfig.java
  17. 7 0
      fs-service/src/main/java/com/fs/company/domain/Company.java
  18. 41 0
      fs-service/src/main/java/com/fs/company/domain/CompanyMiniapp.java
  19. 62 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyMiniappMapper.java
  20. 70 0
      fs-service/src/main/java/com/fs/company/service/ICompanyMiniappService.java
  21. 141 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyMiniappServiceImpl.java
  22. 18 1
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  23. 3 0
      fs-service/src/main/java/com/fs/company/vo/CompanyVO.java
  24. 28 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  25. 1 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  26. 1 0
      fs-service/src/main/java/com/fs/his/enums/FsStoreOrderStatusEnum.java
  27. 1 1
      fs-service/src/main/java/com/fs/his/service/IFsExportTaskService.java
  28. 10 0
      fs-service/src/main/java/com/fs/his/service/IFsStoreOrderService.java
  29. 6 2
      fs-service/src/main/java/com/fs/his/service/impl/FsExportTaskServiceImpl.java
  30. 127 4
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java
  31. 32 0
      fs-service/src/main/java/com/fs/his/vo/FsStoreOrderStatusExcelVO.java
  32. 4 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  33. 6 0
      fs-service/src/main/java/com/fs/sop/domain/SopUserLogsInfo.java
  34. 1 1
      fs-service/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java
  35. 52 0
      fs-service/src/main/java/com/fs/sop/mapper/SopUserLogsInfoMapper.java
  36. 7 0
      fs-service/src/main/java/com/fs/sop/mapper/SopUserLogsMapper.java
  37. 1 0
      fs-service/src/main/java/com/fs/sop/params/QwRatingConfig.java
  38. 8 0
      fs-service/src/main/java/com/fs/sop/service/ISopUserLogsInfoService.java
  39. 3 0
      fs-service/src/main/java/com/fs/sop/service/ISopUserLogsService.java
  40. 318 77
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  41. 84 13
      fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  42. 5 0
      fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsServiceImpl.java
  43. 1 1
      fs-service/src/main/java/com/fs/sop/vo/QwRatingVO.java
  44. 16 13
      fs-service/src/main/java/com/fs/system/mapper/SysRoleMapper.java
  45. 23 21
      fs-service/src/main/java/com/fs/system/service/ISysRoleService.java
  46. 37 22
      fs-service/src/main/java/com/fs/system/service/impl/SysRoleServiceImpl.java
  47. 1 1
      fs-service/src/main/resources/application-common.yml
  48. 4 0
      fs-service/src/main/resources/application-config-druid-fby.yml
  49. 1 1
      fs-service/src/main/resources/application-config-druid-hcl.yml
  50. 1 1
      fs-service/src/main/resources/application-config-druid-sxjz.yml
  51. 1 0
      fs-service/src/main/resources/application-config-druid-xzt.yml
  52. 7 1
      fs-service/src/main/resources/application-druid-sxjz.yml
  53. 3 0
      fs-service/src/main/resources/application-druid-xzt.yml
  54. 91 0
      fs-service/src/main/resources/mapper/CompanyMiniappMapper.xml
  55. 12 0
      fs-service/src/main/resources/mapper/company/CompanyMapper.xml
  56. 24 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  57. 12 1
      fs-service/src/main/resources/mapper/sop/SopUserLogsInfoMapper.xml
  58. 41 0
      fs-service/src/main/resources/mapper/sop/SopUserLogsMapper.xml
  59. 31 18
      fs-service/src/main/resources/mapper/system/SysRoleMapper.xml
  60. 1 0
      fs-user-app/src/main/java/com/fs/app/controller/CourseController.java
  61. 4 0
      fs-user-app/src/main/java/com/fs/app/controller/course/CourseQwLoginController.java

+ 24 - 2
fs-admin/src/main/java/com/fs/crm/controller/CrmCustomerController.java

@@ -4,6 +4,8 @@ import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.entity.SysRole;
+import com.fs.common.core.domain.entity.SysUser;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
@@ -19,6 +21,7 @@ import com.fs.crm.service.ICrmCustomerService;
 import com.fs.crm.vo.CrmCustomerExportVO;
 import com.fs.crm.vo.CrmCustomerExportVO;
 import com.fs.crm.vo.CrmCustomerListVO;
 import com.fs.crm.vo.CrmCustomerListVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.framework.web.service.TokenService;
+import com.fs.system.service.ISysRoleService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -148,13 +151,14 @@ public class CrmCustomerController extends BaseController
         crmCustomer.setIsLine(0);
         crmCustomer.setIsLine(0);
         List<CrmCustomerListVO> list = crmCustomerService.selectCrmCustomerListVO(crmCustomer);
         List<CrmCustomerListVO> list = crmCustomerService.selectCrmCustomerListVO(crmCustomer);
         List<CrmCustomerExportVO> exportList=new ArrayList<>();
         List<CrmCustomerExportVO> exportList=new ArrayList<>();
+        boolean checkPhone = isCheckPhone();
         for(CrmCustomerListVO customer:list){
         for(CrmCustomerListVO customer:list){
             CrmCustomerExportVO vo=new CrmCustomerExportVO();
             CrmCustomerExportVO vo=new CrmCustomerExportVO();
             if(customer.getSource()!=null){
             if(customer.getSource()!=null){
                 vo.setSource(customer.getSource().toString());
                 vo.setSource(customer.getSource().toString());
             }
             }
             BeanUtils.copyProperties(customer,vo);
             BeanUtils.copyProperties(customer,vo);
-            if(customer.getMobile()!=null){
+            if(customer.getMobile()!=null && !checkPhone){
                 vo.setMobile(customer.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 vo.setMobile(customer.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
             }
             }
             exportList.add(vo);
             exportList.add(vo);
@@ -193,8 +197,9 @@ public class CrmCustomerController extends BaseController
         }
         }
         List<CrmCustomerListVO> list = crmCustomerService.selectCrmCustomerListQueryParam(crmCustomer);
         List<CrmCustomerListVO> list = crmCustomerService.selectCrmCustomerListQueryParam(crmCustomer);
         if (list != null) {
         if (list != null) {
+            boolean checkPhone = isCheckPhone();
             for (CrmCustomerListVO vo : list) {
             for (CrmCustomerListVO vo : list) {
-                if(vo.getMobile()!=null){
+                if(vo.getMobile()!=null && !checkPhone){
                     vo.setMobile(vo.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                     vo.setMobile(vo.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
                 }
 
 
@@ -204,6 +209,23 @@ public class CrmCustomerController extends BaseController
         return getDataTable(list);
         return getDataTable(list);
     }
     }
 
 
+    @Autowired
+    private ISysRoleService sysRoleService;
+    private boolean isCheckPhone() {
+        SysUser user = getLoginUser().getUser();
+        boolean flag = user.isAdmin();
+        if (flag) {
+            return true;
+        }
+        List<SysRole> roles = user.getRoles();
+        if (roles != null && !roles.isEmpty()) {
+            Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
+            return sysRoleService.getIsCheckPhone(roleIds);
+        }
+
+        return false;
+    }
+
 
 
     @PreAuthorize("@ss.hasPermi('crm:customer:query')")
     @PreAuthorize("@ss.hasPermi('crm:customer:query')")
     @GetMapping(value = "/query/{customerId}")
     @GetMapping(value = "/query/{customerId}")

+ 51 - 1
fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java

@@ -9,6 +9,8 @@ import java.util.stream.Collectors;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.entity.SysRole;
+import com.fs.common.core.domain.entity.SysUser;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.SecurityUtils;
@@ -43,6 +45,7 @@ import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.*;
 import com.fs.his.vo.*;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.mapper.SysConfigMapper;
+import com.fs.system.service.ISysRoleService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -213,13 +216,31 @@ public class FsStoreOrderController extends BaseController
         task.setUserId(SecurityUtils.getUserId());
         task.setUserId(SecurityUtils.getUserId());
         exportTaskService.insertFsExportTask(task);
         exportTaskService.insertFsExportTask(task);
         param.setTaskId(task.getTaskId());
         param.setTaskId(task.getTaskId());
-        exportTaskService.exportStore1Data(param);
+        boolean checkPhone = isCheckPhone();
+        exportTaskService.exportStore1Data(param,checkPhone);
 
 
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
 
 
 
 
     }
     }
 
 
+    @Autowired
+    private ISysRoleService sysRoleService;
+    private boolean isCheckPhone() {
+        SysUser user = getLoginUser().getUser();
+        boolean flag = user.isAdmin();
+        if (flag) {
+            return true;
+        }
+        List<SysRole> roles = user.getRoles();
+        if (roles != null && !roles.isEmpty()) {
+            Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
+            return sysRoleService.getIsCheckPhone(roleIds);
+        }
+
+        return false;
+    }
+
     @GetMapping("/importTemplate")
     @GetMapping("/importTemplate")
     public AjaxResult sendExport()
     public AjaxResult sendExport()
     {
     {
@@ -227,6 +248,13 @@ public class FsStoreOrderController extends BaseController
         return util.importTemplateExcel("导入运单号");
         return util.importTemplateExcel("导入运单号");
     }
     }
 
 
+    @GetMapping("/importUpdateOrderTemplate")
+    public AjaxResult importUpdateOrderTemplate()
+    {
+        ExcelUtil<FsStoreOrderStatusExcelVO> util = new ExcelUtil<>(FsStoreOrderStatusExcelVO.class);
+        return util.importTemplateExcel("修改订单");
+    }
+
     @Log(title = "导入", businessType = BusinessType.IMPORT)
     @Log(title = "导入", businessType = BusinessType.IMPORT)
     @PreAuthorize("@ss.hasPermi('store:storeOrder:importExpress')")
     @PreAuthorize("@ss.hasPermi('store:storeOrder:importExpress')")
     @PostMapping("/importExpress")
     @PostMapping("/importExpress")
@@ -248,6 +276,17 @@ public class FsStoreOrderController extends BaseController
         return AjaxResult.success(message);
         return AjaxResult.success(message);
     }
     }
 
 
+    @Log(title = "导入修改订单", businessType = BusinessType.IMPORT)
+    @PostMapping("/importOrderStatusData")
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:editImport')")
+    public AjaxResult importOrderStatusData(MultipartFile file, boolean updateSupport) throws Exception
+    {
+        ExcelUtil<FsStoreOrderStatusExcelVO> util = new ExcelUtil<>(FsStoreOrderStatusExcelVO.class);
+        List<FsStoreOrderStatusExcelVO> list = util.importExcel(file.getInputStream());
+        String message = fsStoreOrderService.importOrderStatusData(list);
+        return AjaxResult.success(message);
+    }
+
     @GetMapping("/importExpressTemplate")
     @GetMapping("/importExpressTemplate")
     public AjaxResult importExpressTemplate() {
     public AjaxResult importExpressTemplate() {
         ExcelUtil<StoreOrderExpressExportDTO> util = new ExcelUtil<StoreOrderExpressExportDTO>(StoreOrderExpressExportDTO.class);
         ExcelUtil<StoreOrderExpressExportDTO> util = new ExcelUtil<StoreOrderExpressExportDTO>(StoreOrderExpressExportDTO.class);
@@ -341,6 +380,17 @@ public class FsStoreOrderController extends BaseController
         return toAjax(fsStoreOrderService.updateFsStoreOrder(fsStoreOrder));
         return toAjax(fsStoreOrderService.updateFsStoreOrder(fsStoreOrder));
     }
     }
 
 
+    /**
+     * 修改订单
+     */
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:edit')")
+    @Log(title = "订单", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateStoreOrder")
+    public R updateStoreOrder(@RequestBody FsStoreOrder fsStoreOrder)
+    {
+        return fsStoreOrderService.updateStoreOrder(fsStoreOrder);
+    }
+
     @PreAuthorize("@ss.hasPermi('his:storeOrder:updateDelivery')")
     @PreAuthorize("@ss.hasPermi('his:storeOrder:updateDelivery')")
     @Log(title = "修改物流", businessType = BusinessType.UPDATE)
     @Log(title = "修改物流", businessType = BusinessType.UPDATE)
     @PutMapping("/updateDelivery")
     @PutMapping("/updateDelivery")

+ 37 - 6
fs-admin/src/main/java/com/fs/his/controller/FsUserController.java

@@ -7,6 +7,8 @@ import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.core.domain.entity.SysRole;
+import com.fs.common.core.domain.entity.SysUser;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.SecurityUtils;
@@ -28,6 +30,7 @@ import com.fs.his.vo.UserVo;
 import com.fs.qw.dto.UserProjectDTO;
 import com.fs.qw.dto.UserProjectDTO;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.vo.h5.FsUserPageListVO;
 import com.fs.store.vo.h5.FsUserPageListVO;
+import com.fs.system.service.ISysRoleService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Lists;
@@ -94,20 +97,43 @@ public class FsUserController extends BaseController
             fsUser.setPhone(encryptPhone(fsUser.getPhone()));
             fsUser.setPhone(encryptPhone(fsUser.getPhone()));
         }
         }
         List<FsUserVO> list = fsUserService.selectFsUserListVO(fsUser);
         List<FsUserVO> list = fsUserService.selectFsUserListVO(fsUser);
+        boolean checkPhone = isCheckPhone();
         for (FsUserVO fsUserVO : list) {
         for (FsUserVO fsUserVO : list) {
             if(fsUserVO.getPhone() != null&&fsUserVO.getPhone()!=""){
             if(fsUserVO.getPhone() != null&&fsUserVO.getPhone()!=""){
-                if (fsUserVO.getPhone().length()>11){
-                    fsUserVO.setPhone(decryptPhoneMk(fsUserVO.getPhone()));
-                }else {
-                    fsUserVO.setPhone(fsUserVO.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                if (!checkPhone){
+                    if (fsUserVO.getPhone().length()>11){
+                        fsUserVO.setPhone(decryptPhoneMk(fsUserVO.getPhone()));
+                    }else {
+                        fsUserVO.setPhone(fsUserVO.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                    }
+                } else {
+                    if (fsUserVO.getPhone().length()>11) {
+                        fsUserVO.setPhone(decryptPhone(fsUserVO.getPhone()));
+                    }
                 }
                 }
-
             }
             }
 
 
         }
         }
         return getDataTable(list);
         return getDataTable(list);
     }
     }
 
 
+    @Autowired
+    private ISysRoleService sysRoleService;
+    private boolean isCheckPhone() {
+        SysUser user = getLoginUser().getUser();
+        boolean flag = user.isAdmin();
+        if (flag) {
+            return true;
+        }
+        List<SysRole> roles = user.getRoles();
+        if (roles != null && !roles.isEmpty()) {
+            Long[] roleIds = roles.stream().map(SysRole::getRoleId).toArray(Long[]::new);
+            return sysRoleService.getIsCheckPhone(roleIds);
+        }
+
+        return false;
+    }
+
     @PreAuthorize("@ss.hasPermi('his:user:list')")
     @PreAuthorize("@ss.hasPermi('his:user:list')")
     @GetMapping("/listProject")
     @GetMapping("/listProject")
     public TableDataInfo listProject(FsUser fsUser)
     public TableDataInfo listProject(FsUser fsUser)
@@ -137,9 +163,14 @@ public class FsUserController extends BaseController
             return AjaxResult.error("导出数据不可超过1w条");
             return AjaxResult.error("导出数据不可超过1w条");
         }
         }
         List<FsUserExportListVO> list = fsUserService.selectFsUserExportListVO(fsUser);
         List<FsUserExportListVO> list = fsUserService.selectFsUserExportListVO(fsUser);
+        boolean checkPhone = isCheckPhone();
         for (FsUserExportListVO vo : list) {
         for (FsUserExportListVO vo : list) {
-            if (vo.getMobile()!=null){
+            if (vo.getMobile()!=null && !checkPhone){
                 vo.setMobile(vo.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 vo.setMobile(vo.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+            } else {
+                if (vo.getMobile().length()>11){
+                    vo.setMobile(decryptPhone(vo.getMobile()));
+                }
             }
             }
         }
         }
         ExcelUtil<FsUserExportListVO> util = new ExcelUtil<FsUserExportListVO>(FsUserExportListVO.class);
         ExcelUtil<FsUserExportListVO> util = new ExcelUtil<FsUserExportListVO>(FsUserExportListVO.class);

+ 3 - 1
fs-admin/src/main/resources/application.yml

@@ -4,9 +4,11 @@ server:
 # Spring配置
 # Spring配置
 spring:
 spring:
   profiles:
   profiles:
-    active: dev
+#    active: druid-myhk-test
 #    active: druid-hdt
 #    active: druid-hdt
 #    active: druid-yzt
 #    active: druid-yzt
 #    active: druid-sxjz
 #    active: druid-sxjz
 #    active: druid-sft
 #    active: druid-sft
+#    active: druid-fby
+    active: dev
 
 

+ 6 - 2
fs-common/src/main/java/com/fs/common/annotation/Excel.java

@@ -8,13 +8,17 @@ import java.math.BigDecimal;
 
 
 /**
 /**
  * 自定义导出Excel数据注解
  * 自定义导出Excel数据注解
- * 
+ *
 
 
  */
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
 @Target(ElementType.FIELD)
 public @interface Excel
 public @interface Excel
 {
 {
+    /**
+     * 是否为必填字段
+     */
+    boolean required() default false;
     /**
     /**
      * 导出时在excel中排序
      * 导出时在excel中排序
      */
      */
@@ -162,4 +166,4 @@ public @interface Excel
             return this.value;
             return this.value;
         }
         }
     }
     }
-}
+}

+ 14 - 2
fs-common/src/main/java/com/fs/common/core/domain/entity/SysRole.java

@@ -10,7 +10,7 @@ import com.fs.common.core.domain.BaseEntity;
 
 
 /**
 /**
  * 角色表 sys_role
  * 角色表 sys_role
- * 
+ *
 
 
  */
  */
 public class SysRole extends BaseEntity
 public class SysRole extends BaseEntity
@@ -59,6 +59,9 @@ public class SysRole extends BaseEntity
     /** 部门组(数据权限) */
     /** 部门组(数据权限) */
     private Long[] deptIds;
     private Long[] deptIds;
 
 
+    /** 是否可以查看手机全号 0否 1是 */
+    private Integer isCheckPhone;
+
     public SysRole()
     public SysRole()
     {
     {
 
 
@@ -203,7 +206,15 @@ public class SysRole extends BaseEntity
     {
     {
         this.deptIds = deptIds;
         this.deptIds = deptIds;
     }
     }
-    
+
+    public Integer getIsCheckPhone() {
+        return isCheckPhone;
+    }
+
+    public void setIsCheckPhone(Integer isCheckPhone) {
+        this.isCheckPhone = isCheckPhone;
+    }
+
     @Override
     @Override
     public String toString() {
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -221,6 +232,7 @@ public class SysRole extends BaseEntity
             .append("updateBy", getUpdateBy())
             .append("updateBy", getUpdateBy())
             .append("updateTime", getUpdateTime())
             .append("updateTime", getUpdateTime())
             .append("remark", getRemark())
             .append("remark", getRemark())
+            .append("isCheckPhone", getIsCheckPhone())
             .toString();
             .toString();
     }
     }
 }
 }

+ 63 - 0
fs-common/src/main/java/com/fs/common/utils/ExcelUtils.java

@@ -0,0 +1,63 @@
+package com.fs.common.utils;
+
+import com.fs.common.annotation.Excel;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * excel工具类
+ */
+public class ExcelUtils {
+    private static final Map<Class<?>, List<Field>> fieldCache = new ConcurrentHashMap<>();
+
+    public static void validateRequiredFields(Object obj, int rowIndex) throws Exception {
+        List<String> errorMessages = new ArrayList<>();
+        Class<?> clazz = obj.getClass();
+
+        // 从缓存中获取字段
+        List<Field> fields = getCachedFields(clazz);
+
+        for (Field field : fields) {
+            if (field.isAnnotationPresent(Excel.class)) {
+                Excel excel = field.getAnnotation(Excel.class);
+                if (excel.required()) {
+                    field.setAccessible(true);
+                    Object value = field.get(obj);
+                    if (isEmpty(value)) {
+                        errorMessages.add("第 " + rowIndex + " 行的 " + excel.name() + " 是必填项,不能为空!");
+                    }
+                }
+            }
+        }
+
+        if (!errorMessages.isEmpty()) {
+            throw new Exception(String.join("; ", errorMessages));
+        }
+    }
+
+    private static List<Field> getCachedFields(Class<?> clazz) {
+        return fieldCache.computeIfAbsent(clazz, k -> {
+            Field[] fields = k.getDeclaredFields();
+            return Arrays.asList(fields);
+        });
+    }
+
+    private static boolean isEmpty(Object value) {
+        if (value == null) {
+            return true;
+        }
+        if (value instanceof String) {
+            return ((String) value).trim().isEmpty();
+        }
+        if (value instanceof Iterable) {
+            return !((Iterable<?>) value).iterator().hasNext();
+        }
+        return false;
+    }
+
+}

+ 66 - 45
fs-common/src/main/java/com/fs/common/utils/poi/ExcelUtil.java

@@ -26,26 +26,7 @@ import org.apache.poi.hssf.usermodel.HSSFShape;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.ooxml.POIXMLDocumentPart;
 import org.apache.poi.ooxml.POIXMLDocumentPart;
-import org.apache.poi.ss.usermodel.BorderStyle;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.CellStyle;
-import org.apache.poi.ss.usermodel.CellType;
-import org.apache.poi.ss.usermodel.ClientAnchor;
-import org.apache.poi.ss.usermodel.DataValidation;
-import org.apache.poi.ss.usermodel.DataValidationConstraint;
-import org.apache.poi.ss.usermodel.DataValidationHelper;
-import org.apache.poi.ss.usermodel.DateUtil;
-import org.apache.poi.ss.usermodel.Drawing;
-import org.apache.poi.ss.usermodel.FillPatternType;
-import org.apache.poi.ss.usermodel.Font;
-import org.apache.poi.ss.usermodel.HorizontalAlignment;
-import org.apache.poi.ss.usermodel.IndexedColors;
-import org.apache.poi.ss.usermodel.PictureData;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.VerticalAlignment;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.ss.util.CellRangeAddressList;
 import org.apache.poi.ss.util.CellRangeAddressList;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.xssf.streaming.SXSSFWorkbook;
 import org.apache.poi.xssf.streaming.SXSSFWorkbook;
@@ -77,7 +58,7 @@ import com.fs.common.utils.reflect.ReflectUtils;
 
 
 /**
 /**
  * Excel相关处理
  * Excel相关处理
- * 
+ *
 
 
  */
  */
 public class ExcelUtil<T>
 public class ExcelUtil<T>
@@ -164,7 +145,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 对excel表单默认第一个索引名转换成list
      * 对excel表单默认第一个索引名转换成list
-     * 
+     *
      * @param is 输入流
      * @param is 输入流
      * @return 转换后集合
      * @return 转换后集合
      */
      */
@@ -175,7 +156,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 对excel表单指定表格索引名转换成list
      * 对excel表单指定表格索引名转换成list
-     * 
+     *
      * @param sheetName 表格索引名
      * @param sheetName 表格索引名
      * @param is 输入流
      * @param is 输入流
      * @return 转换后集合
      * @return 转换后集合
@@ -354,7 +335,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 对list数据源将其里面的数据导入到excel表单
      * 对list数据源将其里面的数据导入到excel表单
-     * 
+     *
      * @param list 导出数据集合
      * @param list 导出数据集合
      * @param sheetName 工作表的名称
      * @param sheetName 工作表的名称
      * @return 结果
      * @return 结果
@@ -367,7 +348,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 对list数据源将其里面的数据导入到excel表单
      * 对list数据源将其里面的数据导入到excel表单
-     * 
+     *
      * @param response 返回数据
      * @param response 返回数据
      * @param list 导出数据集合
      * @param list 导出数据集合
      * @param sheetName 工作表的名称
      * @param sheetName 工作表的名称
@@ -384,7 +365,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 对list数据源将其里面的数据导入到excel表单
      * 对list数据源将其里面的数据导入到excel表单
-     * 
+     *
      * @param sheetName 工作表的名称
      * @param sheetName 工作表的名称
      * @return 结果
      * @return 结果
      */
      */
@@ -396,7 +377,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 对list数据源将其里面的数据导入到excel表单
      * 对list数据源将其里面的数据导入到excel表单
-     * 
+     *
      * @param sheetName 工作表的名称
      * @param sheetName 工作表的名称
      * @return 结果
      * @return 结果
      */
      */
@@ -410,7 +391,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 对list数据源将其里面的数据导入到excel表单
      * 对list数据源将其里面的数据导入到excel表单
-     * 
+     *
      * @return 结果
      * @return 结果
      */
      */
     public void exportExcel(OutputStream out)
     public void exportExcel(OutputStream out)
@@ -433,7 +414,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 对list数据源将其里面的数据导入到excel表单
      * 对list数据源将其里面的数据导入到excel表单
-     * 
+     *
      * @return 结果
      * @return 结果
      */
      */
     public AjaxResult exportExcel()
     public AjaxResult exportExcel()
@@ -489,7 +470,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 填充excel数据
      * 填充excel数据
-     * 
+     *
      * @param index 序号
      * @param index 序号
      * @param row 单元格行
      * @param row 单元格行
      */
      */
@@ -516,7 +497,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 创建表格样式
      * 创建表格样式
-     * 
+     *
      * @param wb 工作薄对象
      * @param wb 工作薄对象
      * @return 样式列表
      * @return 样式列表
      */
      */
@@ -591,14 +572,54 @@ public class ExcelUtil<T>
         Cell cell = row.createCell(column);
         Cell cell = row.createCell(column);
         // 写入列信息
         // 写入列信息
         cell.setCellValue(attr.name());
         cell.setCellValue(attr.name());
+//        setDataValidation(attr, row, column);
+//        cell.setCellStyle(styles.get("header"));
+        // 获取默认样式
+        CellStyle cellStyle = styles.get("header");
+        // 如果是必填项,应用红色字体样式
+        if (attr.required()) {
+//            CellStyle requiredStyle = row.getSheet().getWorkbook().createCellStyle();
+//            requiredStyle.cloneStyleFrom(cellStyle); // 复制原样式
+//            Font font = row.getSheet().getWorkbook().createFont();
+//            font.setColor(IndexedColors.RED.getIndex()); // 设置字体红色
+//            requiredStyle.setFont(font);
+//            cellStyle = requiredStyle;
+
+            // 添加 Excel 注释,提示用户该字段必填
+            addCellComment(cell, row.getSheet(), "必填项");
+        }
+        // 应用单元格样式
+        cell.setCellStyle(cellStyle);
+        // 设置数据校验规则(如果有)
         setDataValidation(attr, row, column);
         setDataValidation(attr, row, column);
-        cell.setCellStyle(styles.get("header"));
         return cell;
         return cell;
     }
     }
 
 
+    /**
+     * 给 Excel 单元格添加注释
+     */
+    private void addCellComment(Cell cell, Sheet sheet, String commentText) {
+        if (cell.getCellComment() != null) {
+            return; // 如果已存在注释,直接返回,避免重复添加
+        }
+        Drawing<?> drawing = sheet.createDrawingPatriarch();
+        CreationHelper factory = sheet.getWorkbook().getCreationHelper();
+
+        // 创建注释框
+        ClientAnchor anchor = factory.createClientAnchor();
+        // 确保 anchor 对象配置正确
+        anchor.setCol1(cell.getColumnIndex());  // 设置注释的列
+        anchor.setRow1(cell.getRowIndex());    // 设置注释的行
+        Comment comment = drawing.createCellComment(anchor);
+        comment.setString(factory.createRichTextString(commentText));
+
+        // 绑定注释到单元格
+        cell.setCellComment(comment);
+    }
+
     /**
     /**
      * 设置单元格信息
      * 设置单元格信息
-     * 
+     *
      * @param value 单元格值
      * @param value 单元格值
      * @param attr 注解相关
      * @param attr 注解相关
      * @param cell 单元格信息
      * @param cell 单元格信息
@@ -743,7 +764,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 设置 POI XSSFSheet 单元格提示
      * 设置 POI XSSFSheet 单元格提示
-     * 
+     *
      * @param sheet 表单
      * @param sheet 表单
      * @param promptTitle 提示标题
      * @param promptTitle 提示标题
      * @param promptContent 提示内容
      * @param promptContent 提示内容
@@ -766,7 +787,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 设置某些列的值只能输入预制的数据,显示下拉框.
      * 设置某些列的值只能输入预制的数据,显示下拉框.
-     * 
+     *
      * @param sheet 要设置的sheet.
      * @param sheet 要设置的sheet.
      * @param textlist 下拉框显示的内容
      * @param textlist 下拉框显示的内容
      * @param firstRow 开始行
      * @param firstRow 开始行
@@ -800,7 +821,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 解析导出值 0=男,1=女,2=未知
      * 解析导出值 0=男,1=女,2=未知
-     * 
+     *
      * @param propertyValue 参数值
      * @param propertyValue 参数值
      * @param converterExp 翻译注解
      * @param converterExp 翻译注解
      * @param separator 分隔符
      * @param separator 分隔符
@@ -837,7 +858,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 反向解析值 男=0,女=1,未知=2
      * 反向解析值 男=0,女=1,未知=2
-     * 
+     *
      * @param propertyValue 参数值
      * @param propertyValue 参数值
      * @param converterExp 翻译注解
      * @param converterExp 翻译注解
      * @param separator 分隔符
      * @param separator 分隔符
@@ -874,7 +895,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 解析字典值
      * 解析字典值
-     * 
+     *
      * @param dictValue 字典值
      * @param dictValue 字典值
      * @param dictType 字典类型
      * @param dictType 字典类型
      * @param separator 分隔符
      * @param separator 分隔符
@@ -887,7 +908,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 反向解析值字典值
      * 反向解析值字典值
-     * 
+     *
      * @param dictLabel 字典标签
      * @param dictLabel 字典标签
      * @param dictType 字典类型
      * @param dictType 字典类型
      * @param separator 分隔符
      * @param separator 分隔符
@@ -955,7 +976,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 获取下载路径
      * 获取下载路径
-     * 
+     *
      * @param filename 文件名称
      * @param filename 文件名称
      */
      */
     public String getAbsoluteFile(String filename)
     public String getAbsoluteFile(String filename)
@@ -971,7 +992,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 获取bean中的属性值
      * 获取bean中的属性值
-     * 
+     *
      * @param vo 实体对象
      * @param vo 实体对象
      * @param field 字段
      * @param field 字段
      * @param excel 注解
      * @param excel 注解
@@ -1002,7 +1023,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 以类的属性的get方法方法形式获取值
      * 以类的属性的get方法方法形式获取值
-     * 
+     *
      * @param o
      * @param o
      * @param name
      * @param name
      * @return value
      * @return value
@@ -1087,7 +1108,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 创建工作表
      * 创建工作表
-     * 
+     *
      * @param sheetNo sheet数量
      * @param sheetNo sheet数量
      * @param index 序号
      * @param index 序号
      */
      */
@@ -1108,7 +1129,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 获取单元格值
      * 获取单元格值
-     * 
+     *
      * @param row 获取的行
      * @param row 获取的行
      * @param column 获取单元格列号
      * @param column 获取单元格列号
      * @return 单元格值
      * @return 单元格值
@@ -1168,7 +1189,7 @@ public class ExcelUtil<T>
 
 
     /**
     /**
      * 判断是否是空行
      * 判断是否是空行
-     * 
+     *
      * @param row 判断的行
      * @param row 判断的行
      * @return
      * @return
      */
      */

+ 13 - 2
fs-company/src/main/java/com/fs/company/controller/store/FsStoreOrderController.java

@@ -130,7 +130,7 @@ public class FsStoreOrderController extends BaseController
         task.setCompanyUserId(userId);
         task.setCompanyUserId(userId);
         exportTaskService.insertFsExportTask(task);
         exportTaskService.insertFsExportTask(task);
         param.setTaskId(task.getTaskId());
         param.setTaskId(task.getTaskId());
-        exportTaskService.exportStore1Data(param);
+        exportTaskService.exportStore1Data(param,false);
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
 
 
 
 
@@ -172,7 +172,7 @@ public class FsStoreOrderController extends BaseController
         task.setCompanyUserId(userId);
         task.setCompanyUserId(userId);
         exportTaskService.insertFsExportTask(task);
         exportTaskService.insertFsExportTask(task);
         param.setTaskId(task.getTaskId());
         param.setTaskId(task.getTaskId());
-        exportTaskService.exportStore1Data(param);
+        exportTaskService.exportStore1Data(param,false);
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
     }
     }
     /**
     /**
@@ -298,6 +298,17 @@ public class FsStoreOrderController extends BaseController
         return toAjax(fsStoreOrderService.updateFsStoreOrder(fsStoreOrder));
         return toAjax(fsStoreOrderService.updateFsStoreOrder(fsStoreOrder));
     }
     }
 
 
+    /**
+     * 修改订单
+     */
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:edit')")
+    @Log(title = "订单", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateStoreOrder")
+    public R updateStoreOrder(@RequestBody FsStoreOrder fsStoreOrder)
+    {
+        return fsStoreOrderService.updateStoreOrder(fsStoreOrder);
+    }
+
     /**
     /**
      * 删除订单
      * 删除订单
      */
      */

+ 40 - 5
fs-qw-task/src/main/java/com/fs/app/task/qwTask.java

@@ -1,9 +1,6 @@
 package com.fs.app.task;
 package com.fs.app.task;
 
 
-import com.fs.app.taskService.QwExternalContactRatingService;
-import com.fs.app.taskService.SopLogsChatTaskService;
-import com.fs.app.taskService.SopLogsTaskService;
-import com.fs.app.taskService.SopWxLogsService;
+import com.fs.app.taskService.*;
 import com.fs.qw.service.IQwExternalErrRetryService;
 import com.fs.qw.service.IQwExternalErrRetryService;
 import com.fs.qw.service.IQwGroupMsgService;
 import com.fs.qw.service.IQwGroupMsgService;
 import com.fs.qw.service.IQwWorkUserService;
 import com.fs.qw.service.IQwWorkUserService;
@@ -75,6 +72,12 @@ public class qwTask {
     @Autowired
     @Autowired
     private IQwSopTagService qwSopTagService;
     private IQwSopTagService qwSopTagService;
 
 
+    @Autowired
+    private SopUserLogsInfoByIsDaysNotStudy logsInfoByIsDaysNotStudy;
+
+    @Autowired
+    private QwExternalContactRatingMoreSevenDaysService qwExternalContactRatingMoreSevenDaysService;
+
     /**
     /**
      * 定时任务:检查SOP规则时间
      * 定时任务:检查SOP规则时间
      * 执行时间:每天凌晨 1:10:00
      * 执行时间:每天凌晨 1:10:00
@@ -182,7 +185,7 @@ public class qwTask {
     }
     }
 
 
     /**
     /**
-     * 定时任务:获取企业微信SOP群发消息反馈结果(新版-按营期发送)
+     *
      * 执行时间:每天上午 8:00:00
      * 执行时间:每天上午 8:00:00
      * 功能:获取通过企业微信接口发送的SOP客户群发消息的反馈结果
      * 功能:获取通过企业微信接口发送的SOP客户群发消息的反馈结果
      */
      */
@@ -306,6 +309,22 @@ public class qwTask {
         sopUserLogsService.repairSopUserLogsTimer();
         sopUserLogsService.repairSopUserLogsTimer();
     }
     }
 
 
+
+    /**
+     * 凌晨 2点35开始,将营期小于7天中标记为 是否7天未看课的(E级) 客户的 但是看课了的恢复一下
+     */
+    @Scheduled(cron = "0 35 2 * * ?")
+    @Async
+    public void processSopUserLogsInfoByIsDaysNotStudy() {
+        long startTimeMillis = System.currentTimeMillis();
+        log.info("====== 开始选择和处理 是否7天未看课的(E级) 客户的 恢复一下 ======");
+
+        logsInfoByIsDaysNotStudy.restoreByIsDaysNotStudy();
+
+        long endTimeMillis = System.currentTimeMillis();
+        log.info("====== 用户E级恢复处理完成,耗时 {} 毫秒 ======", (endTimeMillis - startTimeMillis));
+    }
+
     /**
     /**
      * 定时任务:客户评级处理
      * 定时任务:客户评级处理
      * 执行时间:每天凌晨 3:45:00
      * 执行时间:每天凌晨 3:45:00
@@ -327,6 +346,22 @@ public class qwTask {
         log.info("====== sop营期-用户分级处理完成,耗时 {} 毫秒 ======", (endTimeMillis - startTimeMillis));
         log.info("====== sop营期-用户分级处理完成,耗时 {} 毫秒 ======", (endTimeMillis - startTimeMillis));
     }
     }
 
 
+    /**
+     * 凌晨4点35开始 客户超过7天没有看课的 标记E级
+     */
+    @Scheduled(cron = "0 30 3 * * ?")
+    @Async
+    public void processQwSopExternalContactRatingMoreSevenDaysTimer() {
+        long startTimeMillis = System.currentTimeMillis();
+        log.info("====== 开始选择和处理 sop营期-用户超7天的看课情况 ======");
+
+        qwExternalContactRatingMoreSevenDaysService.ratingMoreSevenDaysUserLogs();
+
+        long endTimeMillis = System.currentTimeMillis();
+        log.info("====== sop营期-用户超7天处理完成,耗时 {} 毫秒 ======", (endTimeMillis - startTimeMillis));
+    }
+
+
     /**
     /**
      * 更新掉所有前一天的所有待发送
      * 更新掉所有前一天的所有待发送
      */
      */

+ 10 - 0
fs-qw-task/src/main/java/com/fs/app/taskService/QwExternalContactRatingMoreSevenDaysService.java

@@ -0,0 +1,10 @@
+package com.fs.app.taskService;
+
+import com.fs.common.core.domain.R;
+
+public interface QwExternalContactRatingMoreSevenDaysService {
+    /**
+     * Sop客户超7天评次
+     */
+    public R ratingMoreSevenDaysUserLogs();
+}

+ 9 - 0
fs-qw-task/src/main/java/com/fs/app/taskService/SopUserLogsInfoByIsDaysNotStudy.java

@@ -0,0 +1,9 @@
+package com.fs.app.taskService;
+
+public interface SopUserLogsInfoByIsDaysNotStudy {
+
+    /**
+     * 将前7天营期中标记为 是否7天未看课的(E级) 客户的 恢复一下,突然有的恢复一下 (复刻版)
+     */
+    public void restoreByIsDaysNotStudy();
+}

+ 333 - 0
fs-qw-task/src/main/java/com/fs/app/taskService/impl/QwExternalContactRatingMoreSevenDaysServiceImpl.java

@@ -0,0 +1,333 @@
+package com.fs.app.taskService.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.app.taskService.QwExternalContactRatingMoreSevenDaysService;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.sop.domain.SopUserLogs;
+import com.fs.sop.domain.SopUserLogsInfo;
+import com.fs.sop.mapper.SopUserLogsInfoMapper;
+import com.fs.sop.mapper.SopUserLogsMapper;
+import com.fs.sop.params.QwRatingConfig;
+import com.fs.sop.service.IQwSopTempDayService;
+import com.fs.sop.service.ISopUserLogsInfoService;
+import com.fs.sop.vo.QwRatingVO;
+import com.fs.system.service.ISysConfigService;
+import com.fs.voice.utils.StringUtil;
+import com.google.common.util.concurrent.AtomicDouble;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.*;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+public class QwExternalContactRatingMoreSevenDaysServiceImpl implements QwExternalContactRatingMoreSevenDaysService {
+
+
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    private SopUserLogsMapper sopUserLogsMapper;
+
+    @Autowired
+    private IQwSopTempDayService qwSopTempDayService;
+
+    @Autowired
+    private SopUserLogsInfoMapper sopUserLogsInfoMapper;
+
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+
+    @Autowired
+    private QwExternalContactMapper qwExternalContactMapper;
+
+    @Autowired
+    private ISopUserLogsInfoService iSopUserLogsInfoService;
+
+    @Autowired
+    private ExecutorService sopRatingExecutor;  // 自定义线程池
+
+    // 任务队列
+    private final BlockingQueue<SopUserLogs> taskQueue = new LinkedBlockingQueue<>(10000);
+
+    private volatile boolean running = true;
+    //批量更新队列
+    private final List<CompletableFuture<Void>> updateFutures = Collections.synchronizedList(new ArrayList<>());
+
+    private final Object configLock = new Object();
+
+    // 启动时初始化消费者线程
+    @PostConstruct
+    public void init() {
+
+        loadCourseConfig();
+
+        int consumerCount = Runtime.getRuntime().availableProcessors(); // 消费者线程数,默认 CPU 核心数
+        for (int i = 0; i < consumerCount; i++) {
+            sopRatingExecutor.submit(this::consumeTasks); // 提交消费者任务
+        }
+
+        log.info("初始化 {} 个消费者线程", consumerCount);
+    }
+
+    private  volatile QwRatingConfig qwRatingConfig;
+
+    private void loadCourseConfig() {
+        try {
+            String json = configService.selectConfigByKey("qwRating:config");
+            QwRatingConfig config = JSON.parseObject(json, QwRatingConfig.class);
+            if (!StringUtil.strIsNullOrEmpty(json) && config != null) {
+                qwRatingConfig = config;
+                log.info("Loaded qwRating.config successfully.");
+            } else {
+                log.error("Failed to load course.config from configService.");
+            }
+        } catch (Exception e) {
+            log.error("Exception while loading qwRating.config: {}", e.getMessage(), e);
+        }
+    }
+
+
+    @Override
+    public R ratingMoreSevenDaysUserLogs() {
+        // 分页加载并放入队列
+        int pageSize = 1000;
+        int offset = 0;
+        List<SopUserLogs> sopUserLogs;
+
+        // 获取缓存的配置
+        QwRatingConfig config;
+        synchronized(configLock) {
+            config = qwRatingConfig;
+        }
+
+        do {
+            sopUserLogs = sopUserLogsMapper.meetsTheRatingByUserInfoWithPaginationStudyDays(offset, pageSize,config.getNotStudyDays());
+            if (!sopUserLogs.isEmpty()) {
+                sopUserLogs.forEach(item -> {
+                    try {
+                        taskQueue.put(item); // 将任务放入队列
+                    } catch (InterruptedException e) {
+                        log.error("任务放入队列失败,sopId: {}", item.getSopId(), e);
+                        Thread.currentThread().interrupt();
+                    }
+                });
+                offset += pageSize;
+            }
+        } while (!sopUserLogs.isEmpty());
+
+
+        // 等待队列处理完成
+        CompletableFuture.runAsync(() -> {
+            while (!taskQueue.isEmpty()) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log.error("等待队列处理时中断", e);
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }).join(); // 等待任务完成
+
+        return R.ok();
+    }
+
+
+    private void consumeTasks() {
+
+        if (!running && taskQueue.isEmpty()) {
+            log.info("没有评级任务需要处理");
+            return; // 如果队列为空且没有正在运行的线程,则直接返回
+        }
+
+        while (running) {
+            try {
+                SopUserLogs item = taskQueue.poll(1, TimeUnit.SECONDS); // 等待 1 秒
+                if (item != null) {
+                    processSingleTask(item);
+                }
+            } catch (Exception e) {
+                log.error("消费者线程异常", e);
+            }
+        }
+    }
+
+    private void processSingleTask(SopUserLogs item) {
+
+        // 获取缓存的配置
+        QwRatingConfig config;
+        synchronized(configLock) {
+            config = qwRatingConfig;
+        }
+
+        List<SopUserLogsInfo> sopUserLogsInfosList = sopUserLogsInfoMapper
+                .selectSopUserLogsInfoListBySopId(item.getSopId(), item.getId());
+
+        if (sopUserLogsInfosList == null || sopUserLogsInfosList.isEmpty()) {
+            log.error("当前营期没有客户-sopId:{},营期id:{}", item.getSopId(), item.getId());
+            return;
+        }
+
+        List<QwExternalContact> batchQwExternalContact = sopUserLogsInfosList.stream()
+                .map(logsInfo -> processUserLog(logsInfo, config))
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (!batchQwExternalContact.isEmpty()) {
+            batchUpdateQwExternalContact(batchQwExternalContact);
+        }
+    }
+
+    private QwExternalContact processUserLog(SopUserLogsInfo logsInfo, QwRatingConfig config) {
+        try {
+            Long externalId = logsInfo.getExternalId();
+            if (externalId == null) {
+                return null;
+            }
+
+            List<QwRatingVO> ratingVOS = fsCourseWatchLogMapper
+                    .selectFsCourseWatchLogByExtIdRatingMoreStudyDays(externalId, config.getNotStudyDays());
+
+            if (ratingVOS == null || ratingVOS.isEmpty() || ratingVOS.size() < 6) {
+                log.info("没有记录或不满足条件不评级或看课记录小于6 不评级,externalId: {}", externalId);
+                return null;
+            }
+
+
+            //判断 7天的时长是否大于0
+            boolean scoreMoreStudyLevel = getScoreMoreStudyLevel(ratingVOS);
+
+            if (!scoreMoreStudyLevel) {
+                QwExternalContact externalContact = new QwExternalContact();
+                externalContact.setId(externalId);
+                externalContact.setLevel(5);
+                externalContact.setIsDaysNotStudy(1);
+                return externalContact;
+            }else {
+                QwExternalContact externalContact = new QwExternalContact();
+                externalContact.setId(externalId);
+                externalContact.setLevel(ratingVOS.get(0).getLevel());
+                externalContact.setIsDaysNotStudy(0);
+                return externalContact;
+            }
+
+
+        } catch (Exception e) {
+            log.error("计算用户积分异常,用户:{}", logsInfo, e);
+            return null;
+        }
+    }
+
+    private void batchUpdateQwExternalContact(List<QwExternalContact> notInExternalUseridList) {
+        int batchSize = 300;
+
+        for (int i = 0; i < notInExternalUseridList.size(); i += batchSize) {
+            int endIndex = Math.min(i + batchSize, notInExternalUseridList.size());
+            List<QwExternalContact> batchList = notInExternalUseridList.subList(i, endIndex);
+
+            int finalI = i;
+            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                try {
+                    qwExternalContactMapper.batchUpdateQwExternalContactByMoreStudy(batchList);
+                    iSopUserLogsInfoService.batchUpdateSopUserLogsInfoByMoreStudy(batchList);
+                    log.info("成功更新看课7天数据,起始索引: {}, 数量: {}", finalI, batchList.size());
+                } catch (Exception e) {
+                    log.error("批量更新异常,批次起始索引: {}", finalI, e);
+                }
+
+            }, sopRatingExecutor);
+
+            updateFutures.add(future);
+        }
+    }
+
+    @PreDestroy
+    public void shutdown() {
+        running = false;  // 标记消费者停止
+        log.info("正在关闭线程池...");
+
+        // **等待任务队列处理完毕**
+        while (!taskQueue.isEmpty()) {
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                log.warn("等待任务队列处理完成时被中断", e);
+            }
+        }
+
+        // **确保所有 `batchUpdateQwExternalContact` 的任务完成**
+        log.info("等待所有批量更新任务完成...");
+        CompletableFuture.allOf(updateFutures.toArray(new CompletableFuture[0])).join();
+
+        // 关闭线程池
+        sopRatingExecutor.shutdown();
+        try {
+            if (!sopRatingExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
+                List<Runnable> pendingTasks = sopRatingExecutor.shutdownNow();
+                log.warn("强制关闭线程池,未完成任务数: {}", pendingTasks.size());
+            }
+        } catch (InterruptedException e) {
+            sopRatingExecutor.shutdownNow();
+            Thread.currentThread().interrupt();
+        }
+        log.info("线程池和消费者已完全关闭");
+    }
+
+
+    /**
+     * 每6小时更新一次
+     */
+    @Scheduled(cron = "0 50 0/6 * * ?")
+    public void refreshRatingConfig() {
+
+        synchronized(configLock) {
+            try {
+                String json = configService.selectConfigByKey("qwRating:config");
+                QwRatingConfig config = JSON.parseObject(json, QwRatingConfig.class);
+                if (!StringUtil.strIsNullOrEmpty(json) && config != null) {
+                    qwRatingConfig = config;
+                    log.info("LoadedTime qwRating.config successfully.");
+                } else {
+                    log.error("Failed to load course.config from configService.");
+                }
+            } catch (Exception e) {
+                log.error("Exception while refreshing course.config: {}", e.getMessage(), e);
+            }
+        }
+
+    }
+
+
+    //查 E级
+    public boolean getScoreMoreStudyLevel(List<QwRatingVO> qwRatingVOS) {
+
+        AtomicDouble watchCount= new AtomicDouble();
+
+        qwRatingVOS.forEach(vo -> {
+            watchCount.addAndGet(vo.getWatchDuration());
+        });
+
+        // 判断总 watchDuration 是否 > 0
+        return watchCount.get() > 0;
+    }
+
+}

+ 50 - 60
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -8,7 +8,9 @@ import com.fs.common.core.domain.R;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.PubFun;
 import com.fs.common.utils.PubFun;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyMiniapp;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyMiniappService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.*;
 import com.fs.course.domain.*;
@@ -150,7 +152,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private IQwGroupChatService qwGroupChatService;
     private IQwGroupChatService qwGroupChatService;
     @Autowired
     @Autowired
     private IQwGroupChatUserService qwGroupChatUserService;
     private IQwGroupChatUserService qwGroupChatUserService;
-
+    @Autowired
+    private ICompanyMiniappService companyMiniappService;
     // Shutdown flags
     // Shutdown flags
     private volatile boolean running = true;
     private volatile boolean running = true;
     @Autowired
     @Autowired
@@ -304,44 +307,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         Map<String, List<SopUserLogsVo>> sopLogsGroupedById = sopUserLogsVos.stream()
         Map<String, List<SopUserLogsVo>> sopLogsGroupedById = sopUserLogsVos.stream()
                 .collect(Collectors.groupingBy(SopUserLogsVo::getSopId));
                 .collect(Collectors.groupingBy(SopUserLogsVo::getSopId));
 
 
+        // 查询公司关联小程序数据
+        List<CompanyMiniapp> miniList = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().orderByAsc("sort_num"));
 
 
-        // 查询销售二级域名
-//        Set<Long> ids = sopUserLogsVos.stream().map(s -> {
-//            String[] userKey = s.getUserId().split("\\|");
-//            if (userKey.length < 3) {
-//                return null;
-//            }
-//            return Long.parseLong(userKey[1]);
-//        }).filter(Objects::nonNull).collect(Collectors.toSet());
-//
-//        List<CompanyUser> companyUserList;
-//        if (ids.isEmpty()) {
-//            companyUserList = new ArrayList<>();
-//        } else {
-//            companyUserList = companyUserService.selectCompanyUserByIds(ids);
-//        }
-//
-//        Map<String, List<SopUserLogsVo>> sopLogsGroupedById = sopUserLogsVos.stream()
-//                .peek(s -> {
-//                    String[] userKey = s.getUserId().split("\\|");
-//                    if (userKey.length < 3) {
-//                        return;
-//                    }
-//
-//                    // 销售ID
-//                    Long companyUserId = Long.parseLong(userKey[1]);
-//                    CompanyUser companyUser = companyUserList.stream().filter(cu -> Objects.equals(cu.getUserId(), companyUserId)).findFirst().orElse(null);
-//                    if (Objects.nonNull(companyUser)) {
-//                        if (!StringUtil.strIsNullOrEmpty(companyUser.getDomain())) {
-//                            s.setDomain(companyUser.getDomain().trim());
-//                        } else {
-//                            s.setDomain(config.getRealLinkDomainName().trim());
-//                        }
-//                    } else {
-//                        s.setDomain(config.getRealLinkDomainName().trim());
-//                    }
-//                })
-//                .collect(Collectors.groupingBy(SopUserLogsVo::getSopId));
+        Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniList.stream().collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId, Collectors.groupingBy(CompanyMiniapp::getType)));
 
 
         log.info("共分组 {} 个 SOP ID 进行处理。", sopLogsGroupedById.size());
         log.info("共分组 {} 个 SOP ID 进行处理。", sopLogsGroupedById.size());
 
 
@@ -350,7 +319,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         for (Map.Entry<String, List<SopUserLogsVo>> entry : sopLogsGroupedById.entrySet()) {
         for (Map.Entry<String, List<SopUserLogsVo>> entry : sopLogsGroupedById.entrySet()) {
             String sopId = entry.getKey();
             String sopId = entry.getKey();
             List<SopUserLogsVo> userLogsVos = entry.getValue();
             List<SopUserLogsVo> userLogsVos = entry.getValue();
-            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime, groupChatMap,config);
+            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime, groupChatMap,config,miniMap);
         }
         }
 
 
         // 等待所有 SOP 分组处理完成
         // 等待所有 SOP 分组处理完成
@@ -371,9 +340,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             backoff = @Backoff(delay = 2000)
             backoff = @Backoff(delay = 2000)
     )
     )
     public void processSopGroupAsync(String sopId, List<SopUserLogsVo> userLogsVos, CountDownLatch latch ,LocalDateTime currentTime,
     public void processSopGroupAsync(String sopId, List<SopUserLogsVo> userLogsVos, CountDownLatch latch ,LocalDateTime currentTime,
-                                     Map<String, QwGroupChat> groupChatMap,CourseConfig config) {
+                                     Map<String, QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap) {
         try {
         try {
-            processSopGroup(sopId, userLogsVos,currentTime, groupChatMap, config);
+            processSopGroup(sopId, userLogsVos,currentTime, groupChatMap, config,miniMap);
         } catch (Exception e) {
         } catch (Exception e) {
             log.error("处理 SOP ID {} 时发生异常: {}", sopId, e.getMessage(), e);
             log.error("处理 SOP ID {} 时发生异常: {}", sopId, e.getMessage(), e);
         } finally {
         } finally {
@@ -383,7 +352,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
 
 
     private void processSopGroup(String sopId, List<SopUserLogsVo> userLogsVos,LocalDateTime currentTime, Map<String,
     private void processSopGroup(String sopId, List<SopUserLogsVo> userLogsVos,LocalDateTime currentTime, Map<String,
-            QwGroupChat> groupChatMap,CourseConfig config) throws Exception {
+            QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap) throws Exception {
         QwSopRuleTimeVO ruleTimeVO = sopMapper.selectQwSopByClickHouseId(sopId);
         QwSopRuleTimeVO ruleTimeVO = sopMapper.selectQwSopByClickHouseId(sopId);
 
 
         if (ruleTimeVO == null) {
         if (ruleTimeVO == null) {
@@ -425,7 +394,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
         CountDownLatch userLogsLatch = new CountDownLatch(userLogsVos.size());
         CountDownLatch userLogsLatch = new CountDownLatch(userLogsVos.size());
         for (SopUserLogsVo logVo : userLogsVos) {
         for (SopUserLogsVo logVo : userLogsVos) {
-            processUserLogAsync(logVo, ruleTimeVO, rulesList, userLogsLatch, currentTime, groupChatMap,qwCompany.getMiniAppId(), config);
+            processUserLogAsync(logVo, ruleTimeVO, rulesList, userLogsLatch, currentTime, groupChatMap,qwCompany.getMiniAppId(), config,miniMap);
         }
         }
 
 
         // 等待所有用户日志处理完成
         // 等待所有用户日志处理完成
@@ -446,9 +415,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     )
     )
     public void processUserLogAsync(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings,
     public void processUserLogAsync(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings,
                                     CountDownLatch latch, LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap,
                                     CountDownLatch latch, LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap,
-                                    String miniAppId,CourseConfig config) {
+                                    String miniAppId,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap) {
         try {
         try {
-            processUserLog(logVo, ruleTimeVO, tempSettings,currentTime, groupChatMap, miniAppId, config);
+            processUserLog(logVo, ruleTimeVO, tempSettings,currentTime, groupChatMap, miniAppId, config,miniMap);
         } catch (Exception e) {
         } catch (Exception e) {
             log.error("处理用户日志 {} 时发生异常: {}", logVo.getId(), e.getMessage(), e);
             log.error("处理用户日志 {} 时发生异常: {}", logVo.getId(), e.getMessage(), e);
         } finally {
         } finally {
@@ -458,7 +427,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
 
 
     private void processUserLog(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings,
     private void processUserLog(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings,
-                                LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap,String miniAppId,CourseConfig config) {
+                                LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap,String miniAppId,
+                                CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap) {
         try {
         try {
 
 
             LocalDate startDate = LocalDate.parse(logVo.getStartTime(), DATE_FORMATTER);
             LocalDate startDate = LocalDate.parse(logVo.getStartTime(), DATE_FORMATTER);
@@ -510,6 +480,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             String qwUserId = String.valueOf(qwUserByRedis.getId()).trim();
             String qwUserId = String.valueOf(qwUserByRedis.getId()).trim();
             String companyUserId = String.valueOf(qwUserByRedis.getCompanyUserId()).trim();
             String companyUserId = String.valueOf(qwUserByRedis.getCompanyUserId()).trim();
             String companyId = String.valueOf(qwUserByRedis.getCompanyId()).trim();
             String companyId = String.valueOf(qwUserByRedis.getCompanyId()).trim();
+            Integer sendMsgType = qwUserByRedis.getSendMsgType();
 
 
             if (StringUtil.strIsNullOrEmpty(companyUserId) || StringUtil.strIsNullOrEmpty(companyId) || "null".equals(companyUserId)) {
             if (StringUtil.strIsNullOrEmpty(companyUserId) || StringUtil.strIsNullOrEmpty(companyId) || "null".equals(companyUserId)) {
                 log.error("员工未绑定销售账号或公司,跳过处理:"+qwUserId);
                 log.error("员工未绑定销售账号或公司,跳过处理:"+qwUserId);
@@ -635,7 +606,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
                         insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content, qwUserId,
                         insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content, qwUserId,
                                 companyUserId, companyId, qwUserByRedis.getWelcomeText(),qwUserByRedis.getQwUserName(),
                                 companyUserId, companyId, qwUserByRedis.getWelcomeText(),qwUserByRedis.getQwUserName(),
-                                groupChatMap, miniAppId,config);
+                                groupChatMap, miniAppId,config,miniMap, sendMsgType);
 
 
                     }
                     }
                 } catch (Exception e) {
                 } catch (Exception e) {
@@ -678,7 +649,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private void insertSopUserLogs(List<SopUserLogsInfo> sopUserLogsInfos, SopUserLogsVo logVo, Date sendTime,
     private void insertSopUserLogs(List<SopUserLogsInfo> sopUserLogsInfos, SopUserLogsVo logVo, Date sendTime,
                                    QwSopRuleTimeVO ruleTimeVO, QwSopTempSetting.Content content,
                                    QwSopRuleTimeVO ruleTimeVO, QwSopTempSetting.Content content,
                                    String qwUserId,String companyUserId,String companyId,String welcomeText,String qwUserName,
                                    String qwUserId,String companyUserId,String companyId,String welcomeText,String qwUserName,
-                                   Map<String, QwGroupChat> groupChatMap,String miniAppId,CourseConfig config) {
+                                   Map<String, QwGroupChat> groupChatMap,String miniAppId,CourseConfig config,
+                                   Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap, Integer sendMsgType) {
         String formattedSendTime = sendTime.toInstant()
         String formattedSendTime = sendTime.toInstant()
                 .atZone(ZoneId.systemDefault())
                 .atZone(ZoneId.systemDefault())
                 .format(DATE_TIME_FORMATTER);
                 .format(DATE_TIME_FORMATTER);
@@ -705,7 +677,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null, isOfficial, null);
                 QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null, isOfficial, null);
                 handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                 handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                         type, qwUserId, companyUserId, companyId, groupChat.getChatId(), welcomeText, qwUserName,
                         type, qwUserId, companyUserId, companyId, groupChat.getChatId(), welcomeText, qwUserName,
-                        null, true, miniAppId, groupChat,config);
+                        null, true, miniAppId, groupChat,config, miniMap, null, sendMsgType);
             } else {
             } else {
                 if(groupChat.getChatUserList() != null && !groupChat.getChatUserList().isEmpty()){
                 if(groupChat.getChatUserList() != null && !groupChat.getChatUserList().isEmpty()){
                     groupChat.getChatUserList().forEach(user -> {
                     groupChat.getChatUserList().forEach(user -> {
@@ -714,7 +686,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                         QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, user.getUserId(), user.getName(), null, isOfficial, null);
                         QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, user.getUserId(), user.getName(), null, isOfficial, null);
                         handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                         handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                                 type, qwUserId, companyUserId, companyId, user.getId().toString(), welcomeText, qwUserName,
                                 type, qwUserId, companyUserId, companyId, user.getId().toString(), welcomeText, qwUserName,
-                                null, false, miniAppId, groupChat,config);
+                                null, false, miniAppId, groupChat,config, miniMap, null, sendMsgType);
                     });
                     });
                 }
                 }
             }
             }
@@ -725,9 +697,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     String externalId = contactId.getExternalId().toString();
                     String externalId = contactId.getExternalId().toString();
                     String externalUserName = contactId.getExternalUserName();
                     String externalUserName = contactId.getExternalUserName();
                     Long fsUserId = contactId.getFsUserId();
                     Long fsUserId = contactId.getFsUserId();
+                    Integer grade = contactId.getGrade();
                     QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId, isOfficial, contactId.getExternalId());
                     QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId, isOfficial, contactId.getExternalId());
                     handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                     handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
-                            type, qwUserId, companyUserId, companyId, externalId, welcomeText, qwUserName, fsUserId, false, miniAppId, null,config);
+                            type, qwUserId, companyUserId, companyId, externalId, welcomeText, qwUserName, fsUserId, false, miniAppId,
+                            null,config, miniMap, grade, sendMsgType);
                 } catch (Exception e) {
                 } catch (Exception e) {
                     log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
                     log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
                 }
                 }
@@ -831,11 +805,12 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     }
     }
 
 
     private void handleLogBasedOnType(QwSopLogs sopLogs, QwSopTempSetting.Content content,
     private void handleLogBasedOnType(QwSopLogs sopLogs, QwSopTempSetting.Content content,
-                                      SopUserLogsVo logVo, Date sendTime, Long courseId,
-                                      Long videoId, int type, String qwUserId,
+                                      SopUserLogsVo logVo, Date sendTime, Long courseId, Long videoId, int type, String qwUserId,
                                       String companyUserId, String companyId, String externalId, String welcomeText,
                                       String companyUserId, String companyId, String externalId, String welcomeText,
                                       String qwUserName, Long fsUserId, boolean isGroupChat, String miniAppId,
                                       String qwUserName, Long fsUserId, boolean isGroupChat, String miniAppId,
-                                      QwGroupChat groupChat,CourseConfig config) {
+                                      QwGroupChat groupChat,CourseConfig config,
+                                      Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
+                                      Integer grade, Integer sendMsgType  ) {
         switch (type) {
         switch (type) {
             case 1:
             case 1:
                 handleNormalMessage(sopLogs, content,companyUserId);
                 handleNormalMessage(sopLogs, content,companyUserId);
@@ -843,7 +818,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             case 2:
             case 2:
                 handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId,
                 handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId,
                         qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName, fsUserId,
                         qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName, fsUserId,
-                        isGroupChat, miniAppId, groupChat,config);
+                        isGroupChat, miniAppId, groupChat,config,miniMap, grade, sendMsgType);
                 break;
                 break;
             case 3:
             case 3:
                 handleOrderMessage(sopLogs, content);
                 handleOrderMessage(sopLogs, content);
@@ -873,10 +848,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     }
     }
 
 
     private void handleCourseMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content,
     private void handleCourseMessage(QwSopLogs sopLogs, QwSopTempSetting.Content content,
-                                     SopUserLogsVo logVo, Date sendTime, Long courseId,
-                                     Long videoId, String qwUserId, String companyUserId,
+                                     SopUserLogsVo logVo, Date sendTime, Long courseId, Long videoId, String qwUserId, String companyUserId,
                                      String companyId, String externalId, String welcomeText, String qwUserName,
                                      String companyId, String externalId, String welcomeText, String qwUserName,
-                                     Long fsUserId, boolean isGroupChat, String miniAppId, QwGroupChat groupChat,CourseConfig config) {
+                                     Long fsUserId, boolean isGroupChat, String miniAppId, QwGroupChat groupChat,CourseConfig config,Map<Long,
+                                     Map<Integer, List<CompanyMiniapp>>> miniMap,Integer grade, Integer sendMsgType) {
         // 深拷贝 Content 对象,避免使用 JSON
         // 深拷贝 Content 对象,避免使用 JSON
         QwSopTempSetting.Content clonedContent = deepCopyContent(content);
         QwSopTempSetting.Content clonedContent = deepCopyContent(content);
         if (clonedContent == null) {
         if (clonedContent == null) {
@@ -968,10 +943,25 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     String sortLink = createLinkByMiniApp(setting, logVo, sendTime, courseId, videoId,
                     String sortLink = createLinkByMiniApp(setting, logVo, sendTime, courseId, videoId,
                             qwUserId, companyUserId, companyId, externalId,isOfficial,sopLogs.getFsUserId());
                             qwUserId, companyUserId, companyId, externalId,isOfficial,sopLogs.getFsUserId());
 
 
-                    if (!StringUtil.strIsNullOrEmpty(miniAppId)) {
+                    if (!miniMap.isEmpty() && sendMsgType==1) {
+                        Map<Integer, List<CompanyMiniapp>> integerListMap = miniMap.get(Long.valueOf(companyId));
+                        if (integerListMap != null) {
+
+                            int effectiveGrade = (grade == null) ? 5 : grade;
+                            int listIndex = (effectiveGrade == 1 || effectiveGrade == 2) ? 0 : 1;
+                            List<CompanyMiniapp> miniapps = integerListMap.get(listIndex);
+
+                            if (miniapps != null && !miniapps.isEmpty()) {
+                                CompanyMiniapp companyMiniapp = miniapps.get(0);
+                                if (companyMiniapp != null && !StringUtil.strIsNullOrEmpty(companyMiniapp.getAppId())) {
+                                    setting.setMiniprogramAppid(companyMiniapp.getAppId());
+                                }
+                            }
+                        }
+                    }else if (!StringUtil.strIsNullOrEmpty(miniAppId)){
                         setting.setMiniprogramAppid(miniAppId);
                         setting.setMiniprogramAppid(miniAppId);
                     }else {
                     }else {
-                        log.error("公司的小程序id为空:采用了前端传的固定值"+sopLogs.getSopId());
+                        log.error("公司的小程序id为空:采用了前端传的固定值" + sopLogs.getSopId());
                     }
                     }
 
 
                     setting.setMiniprogramPage(sortLink.replaceAll("^[\\s\\u2005]+", ""));
                     setting.setMiniprogramPage(sortLink.replaceAll("^[\\s\\u2005]+", ""));

+ 262 - 0
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopUserLogsInfoByIsDaysNotStudyImpl.java

@@ -0,0 +1,262 @@
+package com.fs.app.taskService.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.app.taskService.SopUserLogsInfoByIsDaysNotStudy;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.sop.domain.SopUserLogs;
+import com.fs.sop.domain.SopUserLogsInfo;
+import com.fs.sop.params.QwRatingConfig;
+import com.fs.sop.service.ISopUserLogsInfoService;
+import com.fs.sop.service.ISopUserLogsService;
+import com.fs.system.service.ISysConfigService;
+import com.fs.voice.utils.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.*;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+public class SopUserLogsInfoByIsDaysNotStudyImpl implements SopUserLogsInfoByIsDaysNotStudy {
+
+
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    private QwExternalContactMapper qwExternalContactMapper;
+
+    @Autowired
+    private ISopUserLogsInfoService iSopUserLogsInfoService;
+
+    @Autowired
+    private ISopUserLogsService iSopUserLogsService;
+
+    @Autowired
+    private ExecutorService sopRatingExecutor;  // 自定义线程池
+
+    // 任务队列
+    private final BlockingQueue<SopUserLogs> taskQueue = new LinkedBlockingQueue<>(10000);
+
+    private volatile boolean running = true;
+    //批量更新队列
+    private final List<CompletableFuture<Void>> updateFutures = Collections.synchronizedList(new ArrayList<>());
+
+    private final Object configLock = new Object();
+
+
+    private  volatile QwRatingConfig qwRatingConfig;
+
+    // 启动时初始化消费者线程
+    @PostConstruct
+    public void init() {
+
+        loadCourseConfig();
+
+        int consumerCount = Runtime.getRuntime().availableProcessors(); // 消费者线程数,默认 CPU 核心数
+        for (int i = 0; i < consumerCount; i++) {
+            sopRatingExecutor.submit(this::consumeTasks); // 提交消费者任务
+        }
+
+    }
+
+    private void loadCourseConfig() {
+        try {
+            String json = configService.selectConfigByKey("qwRating:config");
+            QwRatingConfig config = JSON.parseObject(json, QwRatingConfig.class);
+            if (!StringUtil.strIsNullOrEmpty(json) && config != null) {
+                qwRatingConfig = config;
+                log.info("Loaded qwRating.config successfully.");
+            } else {
+                log.error("Failed to load course.config from configService.");
+            }
+        } catch (Exception e) {
+            log.error("Exception while loading qwRating.config: {}", e.getMessage(), e);
+        }
+    }
+
+
+
+    @Override
+    public void restoreByIsDaysNotStudy() {
+
+        // 分页加载并放入队列
+        int pageSize = 1000;
+        int offset = 0;
+        List<SopUserLogs> sopUserLogs;
+
+        // 获取缓存的配置
+        QwRatingConfig config;
+        synchronized(configLock) {
+            config = qwRatingConfig;
+        }
+
+        do {
+            sopUserLogs = iSopUserLogsService.meetsTherestoreByIsDaysNotStudy(offset, pageSize,config.getNotStudyDays());
+            if (!sopUserLogs.isEmpty()) {
+                sopUserLogs.forEach(item -> {
+                    try {
+                        taskQueue.put(item); // 将任务放入队列
+                    } catch (InterruptedException e) {
+                        log.error("任务放入队列失败,sopId: {}", item.getSopId(), e);
+                        Thread.currentThread().interrupt();
+                    }
+                });
+                offset += pageSize;
+            }
+        } while (!sopUserLogs.isEmpty());
+
+
+        // 等待队列处理完成
+        CompletableFuture.runAsync(() -> {
+            while (!taskQueue.isEmpty()) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log.error("等待队列处理时中断", e);
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }).join(); // 等待任务完成
+
+    }
+
+    private void consumeTasks() {
+        if (!running && taskQueue.isEmpty()) {
+            log.info("没有评级任务需要处理");
+            return; // 如果队列为空且没有正在运行的线程,则直接返回
+        }
+
+        while (running) {
+            try {
+                SopUserLogs item = taskQueue.poll(1, TimeUnit.SECONDS); // 等待 1 秒
+                if (item != null) {
+                    processRestoreByIsDaysNotStudy(item);
+                }
+            } catch (Exception e) {
+                log.error("消费者线程异常", e);
+            }
+        }
+    }
+
+    private void processRestoreByIsDaysNotStudy(SopUserLogs item) {
+
+        // 获取缓存的配置
+        QwRatingConfig config;
+        synchronized(configLock) {
+            config = qwRatingConfig;
+        }
+
+        List<SopUserLogsInfo> infos = iSopUserLogsInfoService.selectRestoreByIsDaysNotStudy(
+                item.getSopId(), item.getId());
+
+        if (infos == null || infos.isEmpty()) {
+            log.error("当前营期没有E级客户-sopId:{},营期id:{}", item.getSopId(), item.getId());
+            return;
+        }
+
+        List<QwExternalContact> contacts = infos.stream()
+                .map(info -> processUserLog(info, config))
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (!contacts.isEmpty()) {
+            batchUpdateQwExternalContact(contacts);
+        }
+    }
+
+    private void batchUpdateQwExternalContact(List<QwExternalContact> contacts) {
+        // 9. 优化分批逻辑
+        int total = contacts.size();
+        for (int i = 0; i < total; i += 300) {
+            List<QwExternalContact> batch = contacts.subList(i, Math.min(i + 300, total));
+
+            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                try {
+                    qwExternalContactMapper.batchUpdateQwExternalByIsDaysNotStudy(batch);
+                    iSopUserLogsInfoService.batchUpdateSopUserLogsInfoByIsDaysNotStudy(batch);
+                } catch (Exception e) {
+                    log.error("批量更新异常, 批次大小: {}", batch.size(), e);
+                }
+            }, sopRatingExecutor);
+
+            updateFutures.add(future);
+        }
+    }
+
+    @PreDestroy
+    public void shutdown() {
+        running = false;  // 标记消费者停止
+        log.info("正在关闭线程池...");
+
+        // **等待任务队列处理完毕**
+        while (!taskQueue.isEmpty()) {
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                log.warn("等待任务队列处理完成时被中断", e);
+            }
+        }
+
+        // **确保所有  的任务完成**
+        log.info("等待所有批量更新任务完成...");
+        CompletableFuture.allOf(updateFutures.toArray(new CompletableFuture[0])).join();
+
+        // 关闭线程池
+        sopRatingExecutor.shutdown();
+        try {
+            if (!sopRatingExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
+                List<Runnable> pendingTasks = sopRatingExecutor.shutdownNow();
+                log.warn("强制关闭线程池,未完成任务数: {}", pendingTasks.size());
+            }
+        } catch (InterruptedException e) {
+            sopRatingExecutor.shutdownNow();
+            Thread.currentThread().interrupt();
+        }
+        log.info("线程池和消费者已完全关闭");
+    }
+
+    /**
+    * 只计算时长
+    */
+    private QwExternalContact processUserLog(SopUserLogsInfo logsInfo, QwRatingConfig config) {
+        try {
+
+            Long externalId = logsInfo.getExternalId();
+            if (externalId == null) {
+                return null;
+            }
+
+            Integer sumDuration = fsCourseWatchLogMapper.selectFsCourseWatchLogByByIsDaysNotStudy(externalId, config.getNotStudyDays());
+
+            if (sumDuration!=null && sumDuration>0) {
+                QwExternalContact externalContact = new QwExternalContact();
+                externalContact.setId(externalId);
+                externalContact.setIsDaysNotStudy(0);
+                return externalContact;
+            }
+
+            return null;
+
+        } catch (Exception e) {
+            log.error("计算用户积分异常,用户:{}", logsInfo, e);
+            return null;
+        }
+    }
+
+
+}

+ 13 - 0
fs-qw-task/src/main/java/com/fs/framework/config/ThreadPoolConfig.java

@@ -4,8 +4,10 @@ import com.fs.common.utils.Threads;
 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.TaskScheduler;
 import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledExecutorService;
@@ -33,6 +35,17 @@ public class ThreadPoolConfig
     // 线程池维护线程所允许的空闲时间
     // 线程池维护线程所允许的空闲时间
     private int keepAliveSeconds = 300;
     private int keepAliveSeconds = 300;
 
 
+
+    @Bean
+    public TaskScheduler taskScheduler(){
+        ThreadPoolTaskScheduler scheduler=new ThreadPoolTaskScheduler();
+        scheduler.setPoolSize(18);
+        scheduler.setThreadNamePrefix("scheduled-task-");
+        scheduler.setAwaitTerminationSeconds(60);
+        scheduler.setWaitForTasksToCompleteOnShutdown(true);
+        return scheduler;
+    }
+
     @Bean(name = "threadPoolTaskExecutor")
     @Bean(name = "threadPoolTaskExecutor")
     public ThreadPoolTaskExecutor threadPoolTaskExecutor()
     public ThreadPoolTaskExecutor threadPoolTaskExecutor()
     {
     {

+ 7 - 0
fs-service/src/main/java/com/fs/company/domain/Company.java

@@ -112,6 +112,13 @@ public class Company extends BaseEntity
     private String courseMiniAppId;
     private String courseMiniAppId;
     /** 会员是否默认黑名单,1-是;0-否(用于销售分享成为会员的操作) */
     /** 会员是否默认黑名单,1-是;0-否(用于销售分享成为会员的操作) */
     private Integer fsUserIsDefaultBlack;
     private Integer fsUserIsDefaultBlack;
+    private Integer repeat;
+    private Integer sendIfType;
+    private Integer ifNum;
+    @TableField(exist = false)
+    private List<String> miniAppMaster;
+    @TableField(exist = false)
+    private List<String> miniAppServer;
 
 
     /** 后台制单是否需要付款 默认1 0-否 1-是*/
     /** 后台制单是否需要付款 默认1 0-否 1-是*/
     private Integer isPay;
     private Integer isPay;

+ 41 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyMiniapp.java

@@ -0,0 +1,41 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 客服公司小程序对象 company_miniapp
+ *
+ * @author fs
+ * @date 2025-07-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyMiniapp extends BaseEntity{
+
+    /** id */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 客服公司ID */
+    @Excel(name = "客服公司ID")
+    private Long companyId;
+
+    /** 小程序appid */
+    @Excel(name = "小程序appid")
+    private String appId;
+
+    /** 主从 0主小程序1备用小程序 */
+    @Excel(name = "主从 0主小程序1备用小程序")
+    private Integer type;
+
+    /** 排序 */
+    @Excel(name = "排序")
+    private Integer sortNum;
+
+
+}

+ 62 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyMiniappMapper.java

@@ -0,0 +1,62 @@
+package com.fs.company.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyMiniapp;
+
+import java.util.List;
+
+/**
+ * 客服公司小程序Mapper接口
+ * 
+ * @author fs
+ * @date 2025-07-24
+ */
+public interface CompanyMiniappMapper extends BaseMapper<CompanyMiniapp>{
+    /**
+     * 查询客服公司小程序
+     * 
+     * @param id 客服公司小程序主键
+     * @return 客服公司小程序
+     */
+    CompanyMiniapp selectCompanyMiniappById(Long id);
+
+    /**
+     * 查询客服公司小程序列表
+     * 
+     * @param companyMiniapp 客服公司小程序
+     * @return 客服公司小程序集合
+     */
+    List<CompanyMiniapp> selectCompanyMiniappList(CompanyMiniapp companyMiniapp);
+
+    /**
+     * 新增客服公司小程序
+     * 
+     * @param companyMiniapp 客服公司小程序
+     * @return 结果
+     */
+    int insertCompanyMiniapp(CompanyMiniapp companyMiniapp);
+
+    /**
+     * 修改客服公司小程序
+     * 
+     * @param companyMiniapp 客服公司小程序
+     * @return 结果
+     */
+    int updateCompanyMiniapp(CompanyMiniapp companyMiniapp);
+
+    /**
+     * 删除客服公司小程序
+     * 
+     * @param id 客服公司小程序主键
+     * @return 结果
+     */
+    int deleteCompanyMiniappById(Long id);
+
+    /**
+     * 批量删除客服公司小程序
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyMiniappByIds(Long[] ids);
+}

+ 70 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyMiniappService.java

@@ -0,0 +1,70 @@
+package com.fs.company.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyMiniapp;
+import com.fs.company.vo.CompanyVO;
+
+import java.util.List;
+
+/**
+ * 客服公司小程序Service接口
+ * 
+ * @author fs
+ * @date 2025-07-24
+ */
+public interface ICompanyMiniappService extends IService<CompanyMiniapp>{
+    /**
+     * 查询客服公司小程序
+     * 
+     * @param id 客服公司小程序主键
+     * @return 客服公司小程序
+     */
+    CompanyMiniapp selectCompanyMiniappById(Long id);
+
+    /**
+     * 查询客服公司小程序列表
+     * 
+     * @param companyMiniapp 客服公司小程序
+     * @return 客服公司小程序集合
+     */
+    List<CompanyMiniapp> selectCompanyMiniappList(CompanyMiniapp companyMiniapp);
+
+    /**
+     * 新增客服公司小程序
+     * 
+     * @param companyMiniapp 客服公司小程序
+     * @return 结果
+     */
+    int insertCompanyMiniapp(CompanyMiniapp companyMiniapp);
+
+    /**
+     * 修改客服公司小程序
+     * 
+     * @param companyMiniapp 客服公司小程序
+     * @return 结果
+     */
+    int updateCompanyMiniapp(CompanyMiniapp companyMiniapp);
+
+    /**
+     * 批量删除客服公司小程序
+     * 
+     * @param ids 需要删除的客服公司小程序主键集合
+     * @return 结果
+     */
+    int deleteCompanyMiniappByIds(Long[] ids);
+
+    /**
+     * 删除客服公司小程序信息
+     * 
+     * @param id 客服公司小程序主键
+     * @return 结果
+     */
+    int deleteCompanyMiniappById(Long id);
+
+    void insertBatch(List<String> appIds, Long companyId, Integer type);
+
+    void removeByCompanyId(Long companyId);
+
+    void setMiniAppList(List<CompanyVO> companyVOS);
+    List<CompanyMiniapp> getMiniAppListByCompanyList(List<Long> companyIds);
+}

+ 141 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyMiniappServiceImpl.java

@@ -0,0 +1,141 @@
+package com.fs.company.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.PubFun;
+import com.fs.company.domain.CompanyMiniapp;
+import com.fs.company.mapper.CompanyMiniappMapper;
+import com.fs.company.service.ICompanyMiniappService;
+import com.fs.company.vo.CompanyVO;
+import org.springframework.stereotype.Service;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+/**
+ * 客服公司小程序Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-07-24
+ */
+@Service
+public class CompanyMiniappServiceImpl extends ServiceImpl<CompanyMiniappMapper, CompanyMiniapp> implements ICompanyMiniappService {
+
+    public static BiFunction<Integer, List<CompanyMiniapp>, List<String>> GET_MINI_APP_STR = (type, list) -> list.stream().filter(m -> Objects.equals(m.getType(), type)).sorted(Comparator.comparing(CompanyMiniapp::getSortNum)).map(CompanyMiniapp::getAppId).collect(Collectors.toList());
+
+
+    /**
+     * 查询客服公司小程序
+     * 
+     * @param id 客服公司小程序主键
+     * @return 客服公司小程序
+     */
+    @Override
+    public CompanyMiniapp selectCompanyMiniappById(Long id)
+    {
+        return baseMapper.selectCompanyMiniappById(id);
+    }
+
+    /**
+     * 查询客服公司小程序列表
+     * 
+     * @param companyMiniapp 客服公司小程序
+     * @return 客服公司小程序
+     */
+    @Override
+    public List<CompanyMiniapp> selectCompanyMiniappList(CompanyMiniapp companyMiniapp)
+    {
+        return baseMapper.selectCompanyMiniappList(companyMiniapp);
+    }
+
+    /**
+     * 新增客服公司小程序
+     * 
+     * @param companyMiniapp 客服公司小程序
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyMiniapp(CompanyMiniapp companyMiniapp)
+    {
+        companyMiniapp.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyMiniapp(companyMiniapp);
+    }
+
+    /**
+     * 修改客服公司小程序
+     * 
+     * @param companyMiniapp 客服公司小程序
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyMiniapp(CompanyMiniapp companyMiniapp)
+    {
+        companyMiniapp.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateCompanyMiniapp(companyMiniapp);
+    }
+
+    /**
+     * 批量删除客服公司小程序
+     * 
+     * @param ids 需要删除的客服公司小程序主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyMiniappByIds(Long[] ids)
+    {
+        return baseMapper.deleteCompanyMiniappByIds(ids);
+    }
+
+    /**
+     * 删除客服公司小程序信息
+     * 
+     * @param id 客服公司小程序主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyMiniappById(Long id)
+    {
+        return baseMapper.deleteCompanyMiniappById(id);
+    }
+
+    @Override
+    public void insertBatch(List<String> appIds, Long companyId, Integer type) {
+        AtomicInteger i = new AtomicInteger();
+        List<CompanyMiniapp> list = appIds.stream().map(e -> {
+            CompanyMiniapp miniapp = new CompanyMiniapp();
+            miniapp.setCompanyId(companyId);
+            miniapp.setAppId(e);
+            miniapp.setType(type);
+            miniapp.setSortNum(i.getAndIncrement());
+            return miniapp;
+        }).collect(Collectors.toList());
+        super.saveBatch(list);
+    }
+
+    @Override
+    public void removeByCompanyId(Long companyId) {
+        remove(new QueryWrapper<CompanyMiniapp>().eq("company_id",companyId));
+    }
+
+    @Override
+    public void setMiniAppList(List<CompanyVO> companyVOS) {
+        List<CompanyMiniapp> miniAppList = getMiniAppListByCompanyList(PubFun.listToNewList(companyVOS, CompanyVO::getCompanyId));
+        Map<Long, List<CompanyMiniapp>> miniAppMap = PubFun.listToMapByGroupList(miniAppList, CompanyMiniapp::getCompanyId);
+        companyVOS.stream().filter(e -> miniAppMap.containsKey(e.getCompanyId())).forEach(e -> {
+            List<CompanyMiniapp> list = miniAppMap.get(e.getCompanyId());
+            e.setMiniAppMaster(GET_MINI_APP_STR.apply(0, list));
+            e.setMiniAppServer(GET_MINI_APP_STR.apply(1, list));
+        });
+    }
+
+    @Override
+    public List<CompanyMiniapp> getMiniAppListByCompanyList(List<Long> companyIds) {
+        return list(new QueryWrapper<CompanyMiniapp>().in("company_id", companyIds));
+    }
+}

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

@@ -13,6 +13,7 @@ import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.*;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
 import com.fs.company.mapper.*;
 import com.fs.company.param.CompanyParam;
 import com.fs.company.param.CompanyParam;
+import com.fs.company.service.ICompanyMiniappService;
 import com.fs.company.service.ICompanyProfitService;
 import com.fs.company.service.ICompanyProfitService;
 import com.fs.company.service.ICompanyRoleService;
 import com.fs.company.service.ICompanyRoleService;
 import com.fs.company.vo.CompanyCrmVO;
 import com.fs.company.vo.CompanyCrmVO;
@@ -57,6 +58,8 @@ public class CompanyServiceImpl implements ICompanyService
     @Autowired
     @Autowired
     private CompanyMapper companyMapper;
     private CompanyMapper companyMapper;
     @Autowired
     @Autowired
+    private ICompanyMiniappService companyMiniappService;
+    @Autowired
     private CompanyDeptMapper deptMapper;
     private CompanyDeptMapper deptMapper;
     @Autowired
     @Autowired
     private ICompanyRoleService roleService;
     private ICompanyRoleService roleService;
@@ -238,7 +241,7 @@ public class CompanyServiceImpl implements ICompanyService
             userPostMapper.insertCompanyUserPost(userPost);
             userPostMapper.insertCompanyUserPost(userPost);
             company.setUserId(user.getUserId());
             company.setUserId(user.getUserId());
             companyMapper.updateCompany(company);
             companyMapper.updateCompany(company);
-
+            bindMiniApp(company);
             return R.ok();
             return R.ok();
         }
         }
         else
         else
@@ -257,8 +260,22 @@ public class CompanyServiceImpl implements ICompanyService
     public int updateCompany(Company company)
     public int updateCompany(Company company)
     {
     {
         company.setUpdateTime(DateUtils.getNowDate());
         company.setUpdateTime(DateUtils.getNowDate());
+        bindMiniApp(company);
         return companyMapper.updateCompany(company);
         return companyMapper.updateCompany(company);
     }
     }
+    // 绑定小程序
+    public void bindMiniApp(Company company){
+        companyMiniappService.removeByCompanyId(company.getCompanyId());
+        List<String> miniAppMaster = company.getMiniAppMaster();
+        if(miniAppMaster != null && !miniAppMaster.isEmpty()){
+            companyMiniappService.insertBatch(miniAppMaster, company.getCompanyId(), 0);
+        }
+        List<String> miniAppServer = company.getMiniAppServer();
+        if(miniAppServer != null && !miniAppServer.isEmpty()){
+            companyMiniappService.insertBatch(miniAppServer, company.getCompanyId(), 1);
+        }
+
+    }
 
 
     /**
     /**
      * 批量删除企业
      * 批量删除企业

+ 3 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyVO.java

@@ -7,6 +7,7 @@ import lombok.Data;
 import java.io.Serializable;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.util.Date;
 import java.util.Date;
+import java.util.List;
 
 
 /**
 /**
  * 企业账户记录对象 company_money_logs
  * 企业账户记录对象 company_money_logs
@@ -86,4 +87,6 @@ public class CompanyVO implements Serializable
     private String followDoctorName;
     private String followDoctorName;
 
 
     private String restartTime;
     private String restartTime;
+    private List<String> miniAppMaster;
+    private List<String> miniAppServer;
 }
 }

+ 28 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -165,6 +165,34 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "\tAND wl.create_time < CURDATE() ")
             "\tAND wl.create_time < CURDATE() ")
     List<QwRatingVO> selectFsCourseWatchLogByExtIdRating(@Param("externalId") Long externalId, @Param("dayNum") Integer dayNum);
     List<QwRatingVO> selectFsCourseWatchLogByExtIdRating(@Param("externalId") Long externalId, @Param("dayNum") Integer dayNum);
 
 
+    @Select("SELECT\n" +
+            "\twl.duration AS watchDuration,\n" +
+            "\tcv.duration AS allDuration,\n" +
+            "\twl.finish_time,\n" +
+            "\twl.create_time," +
+            "\tec.`level` \n" +
+            "FROM\n" +
+            "\tfs_course_watch_log wl\n" +
+            "\tLEFT JOIN qw_external_contact ec ON wl.qw_external_contact_id = ec.id\n" +
+            "\tLEFT JOIN fs_user_course_video cv ON wl.video_id = cv.video_id \n" +
+            "WHERE\n" +
+            "\twl.send_type = 2 \n" +
+            "\tAND wl.qw_external_contact_id = #{externalId} \n" +
+            "\tAND wl.create_time >= DATE_SUB(CURDATE(), INTERVAL #{dayNum} DAY ) \n" +
+            "\tAND wl.create_time < CURDATE()")
+    List<QwRatingVO> selectFsCourseWatchLogByExtIdRatingMoreStudyDays(@Param("externalId") Long externalId, @Param("dayNum") Integer dayNum);
+
+    @Select("SELECT\n" +
+            "\tCOALESCE(SUM(wl.duration), 0) AS watchDuration\n" +
+            "FROM\n" +
+            "\tfs_course_watch_log wl\n" +
+            "WHERE\n" +
+            "\twl.send_type = 2 \n" +
+            "\tAND wl.qw_external_contact_id = #{externalId} \n" +
+            "\tAND wl.create_time >= DATE_SUB( CURDATE(), INTERVAL #{dayNum} DAY ) \n" +
+            "\tAND wl.create_time < CURDATE()")
+    Integer selectFsCourseWatchLogByByIsDaysNotStudy(@Param("externalId") Long externalId,@Param("dayNum") Integer dayNum);
+
     @Select("select l.*,v.title,c.course_name from fs_course_watch_log l LEFT JOIN fs_user_course_video v ON v.video_id = l.video_id LEFT JOIN fs_user_course c ON c.course_id = l.course_id WHERE l.qw_external_contact_id =#{ExtId} and l.qw_user_id=#{QwUserId} and DATE(l.create_time) =CURDATE() ORDER BY l.create_time  desc LIMIT 1  ")
     @Select("select l.*,v.title,c.course_name from fs_course_watch_log l LEFT JOIN fs_user_course_video v ON v.video_id = l.video_id LEFT JOIN fs_user_course c ON c.course_id = l.course_id WHERE l.qw_external_contact_id =#{ExtId} and l.qw_user_id=#{QwUserId} and DATE(l.create_time) =CURDATE() ORDER BY l.create_time  desc LIMIT 1  ")
     FsCourseWatchLogVO selectFsCourseWatchLogByExtIdAndQwUserId(@Param("ExtId")String ExtId,@Param("QwUserId")Long QwUserId);
     FsCourseWatchLogVO selectFsCourseWatchLogByExtIdAndQwUserId(@Param("ExtId")String ExtId,@Param("QwUserId")Long QwUserId);
 
 

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

@@ -279,7 +279,7 @@ public interface FsUserCourseMapper
     Integer selectTodayCourseWatchLogCountByUserIdAndProjectId(@Param("userId") Long userId, @Param("projectId") Long projectId);
     Integer selectTodayCourseWatchLogCountByUserIdAndProjectId(@Param("userId") Long userId, @Param("projectId") Long projectId);
 
 
     @Select("select course_id,course_name,description,img_url,second_img secondImg,views from fs_user_course where " +
     @Select("select course_id,course_name,description,img_url,second_img secondImg,views from fs_user_course where " +
-            " is_private = 0 and is_del = 0 order by sort,course_id")
+            " is_private = 0 and is_del = 0 and is_show = 1 and is_tui = 1 order by sort,course_id")
     List<FsUserCourseVideoAppletVO> selectFsUserCourseVideoApplet();
     List<FsUserCourseVideoAppletVO> selectFsUserCourseVideoApplet();
 
 
     @Select("select video_id,title,course_id,video_url,SEC_TO_TIME(duration) as total_duration," +
     @Select("select video_id,title,course_id,video_url,SEC_TO_TIME(duration) as total_duration," +

+ 1 - 0
fs-service/src/main/java/com/fs/his/enums/FsStoreOrderStatusEnum.java

@@ -17,6 +17,7 @@ public enum FsStoreOrderStatusEnum {
     STATUS_2(2,"待发货"),
     STATUS_2(2,"待发货"),
     STATUS_3(3,"待收货"),
     STATUS_3(3,"待收货"),
     STATUS_4(4,"已完成"),
     STATUS_4(4,"已完成"),
+    STATUS_6(6,"待推送"), //金牛代服特殊状态
 
 
     REFUND_STATUS_0(0,"正常"),
     REFUND_STATUS_0(0,"正常"),
     REFUND_STATUS_1(1,"退款中"),
     REFUND_STATUS_1(1,"退款中"),

+ 1 - 1
fs-service/src/main/java/com/fs/his/service/IFsExportTaskService.java

@@ -63,7 +63,7 @@ public interface IFsExportTaskService
 
 
     Integer isExportType1( Long userId);
     Integer isExportType1( Long userId);
 
 
-    public void exportStore1Data(FsStoreOrderParam param);
+    public void exportStore1Data(FsStoreOrderParam param,boolean isAdmin);
 
 
     public void exportStoreData(FsStoreOrderParam fsStoreOrder);
     public void exportStoreData(FsStoreOrderParam fsStoreOrder);
 
 

+ 10 - 0
fs-service/src/main/java/com/fs/his/service/IFsStoreOrderService.java

@@ -62,6 +62,14 @@ public interface IFsStoreOrderService
      */
      */
     public int updateFsStoreOrder(FsStoreOrder fsStoreOrder);
     public int updateFsStoreOrder(FsStoreOrder fsStoreOrder);
 
 
+    /**
+     * 修改订单状态-收货信息
+     *
+     * @param fsStoreOrder 订单
+     * @return 结果(含具体报错信息)
+     */
+    R updateStoreOrder(FsStoreOrder fsStoreOrder);
+
     /**
     /**
      * 批量删除订单
      * 批量删除订单
      *
      *
@@ -248,4 +256,6 @@ public interface IFsStoreOrderService
 
 
     List<FsStoreOrderListVO> selectFsStoreOrderListVOByErpAccount(FsStoreOrderParam fsStoreOrder);
     List<FsStoreOrderListVO> selectFsStoreOrderListVOByErpAccount(FsStoreOrderParam fsStoreOrder);
     List<FsStoreOrder> selectFsStoreOrderByFsUserId(Long fsUserId);
     List<FsStoreOrder> selectFsStoreOrderByFsUserId(Long fsUserId);
+
+    String importOrderStatusData(List<FsStoreOrderStatusExcelVO> list);
 }
 }

+ 6 - 2
fs-service/src/main/java/com/fs/his/service/impl/FsExportTaskServiceImpl.java

@@ -11,6 +11,7 @@ import com.fs.his.mapper.FsStorePaymentMapper;
 import com.fs.his.param.FsStoreOrderParam;
 import com.fs.his.param.FsStoreOrderParam;
 import com.fs.his.param.FsStorePaymentParam;
 import com.fs.his.param.FsStorePaymentParam;
 import com.fs.his.service.IFsStoreOrderService;
 import com.fs.his.service.IFsStoreOrderService;
+import com.fs.his.utils.PhoneUtil;
 import com.fs.his.vo.FsStoreOrderExcelVO;
 import com.fs.his.vo.FsStoreOrderExcelVO;
 import com.fs.his.vo.FsStoreOrderExportVO;
 import com.fs.his.vo.FsStoreOrderExportVO;
 import com.fs.his.vo.FsStorePaymentExcelVO;
 import com.fs.his.vo.FsStorePaymentExcelVO;
@@ -117,7 +118,7 @@ public class FsExportTaskServiceImpl implements IFsExportTaskService
 
 
     @Async
     @Async
     @Override
     @Override
-    public void exportStore1Data(FsStoreOrderParam fsStoreOrder) {
+    public void exportStore1Data(FsStoreOrderParam fsStoreOrder,boolean isAdmin) {
         List<FsStoreOrderExportVO> list = fsStoreOrderMapper.selectFsStoreOrderListVOByExport(fsStoreOrder);
         List<FsStoreOrderExportVO> list = fsStoreOrderMapper.selectFsStoreOrderListVOByExport(fsStoreOrder);
         //对手机号脱敏
         //对手机号脱敏
         if (list != null) {
         if (list != null) {
@@ -126,9 +127,12 @@ public class FsExportTaskServiceImpl implements IFsExportTaskService
                 if (vo.getCycle()!=null){
                 if (vo.getCycle()!=null){
                     vo.setFollowCount(vo.getCycle()/ vo.getFollowFrequency());
                     vo.setFollowCount(vo.getCycle()/ vo.getFollowFrequency());
                 }
                 }
-                if (vo.getUserPhone() != null) {
+                if (vo.getUserPhone() != null &&  !isAdmin) {
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                     vo.setUserPhone(vo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
                 }
                 }
+                if (vo.getUserPhone() != null && isAdmin && vo.getUserPhone().length()>11) {
+                    vo.setUserPhone(PhoneUtil.decryptPhone(vo.getUserPhone()));
+                }
                 if (vo.getUserAddress() != null) {
                 if (vo.getUserAddress() != null) {
                     vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
                     vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
                 }
                 }

+ 127 - 4
fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java

@@ -10,10 +10,7 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.ServiceException;
-import com.fs.common.utils.DateUtils;
-import com.fs.common.utils.ParseUtils;
-import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.*;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyDept;
 import com.fs.company.domain.CompanyDept;
@@ -305,6 +302,56 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         return fsStoreOrderMapper.updateFsStoreOrder(fsStoreOrder);
         return fsStoreOrderMapper.updateFsStoreOrder(fsStoreOrder);
     }
     }
 
 
+    /**
+     * 修改订单状态-收货信息
+     *
+     * @param fsStoreOrder 订单
+     * @return 结果(含具体报错信息)
+     */
+    @Override
+    public R updateStoreOrder(FsStoreOrder fsStoreOrder) {
+        //userPhone 电话处理
+        String userPhone = fsStoreOrder.getUserPhone();
+        if (StringUtils.isNotBlank(userPhone)) {
+            if (!userPhone.contains("*")){
+                fsStoreOrder.setUserPhone(userPhone);
+            } else {
+                userPhone = null;
+                fsStoreOrder.setUserPhone(null);
+            }
+        }
+        String userAddress = fsStoreOrder.getUserAddress();
+        if (StringUtils.isNotBlank(userAddress)) {
+            if (!userAddress.contains("*")){
+                fsStoreOrder.setUserAddress(userAddress);
+            } else {
+                userAddress = null;
+                fsStoreOrder.setUserAddress(null);
+            }
+        }
+        FsStoreOrder oldOrder = fsStoreOrderMapper.selectFsStoreOrderByOrderId(fsStoreOrder.getOrderId());
+        if (oldOrder == null) return R.error("修改订单不存在");
+        if (StringUtils.isNotBlank(userAddress)){
+            if (userAddress.equals(oldOrder.getUserAddress())) {
+                userAddress = null;
+            }
+        }
+        if (StringUtils.isNotBlank(userAddress) || StringUtils.isNotBlank(userPhone)){
+            Integer status = fsStoreOrder.getStatus();
+            if (status == null){
+                status = oldOrder.getStatus();
+            }
+            if (Objects.equals(FsStoreOrderStatusEnum.STATUS_1.getValue(), status) || Objects.equals(FsStoreOrderStatusEnum.STATUS_6.getValue(), status)) {
+                fsStoreOrder.setUserAddress(StringUtils.isNotBlank(userAddress)?null:userAddress);
+                fsStoreOrder.setUserPhone(StringUtils.isNotBlank(userPhone)?null:userPhone);
+            } else {
+                return R.error("该订单状态不支持修改收货地址/电话");
+            }
+        }
+        fsStoreOrder.setUpdateTime(DateUtils.getNowDate());
+        return fsStoreOrderMapper.updateFsStoreOrder(fsStoreOrder)>0?R.ok():R.error();
+    }
+
     /**
     /**
      * 批量删除订单
      * 批量删除订单
      *
      *
@@ -3422,4 +3469,80 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         return fsStoreOrderMapper.selectFsStoreOrderByFsUserId(fsUserId);
         return fsStoreOrderMapper.selectFsStoreOrderByFsUserId(fsUserId);
     }
     }
 
 
+    @Override
+    public String importOrderStatusData(List<FsStoreOrderStatusExcelVO> list) {
+        if (StringUtils.isNull(list) || list.isEmpty()) {
+            throw new ServiceException("导入数据不能为空!");
+        }
+        int successNum = 0;
+        int failureNum = 0;
+        StringBuilder successMsg = new StringBuilder();
+        StringBuilder failureMsg = new StringBuilder();
+        for (FsStoreOrderStatusExcelVO vo : list) {
+            try {
+                //1.必填参数
+                ExcelUtils.validateRequiredFields(vo, list.indexOf(vo) + 1); // 传入行号
+                FsStoreOrder o = fsStoreOrderMapper.selectFsStoreOrderByOrderCode(vo.getOrderCode());
+                if (o ==null){
+                    failureNum++;
+                    String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
+                    failureMsg.append(msg).append("订单不存在");
+                    continue;
+                }
+                FsStoreOrder param = new FsStoreOrder(); //修改订单的参数
+                param.setOrderCode(vo.getOrderCode());
+                param.setOrderId(o.getOrderId());
+                if ("6".equals(vo.getStatus())) {
+                    failureNum++;
+                    String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
+                    failureMsg.append(msg).append("该状态不支持修改为待推送");
+                    continue;
+                }
+
+                Integer status = o.getStatus();
+
+                if (StringUtils.isNotBlank(vo.getStatus())){
+                    param.setStatus(Integer.valueOf(vo.getStatus()));
+                    status = Integer.valueOf(vo.getStatus());
+                }
+                /**
+                 * 地址和电话仅待付款和待推送可以修改
+                 */
+                String userAddress = vo.getUserAddress();
+                String userPhone = vo.getUserPhone();
+                if (StringUtils.isNotBlank(userAddress) || StringUtils.isNotBlank(userPhone)){
+                    if (Objects.equals(FsStoreOrderStatusEnum.STATUS_1.getValue(), status) || Objects.equals(FsStoreOrderStatusEnum.STATUS_6.getValue(), status)) {
+                        param.setUserAddress(userAddress.isEmpty()?null:userAddress);
+                        param.setUserPhone(userPhone.isEmpty()?null:userPhone);
+                    } else {
+                        failureNum++;
+                        String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 修改失败:";
+                        failureMsg.append(msg).append("该状态不支持修改收货人地址或电话");
+                        continue;
+                    }
+                }
+                param.setDeliveryStatus((vo.getDeliveryStatus()==null|| vo.getDeliveryStatus().isEmpty())?null:Integer.valueOf(vo.getDeliveryStatus()));
+                param.setDeliveryType(vo.getDeliveryType().isEmpty()?null:vo.getDeliveryType());
+                param.setUpdateTime(DateUtils.getNowDate());
+                fsStoreOrderMapper.updateFsStoreOrder(param);
+
+                successNum++;
+                successMsg.append("<br/>").append(successNum).append("、订单编号 ").append(vo.getOrderCode()).append(" 修改成功");
+
+            } catch (Exception e) {
+
+                failureNum++;
+                String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 修改失败:";
+                failureMsg.append(msg).append(e.getMessage());
+            }
+        }
+        if (failureNum > 0) {
+            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
+            throw new ServiceException(failureMsg.toString());
+        } else {
+            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
+        }
+        return successMsg.toString();
+    }
+
 }
 }

+ 32 - 0
fs-service/src/main/java/com/fs/his/vo/FsStoreOrderStatusExcelVO.java

@@ -0,0 +1,32 @@
+package com.fs.his.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class FsStoreOrderStatusExcelVO {
+
+    @Excel(name = "药品订单号",required = true)
+    private String orderCode;
+
+    @Excel(name = "订单状态",dictType = "sys_order_status")
+    private String status;
+
+
+    /** 物流状态 */
+    @Excel(name = "物流状态",dictType = "sys_store_order_delivery_status")
+    private String deliveryStatus;
+
+    /** 物流跟踪状态 */
+    @Excel(name = "物流跟踪状态",dictType = "sys_delivery_type")
+    private String deliveryType;
+
+    /** shou */
+    @Excel(name = "收货人电话")
+    private String userPhone;
+    /** 详情地址 */
+    @Excel(name = "详情地址")
+    private String userAddress;
+
+
+}

+ 4 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -78,6 +78,10 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
                                                   @Param("corpId") String corpId);
                                                   @Param("corpId") String corpId);
 
 
 
 
+    public int batchUpdateQwExternalContactByMoreStudy(List<QwExternalContact> qwExternalContact);
+    public int batchUpdateQwExternalByIsDaysNotStudy(List<QwExternalContact> qwExternalContact);
+
+
     @Select("SELECT * FROM qw_external_contact WHERE external_user_id = #{externalUserId}  AND corp_id=#{corpId} and user_id=#{qwUserId} limit 1")
     @Select("SELECT * FROM qw_external_contact WHERE external_user_id = #{externalUserId}  AND corp_id=#{corpId} and user_id=#{qwUserId} limit 1")
     public QwExternalContact selectQwExternalContactByExternalUserIdAndQwUserId(@Param("externalUserId") String externalUserId,@Param("corpId") String corpId,@Param("qwUserId") String qwUserId);
     public QwExternalContact selectQwExternalContactByExternalUserIdAndQwUserId(@Param("externalUserId") String externalUserId,@Param("corpId") String corpId,@Param("qwUserId") String qwUserId);
 
 

+ 6 - 0
fs-service/src/main/java/com/fs/sop/domain/SopUserLogsInfo.java

@@ -73,6 +73,12 @@ public class SopUserLogsInfo implements Serializable {
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private String inComingETime;
     private String inComingETime;
 
 
+
+    /**
+     * 评级
+     */
+    private Integer grade;
+
     /**
     /**
      * 营期时间
      * 营期时间
      */
      */

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

@@ -321,7 +321,7 @@ public interface QwSopLogsMapper extends BaseMapper<QwSopLogs> {
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     List<QwSopLogs> selectIpadByCorpId(@Param("corpId") String corpId, @Param("now") LocalDateTime now);
     List<QwSopLogs> selectIpadByCorpId(@Param("corpId") String corpId, @Param("now") LocalDateTime now);
 
 
-    @DataSource(DataSourceType.SopREAD)
+    @DataSource(DataSourceType.SOP)
     List<QwSopLogs> selectByQwUserId(@Param("id") Long id);
     List<QwSopLogs> selectByQwUserId(@Param("id") Long id);
 
 
     @Select("select * from qw_sop_logs where send_type=8 and send_status=3 and  create_time <= DATE_SUB(NOW(), INTERVAL 2 HOUR) ")
     @Select("select * from qw_sop_logs where send_type=8 and send_status=3 and  create_time <= DATE_SUB(NOW(), INTERVAL 2 HOUR) ")

+ 52 - 0
fs-service/src/main/java/com/fs/sop/mapper/SopUserLogsInfoMapper.java

@@ -2,6 +2,7 @@ package com.fs.sop.mapper;
 
 
 import com.fs.common.annotation.DataSource;
 import com.fs.common.annotation.DataSource;
 import com.fs.common.enums.DataSourceType;
 import com.fs.common.enums.DataSourceType;
+import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.params.BatchSopUserLogsInfoParamU;
 import com.fs.sop.params.BatchSopUserLogsInfoParamU;
@@ -148,11 +149,62 @@ public interface SopUserLogsInfoMapper {
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     List<SopUserLogsInfo> selectSopUserLogsInfoListBySopId(@Param("sopId") String sopId, @Param("userLogsId") String userLogsId);
     List<SopUserLogsInfo> selectSopUserLogsInfoListBySopId(@Param("sopId") String sopId, @Param("userLogsId") String userLogsId);
 
 
+    @DataSource(DataSourceType.SOP)
+    List<SopUserLogsInfo> selectRestoreByIsDaysNotStudy(@Param("sopId") String sopId, @Param("userLogsId") String userLogsId);
+
 
 
 
 
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     void batchInsertSopUserLogsInfo(@Param("SopUserLogsInfo") List<SopUserLogsInfo> logsToInsert);
     void batchInsertSopUserLogsInfo(@Param("SopUserLogsInfo") List<SopUserLogsInfo> logsToInsert);
 
 
+
+    @DataSource(DataSourceType.SOP)
+    @Update("<script>" +
+            "UPDATE sop_user_logs_info " +
+            "SET is_days_not_study = CASE " +
+            "<foreach collection='contactList' item='item'> " +
+            "    WHEN external_id = #{item.id} THEN " +
+            "    CASE WHEN #{item.level} = 5 AND #{item.isDaysNotStudy}=1 THEN 1 ELSE 0 END " +
+            "</foreach> " +
+            "ELSE is_days_not_study " +
+            "END " +
+            "WHERE external_id IN " +
+            "<foreach collection='contactList' item='item' open='(' separator=',' close=')'> " +
+            "    #{item.id} " +
+            "</foreach>" +
+            "</script>")
+    void batchUpdateSopUserLogsInfoByMoreStudy(@Param("contactList") List<QwExternalContact> contactList);
+
+    @DataSource(DataSourceType.SOP)
+    @Update("<script>" +
+            "UPDATE sop_user_logs_info " +
+            "SET is_days_not_study = 0 " +
+            "WHERE external_id IN " +
+            "<foreach collection='contactList' item='item' open='(' separator=',' close=')'>" +
+            "    #{item.id} " +
+            "</foreach>" +
+            "</script>")
+    void batchUpdateSopUserLogsInfoByIsDaysNotStudy(@Param("contactList") List<QwExternalContact> contactList);
+
+    @DataSource(DataSourceType.SOP)
+    @Update("<script>" +
+            "UPDATE sop_user_logs_info " +
+            "SET grade = CASE external_id " +
+            "<foreach collection=\"contactList\" item=\"item\">" +
+            "    WHEN #{item.id} THEN #{item.level} " +
+            "</foreach>" +
+            "    ELSE grade " +
+            "END " +
+            "WHERE external_id IN " +
+            "<foreach collection='contactList' item='item' open='(' separator=',' close=')'>" +
+            "    #{item.id} " +
+            "</foreach>" +
+            "</script>")
+    void batchUpdateSopUserLogsInfoByLevel(@Param("contactList") List<QwExternalContact> contactList);
+
+
+
+
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     void batchUpdateSopUserLogsInfoToTime(BatchSopUserLogsInfoParamU paramU);
     void batchUpdateSopUserLogsInfoToTime(BatchSopUserLogsInfoParamU paramU);
 
 

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

@@ -64,6 +64,13 @@ public interface SopUserLogsMapper {
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     public List<SopUserLogs> meetsTheRatingByUserInfoWithPagination(@Param("offset") int offset,@Param("pageSize") int pageSize);
     public List<SopUserLogs> meetsTheRatingByUserInfoWithPagination(@Param("offset") int offset,@Param("pageSize") int pageSize);
 
 
+    @DataSource(DataSourceType.SOP)
+    public List<SopUserLogs> meetsTheRatingByUserInfoWithPaginationStudyDays(@Param("offset") int offset,@Param("pageSize") int pageSize,@Param("notStudyDays") Integer notStudyDays);
+
+    @DataSource(DataSourceType.SOP)
+    public List<SopUserLogs> meetsTherestoreByIsDaysNotStudy(@Param("offset") int offset,@Param("pageSize") int pageSize,@Param("notStudyDays") Integer notStudyDays);
+
+
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     public List<SopUserLogs> meetsTheRatingByUserInfoBySopId(@Param("sopId") String sopId);
     public List<SopUserLogs> meetsTheRatingByUserInfoBySopId(@Param("sopId") String sopId);
 
 

+ 1 - 0
fs-service/src/main/java/com/fs/sop/params/QwRatingConfig.java

@@ -5,6 +5,7 @@ import lombok.Data;
 @Data
 @Data
 public class QwRatingConfig {
 public class QwRatingConfig {
     private Integer levelDay;
     private Integer levelDay;
+    private Integer notStudyDays;
     private Integer aLevelMin;
     private Integer aLevelMin;
     private Integer aLevelMax;
     private Integer aLevelMax;
     private Integer bLevelMin;
     private Integer bLevelMin;

+ 8 - 0
fs-service/src/main/java/com/fs/sop/service/ISopUserLogsInfoService.java

@@ -1,6 +1,7 @@
 package com.fs.sop.service;
 package com.fs.sop.service;
 
 
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
+import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.params.BatchSopUserLogsInfoParam;
 import com.fs.sop.params.BatchSopUserLogsInfoParam;
@@ -54,6 +55,11 @@ public interface ISopUserLogsInfoService {
     List<SopUserLogsInfoVOE> selectSopUserLogsInfoListVO(SopUserLogsInfo info);
     List<SopUserLogsInfoVOE> selectSopUserLogsInfoListVO(SopUserLogsInfo info);
 
 
     void batchInsertSopUserLogsInfo(List<SopUserLogsInfo> logsToInsert);
     void batchInsertSopUserLogsInfo(List<SopUserLogsInfo> logsToInsert);
+
+    void batchUpdateSopUserLogsInfoByMoreStudy(List<QwExternalContact> contactList);
+    void batchUpdateSopUserLogsInfoByIsDaysNotStudy(List<QwExternalContact> contactList);
+    void batchUpdateSopUserLogsInfoByLevel(List<QwExternalContact> contactList);
+
     void insertSopUserLogsInfo(SopUserLogsInfo logsToInsert);
     void insertSopUserLogsInfo(SopUserLogsInfo logsToInsert);
     /**
     /**
      * 查询sopUserLogsInfo
      * 查询sopUserLogsInfo
@@ -74,5 +80,7 @@ public interface ISopUserLogsInfoService {
     public R sendUserLogsInfoMsg(SendUserLogsInfoMsgParam param);
     public R sendUserLogsInfoMsg(SendUserLogsInfoMsgParam param);
     public R sendUserLogsInfoMsgType(SendUserLogsInfoMsgParam param);
     public R sendUserLogsInfoMsgType(SendUserLogsInfoMsgParam param);
 
 
+    List<SopUserLogsInfo> selectRestoreByIsDaysNotStudy(String sopId, String userLogsId);
+
     public List<ExtCourseSopWatchLogVO> getExtCourseSopWatchLog(QwExtCourseSopWatchLog qwExternalContactId);
     public List<ExtCourseSopWatchLogVO> getExtCourseSopWatchLog(QwExtCourseSopWatchLog qwExternalContactId);
 }
 }

+ 3 - 0
fs-service/src/main/java/com/fs/sop/service/ISopUserLogsService.java

@@ -61,4 +61,7 @@ public interface ISopUserLogsService {
     void updateLogDate(UpdateSopUserLogDateVo vo);
     void updateLogDate(UpdateSopUserLogDateVo vo);
 
 
     void addGroupChat(AddSopUserGroupChat vo);
     void addGroupChat(AddSopUserGroupChat vo);
+
+    public List<SopUserLogs> meetsTherestoreByIsDaysNotStudy(int offset,int pageSize,Integer notStudyDays);
+
 }
 }

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

@@ -59,6 +59,7 @@ import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Function;
 import java.util.function.Function;
 import java.util.stream.Collector;
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
@@ -571,135 +572,375 @@ public class QwSopLogsServiceImpl extends ServiceImpl<QwSopLogsMapper, QwSopLogs
     @Override
     @Override
     public void qwSopLogsResultNew() {
     public void qwSopLogsResultNew() {
 
 
+
         logger.info("开始执行企业微信群发消息结果查询任务");
         logger.info("开始执行企业微信群发消息结果查询任务");
         long startTime = System.currentTimeMillis();
         long startTime = System.currentTimeMillis();
 
 
         List<QwSopLogs> qwSopLogsList = qwSopLogsMapper.selectSopLogsByCreateCorpMassSendResult();
         List<QwSopLogs> qwSopLogsList = qwSopLogsMapper.selectSopLogsByCreateCorpMassSendResult();
         if (qwSopLogsList.isEmpty()) {
         if (qwSopLogsList.isEmpty()) {
-            logger.info("没有需要查询结果的群发消息记录");
-            return;
+            return ;
         }
         }
 
 
-
+        // 分组处理
         Map<String, List<QwSopLogs>> grouped = qwSopLogsList.stream().collect(
         Map<String, List<QwSopLogs>> grouped = qwSopLogsList.stream().collect(
                 Collectors.groupingBy(log -> log.getQwUserid() + "|" + log.getCorpId() + "|" + log.getMsgId())
                 Collectors.groupingBy(log -> log.getQwUserid() + "|" + log.getCorpId() + "|" + log.getMsgId())
         );
         );
-        for (Map.Entry<String, List<QwSopLogs>> entry : grouped.entrySet()) {
-            String key = entry.getKey();
-            List<QwSopLogs> corpLogs = entry.getValue();
 
 
-            String[] keys = key.split("\\|");
-            String qwUserid = keys[0];
-            String corpId = keys[1];
-            String msgID = keys[2];
+        // 线程安全的数据结构
+        List<QwSopLogs> allUpdates = Collections.synchronizedList(new ArrayList<>());
+        Queue<Map.Entry<String, List<QwSopLogs>>> taskQueue = new ConcurrentLinkedQueue<>(grouped.entrySet());
+        Queue<Map.Entry<String, List<QwSopLogs>>> apiFailedTasks = new ConcurrentLinkedQueue<>();
+        ExecutorService executor = Executors.newFixedThreadPool(10);
+
+        AtomicInteger totalGroupsProcessed = new AtomicInteger(0); // 处理的分组数
+        AtomicInteger totalRecordsUpdated = new AtomicInteger(0);  // 更新的记录数
+
+        int batchSize = 300; // 每300个组批量更新一次
+        int maxRetries = 3;   // 最大重试次数
+
+        // 处理所有任务(包括重试)
+        for (int retryCount = 0; retryCount <= maxRetries; retryCount++) {
+            int currentBatchCount = 0;
+
+            while (!taskQueue.isEmpty()) {
+                int currentBatchSize = Math.min(batchSize - currentBatchCount, taskQueue.size());
+                CountDownLatch batchLatch = new CountDownLatch(currentBatchSize);
+
+                // 处理当前批次任务
+                for (int i = 0; i < currentBatchSize; i++) {
+                    Map.Entry<String, List<QwSopLogs>> entry = taskQueue.poll();
+                    executor.submit(() -> {
+                        try {
+                            // 处理单个分组(传入apiFailedTasks)
+                            List<QwSopLogs> updates = processSingleGroup(entry, apiFailedTasks);
+                            if (updates != null) {
+                                synchronized (allUpdates) {
+                                    allUpdates.addAll(updates);
+                                }
+                                // 统计更新记录数
+                                totalRecordsUpdated.addAndGet(updates.size());
+                            }
+
+                            // 统计处理的分组数
+                            totalGroupsProcessed.incrementAndGet();
+                        } finally {
+                            batchLatch.countDown();
+                        }
+                    });
+                }
+
+                // 等待当前批次完成
+                try {
+                    batchLatch.await();
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+
+                currentBatchCount += currentBatchSize;
 
 
-            QwGetGroupmsgSendParam param = new QwGetGroupmsgSendParam();
-            param.setMsgid(msgID);
-            param.setUserid(qwUserid);
-            param.setLimit(1000);
+                // 每处理1000个组或任务队列空时更新数据库
+                if (currentBatchCount >= batchSize || taskQueue.isEmpty()) {
+                    synchronized (allUpdates) {
+                        if (!allUpdates.isEmpty()) {
+                            batchUpdateDatabase(new ArrayList<>(allUpdates));
+                            logger.info("每处理300个组或任务队列空时更新数据库:"+new ArrayList<>(allUpdates).size());
+                            allUpdates.clear();
+                        }
 
 
-            fetchAndProcessAllPages(param, corpId, corpLogs, msgID);
+                    }
+                    currentBatchCount = 0;
+                }
+            }
 
 
+            // 准备下一轮重试
+            if (retryCount < maxRetries) {
+                taskQueue.addAll(apiFailedTasks);
+                apiFailedTasks.clear();
+            }
         }
         }
 
 
-        long endTime = System.currentTimeMillis();
-        logger.info("企业微信群发消息结果查询任务完成,处理记录总数: {},总耗时: {}ms",
-                qwSopLogsList.size(), (endTime - startTime));
+        // 最终更新剩余数据
+        synchronized (allUpdates) {
+            if (!allUpdates.isEmpty()) {
+                logger.info("最终更新剩余数据:"+new ArrayList<>(allUpdates).size());
+                batchUpdateDatabase(new ArrayList<>(allUpdates));
+            }
+        }
+
+        executor.shutdown();
+        try {
+            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
+                executor.shutdownNow();
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
 
 
+        long endTime = System.currentTimeMillis();
+        logger.info("成功处理分组数: " + totalGroupsProcessed.get());
+        logger.info("企业微信群发消息结果查询任务完成-更新记录总数:{},总耗时:{} " , totalRecordsUpdated.get(),(endTime - startTime));
     }
     }
 
 
-    private void fetchAndProcessAllPages(QwGetGroupmsgSendParam param, String corpId, List<QwSopLogs> logs, String msgId) {
+    private List<QwSopLogs> processSingleGroup(Map.Entry<String, List<QwSopLogs>> entry, Queue<Map.Entry<String, List<QwSopLogs>>> apiFailedTasks) {
+
+        String key = entry.getKey();
+        List<QwSopLogs> corpLogs = entry.getValue();
+        String[] keys = key.split("\\|");
+        String qwUserid = keys[0];
+        String corpId = keys[1];
+        String msgID = keys[2];
+
+        QwGetGroupmsgSendParam param = new QwGetGroupmsgSendParam();
+        param.setMsgid(msgID);
+        param.setUserid(qwUserid);
+        param.setLimit(1000);
+
+        List<QwSopLogs> groupUpdates = new ArrayList<>();
         String nextCursor = null;
         String nextCursor = null;
+        boolean apiSuccess = true;
 
 
         do {
         do {
             param.setCursor(nextCursor);
             param.setCursor(nextCursor);
+            QwGroupmsgSendResult result = fetchWithRetry(param, corpId); // 重试3次
+
+            // API调用失败处理
+            if (result == null || result.getErrCode() != 0) {
+                apiSuccess = false;
+                break;
+            }
+
+            // 处理当前页结果
+            List<QwSopLogs> pageUpdates = processPageResult(result, corpLogs, corpId, msgID);
+            groupUpdates.addAll(pageUpdates);
+            nextCursor = result.getNextCursor();
+
+        } while (nextCursor != null && !nextCursor.isEmpty());
+
+        // API调用失败时返回null,将任务加入失败队列
+        if (!apiSuccess) {
+            apiFailedTasks.offer(entry);
+            return null;
+        }
+
+        return groupUpdates;
+    }
+
+    private QwGroupmsgSendResult fetchWithRetry(QwGetGroupmsgSendParam param, String corpId) {
+        int retryCount = 0;
+        while (retryCount <= 3) {
             QwGroupmsgSendResult result = qwApiService.getGroupmsgSendResult(param, corpId);
             QwGroupmsgSendResult result = qwApiService.getGroupmsgSendResult(param, corpId);
 
 
+            // 请求失败情况
             if (result == null) {
             if (result == null) {
-                logger.error("接口调用失败: {}", param);
-                return;
+                retryCount++;
+                sleepWithJitter(2000);
+                continue;
             }
             }
 
 
-            if (result.getErrCode() == 45033) {
-                try {
-                    Thread.sleep(2000 + new Random().nextInt(1000));
-                    result = qwApiService.getGroupmsgSendResult(param, corpId);
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                    logger.error("线程中断", e);
-                    return;
-                }
+            // 成功情况
+            if (result.getErrCode() == 0) {
+                return result;
             }
             }
 
 
-            if (result.getErrCode() != 0) {
-                logger.error("查询失败: {}, errCode: {}, errMsg: {}", param, result.getErrCode(), result.getErrMsg());
-                return;
+            // 需要重试的错误码
+            if (result.getErrCode() == 45033 || result.getErrCode() == -1) {
+                retryCount++;
+                sleepWithJitter(2000 + new Random().nextInt(1000));
+                continue;
             }
             }
 
 
-            processPageResult(result, logs, corpId, msgId);
-            nextCursor = result.getNextCursor();
-
-        } while (nextCursor != null && !nextCursor.isEmpty());
+            // 其他错误码不重试
+            return result;
+        }
+        return null; // 超过重试次数
     }
     }
 
 
+    private void sleepWithJitter(int baseMillis) {
+        try {
+            int sleepTime = baseMillis + (new Random().nextInt(1000));
+            Thread.sleep(sleepTime);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
 
 
-    private void processPageResult(QwGroupmsgSendResult result, List<QwSopLogs> logs, String corpId, String msgId) {
+    private List<QwSopLogs> processPageResult(QwGroupmsgSendResult result, List<QwSopLogs> logs, String corpId, String msgId) {
         Map<String, SendItemResult> sendMap = result.getSendList().stream()
         Map<String, SendItemResult> sendMap = result.getSendList().stream()
                 .collect(Collectors.toMap(
                 .collect(Collectors.toMap(
                         r -> r.getUserId() + "_" + r.getExternalUserId() + "_" + corpId + "_" + msgId,
                         r -> r.getUserId() + "_" + r.getExternalUserId() + "_" + corpId + "_" + msgId,
                         Function.identity(),
                         Function.identity(),
-                        (a, b) -> a  // 如果重复,保留第一个
+                        (a, b) -> a
                 ));
                 ));
 
 
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
         String now = LocalDateTime.now().format(formatter);
         String now = LocalDateTime.now().format(formatter);
-
-        // 只处理匹配得上的记录
         List<QwSopLogs> matchedLogs = new ArrayList<>();
         List<QwSopLogs> matchedLogs = new ArrayList<>();
 
 
         for (QwSopLogs log : logs) {
         for (QwSopLogs log : logs) {
             String logKey = log.getQwUserid() + "_" + log.getExternalUserId() + "_" + log.getCorpId() + "_" + msgId;
             String logKey = log.getQwUserid() + "_" + log.getExternalUserId() + "_" + log.getCorpId() + "_" + msgId;
             SendItemResult matched = sendMap.get(logKey);
             SendItemResult matched = sendMap.get(logKey);
-
-            if (matched != null) {
-
-                switch (matched.getStatus()) {
-                    case 0:
-                        log.setSendStatus(5L);
-                        log.setRemark("员工未在规定时间发送");
-                        break;
-                    case 1:
-                        log.setSendStatus(1L);
-                        log.setReceivingStatus(1L);
-                        break;
-                    case 2:
-                        log.setSendType(2);
-                        log.setSendStatus(3L);
-                        log.setRemark("因客户不是好友导致发送失败,补发");
-                        log.setReceivingStatus(0L);
-                        log.setSendTime(now);
-                        log.setSort(30000001);
-                    case 3:
-                        log.setSendType(2);
-                        log.setSendStatus(3L);
-                        log.setRemark("客户已经收到其他群发消息,补发");
-                        log.setReceivingStatus(0L);
-                        log.setSendTime(now);
-                        log.setSort(30000001);
-                        break;
-                    default:
-                        break;
-                }
-
-                matchedLogs.add(log);
+            if (matched == null) continue;
+
+            switch (matched.getStatus()) {
+                case 0:
+                    log.setSendStatus(5L);
+                    log.setRemark("员工未在规定时间发送");
+                    break;
+                case 1:
+                    log.setSendStatus(1L);
+                    log.setReceivingStatus(1L);
+                    break;
+                case 2:
+                    log.setSendType(2);
+                    log.setSendStatus(3L);
+                    log.setRemark("因客户不是好友导致发送失败,补发");
+                    log.setReceivingStatus(0L);
+                    log.setSendTime(now);
+                    log.setSort(30000001);
+                    break;
+                case 3:
+                    log.setSendType(2);
+                    log.setSendStatus(3L);
+                    log.setRemark("客户已经收到其他群发消息,补发");
+                    log.setReceivingStatus(0L);
+                    log.setSendTime(now);
+                    log.setSort(30000001);
+                    break;
             }
             }
+            matchedLogs.add(log);
         }
         }
-
-        if (!matchedLogs.isEmpty()) {
-            batchUpdateDatabase(matchedLogs);
-        }
+        return matchedLogs;
     }
     }
 
 
+//    @Override
+//    public void qwSopLogsResultNew() {
+//
+//        logger.info("开始执行企业微信群发消息结果查询任务");
+//        long startTime = System.currentTimeMillis();
+//
+//        List<QwSopLogs> qwSopLogsList = qwSopLogsMapper.selectSopLogsByCreateCorpMassSendResult();
+//        if (qwSopLogsList.isEmpty()) {
+//            logger.info("没有需要查询结果的群发消息记录");
+//            return;
+//        }
+//
+//
+//        Map<String, List<QwSopLogs>> grouped = qwSopLogsList.stream().collect(
+//                Collectors.groupingBy(log -> log.getQwUserid() + "|" + log.getCorpId() + "|" + log.getMsgId())
+//        );
+//        for (Map.Entry<String, List<QwSopLogs>> entry : grouped.entrySet()) {
+//            String key = entry.getKey();
+//            List<QwSopLogs> corpLogs = entry.getValue();
+//
+//            String[] keys = key.split("\\|");
+//            String qwUserid = keys[0];
+//            String corpId = keys[1];
+//            String msgID = keys[2];
+//
+//            QwGetGroupmsgSendParam param = new QwGetGroupmsgSendParam();
+//            param.setMsgid(msgID);
+//            param.setUserid(qwUserid);
+//            param.setLimit(1000);
+//
+//            fetchAndProcessAllPages(param, corpId, corpLogs, msgID);
+//
+//        }
+//
+//        long endTime = System.currentTimeMillis();
+//        logger.info("企业微信群发消息结果查询任务完成,处理记录总数: {},总耗时: {}ms",
+//                qwSopLogsList.size(), (endTime - startTime));
+//
+//    }
+//    private void fetchAndProcessAllPages(QwGetGroupmsgSendParam param, String corpId, List<QwSopLogs> logs, String msgId) {
+//        String nextCursor = null;
+//
+//        do {
+//            param.setCursor(nextCursor);
+//            QwGroupmsgSendResult result = qwApiService.getGroupmsgSendResult(param, corpId);
+//
+//            if (result == null) {
+//                logger.error("接口调用失败: {}", param);
+//                return;
+//            }
+//
+//            if (result.getErrCode() == 45033) {
+//                try {
+//                    Thread.sleep(2000 + new Random().nextInt(1000));
+//                    result = qwApiService.getGroupmsgSendResult(param, corpId);
+//                } catch (InterruptedException e) {
+//                    Thread.currentThread().interrupt();
+//                    logger.error("线程中断", e);
+//                    return;
+//                }
+//            }
+//
+//            if (result.getErrCode() != 0) {
+//                logger.error("查询失败: {}, errCode: {}, errMsg: {}", param, result.getErrCode(), result.getErrMsg());
+//                return;
+//            }
+//
+//            processPageResult(result, logs, corpId, msgId);
+//            nextCursor = result.getNextCursor();
+//
+//        } while (nextCursor != null && !nextCursor.isEmpty());
+//    }
+//    private void processPageResult(QwGroupmsgSendResult result, List<QwSopLogs> logs, String corpId, String msgId) {
+//        Map<String, SendItemResult> sendMap = result.getSendList().stream()
+//                .collect(Collectors.toMap(
+//                        r -> r.getUserId() + "_" + r.getExternalUserId() + "_" + corpId + "_" + msgId,
+//                        Function.identity(),
+//                        (a, b) -> a  // 如果重复,保留第一个
+//                ));
+//
+//        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+//        String now = LocalDateTime.now().format(formatter);
+//
+//        // 只处理匹配得上的记录
+//        List<QwSopLogs> matchedLogs = new ArrayList<>();
+//
+//        for (QwSopLogs log : logs) {
+//            String logKey = log.getQwUserid() + "_" + log.getExternalUserId() + "_" + log.getCorpId() + "_" + msgId;
+//            SendItemResult matched = sendMap.get(logKey);
+//
+//            if (matched != null) {
+//
+//                switch (matched.getStatus()) {
+//                    case 0:
+//                        log.setSendStatus(5L);
+//                        log.setRemark("员工未在规定时间发送");
+//                        break;
+//                    case 1:
+//                        log.setSendStatus(1L);
+//                        log.setReceivingStatus(1L);
+//                        break;
+//                    case 2:
+//                        log.setSendType(2);
+//                        log.setSendStatus(3L);
+//                        log.setRemark("因客户不是好友导致发送失败,补发");
+//                        log.setReceivingStatus(0L);
+//                        log.setSendTime(now);
+//                        log.setSort(30000001);
+//                    case 3:
+//                        log.setSendType(2);
+//                        log.setSendStatus(3L);
+//                        log.setRemark("客户已经收到其他群发消息,补发");
+//                        log.setReceivingStatus(0L);
+//                        log.setSendTime(now);
+//                        log.setSort(30000001);
+//                        break;
+//                    default:
+//                        break;
+//                }
+//
+//                matchedLogs.add(log);
+//            }
+//        }
+//
+//        if (!matchedLogs.isEmpty()) {
+//            batchUpdateDatabase(matchedLogs);
+//        }
+//    }
+
 
 
     /**
     /**
      * 批量更新数据库
      * 批量更新数据库

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

@@ -3,12 +3,15 @@ package com.fs.sop.service.impl;
 import cn.hutool.json.JSONUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONArray;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.PubFun;
 import com.fs.common.utils.PubFun;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.date.DateUtil;
 import com.fs.common.utils.date.DateUtil;
+import com.fs.company.domain.CompanyMiniapp;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.company.service.ICompanyMiniappService;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseDomainName;
 import com.fs.course.domain.FsCourseDomainName;
 import com.fs.course.domain.FsCourseLink;
 import com.fs.course.domain.FsCourseLink;
@@ -153,6 +156,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
     @Autowired
     @Autowired
     private QwGroupChatUserMapper qwGroupChatUserMapper;
     private QwGroupChatUserMapper qwGroupChatUserMapper;
 
 
+    @Autowired
+    private ICompanyMiniappService companyMiniappService;
+
     @Override
     @Override
     public void save(SopUserLogsInfo sopUserLogsInfo) {
     public void save(SopUserLogsInfo sopUserLogsInfo) {
         sopUserLogsInfoMapper.insertSopUserLogsInfo(sopUserLogsInfo);
         sopUserLogsInfoMapper.insertSopUserLogsInfo(sopUserLogsInfo);
@@ -218,6 +224,21 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
         sopUserLogsInfoMapper.batchInsertSopUserLogsInfo(logsToInsert);
         sopUserLogsInfoMapper.batchInsertSopUserLogsInfo(logsToInsert);
     }
     }
 
 
+    @Override
+    public void batchUpdateSopUserLogsInfoByMoreStudy(List<QwExternalContact> contactList) {
+        sopUserLogsInfoMapper.batchUpdateSopUserLogsInfoByMoreStudy(contactList);
+    }
+
+    @Override
+    public void batchUpdateSopUserLogsInfoByIsDaysNotStudy(List<QwExternalContact> contactList) {
+        sopUserLogsInfoMapper.batchUpdateSopUserLogsInfoByIsDaysNotStudy(contactList);
+    }
+
+    @Override
+    public void batchUpdateSopUserLogsInfoByLevel(List<QwExternalContact> contactList) {
+        sopUserLogsInfoMapper.batchUpdateSopUserLogsInfoByLevel(contactList);
+    }
+
     @Override
     @Override
     public void insertSopUserLogsInfo(SopUserLogsInfo logsToInsert) {
     public void insertSopUserLogsInfo(SopUserLogsInfo logsToInsert) {
         sopUserLogsInfoMapper.insertSopUserLogsInfo(logsToInsert);
         sopUserLogsInfoMapper.insertSopUserLogsInfo(logsToInsert);
@@ -698,6 +719,14 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 }).collect(Collectors.toList());
                 }).collect(Collectors.toList());
             }
             }
         }else{
         }else{
+
+
+            // 查询公司关联小程序数据
+            List<CompanyMiniapp> miniList = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().orderByAsc("sort_num"));
+
+            Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniList.stream().collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId, Collectors.groupingBy(CompanyMiniapp::getType)));
+
+
             sopLogsList = new ArrayList<>();
             sopLogsList = new ArrayList<>();
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoByIds(param.getIds());
             List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoByIds(param.getIds());
@@ -810,12 +839,28 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                             String linkByMiniApp = createLinkByMiniApp(st, param.getCorpId(), createTime, param.getCourseId(), param.getVideoId(),
                             String linkByMiniApp = createLinkByMiniApp(st, param.getCorpId(), createTime, param.getCourseId(), param.getVideoId(),
                                     qwUserId, companyUserId, companyId, item.getExternalId(), config);
                                     qwUserId, companyUserId, companyId, item.getExternalId(), config);
 
 
-                            if (StringUtil.strIsNullOrEmpty(qwCompany.getMiniAppId())){
-                                log.error("企业未配置小程序-"+param.getCorpId());
-                            }else {
-                                //置换各自的小程序
+                            if (!miniMap.isEmpty() && qwUser.getSendMsgType() == 1) {
+                                Map<Integer, List<CompanyMiniapp>> integerListMap = miniMap.get(Long.valueOf(companyId));
+                                if (integerListMap != null) {
+
+                                    int effectiveGrade = (item.getGrade() == null) ? 5 : item.getGrade();
+                                    int listIndex = (effectiveGrade == 1 || effectiveGrade == 2) ? 0 : 1;
+                                    List<CompanyMiniapp> miniapps = integerListMap.get(listIndex);
+
+                                    if (miniapps != null && !miniapps.isEmpty()) {
+                                        CompanyMiniapp companyMiniapp = miniapps.get(0);
+                                        if (companyMiniapp != null && !StringUtil.strIsNullOrEmpty(companyMiniapp.getAppId())) {
+                                            st.setMiniprogramAppid(companyMiniapp.getAppId());
+                                        }
+                                    }
+                                }
+                            } else if (!StringUtil.strIsNullOrEmpty(qwCompany.getMiniAppId())) {
                                 st.setMiniprogramAppid(qwCompany.getMiniAppId());
                                 st.setMiniprogramAppid(qwCompany.getMiniAppId());
+                            } else {
+                                log.error("公司的小程序id为空:采用了前端传的固定值" + sopLogs.getSopId());
                             }
                             }
+
+
                             st.setMiniprogramPage(linkByMiniApp);
                             st.setMiniprogramPage(linkByMiniApp);
 
 
                             break;
                             break;
@@ -874,6 +919,11 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
         return null;
         return null;
     }
     }
 
 
+    @Override
+    public List<SopUserLogsInfo> selectRestoreByIsDaysNotStudy(String sopId, String userLogsId) {
+        return sopUserLogsInfoMapper.selectRestoreByIsDaysNotStudy(sopId, userLogsId);
+    }
+
     @Override
     @Override
     public List<ExtCourseSopWatchLogVO> getExtCourseSopWatchLog(QwExtCourseSopWatchLog qwExternalContactId) {
     public List<ExtCourseSopWatchLogVO> getExtCourseSopWatchLog(QwExtCourseSopWatchLog qwExternalContactId) {
 
 
@@ -915,6 +965,9 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
 
         List<SopUserLogsInfo> sopUserLogs = sopUserLogsMapper.selectSopUserLogsByIds(param.getIds());
         List<SopUserLogsInfo> sopUserLogs = sopUserLogsMapper.selectSopUserLogsByIds(param.getIds());
 
 
+        // 查询公司关联小程序数据
+        List<CompanyMiniapp> miniList = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().orderByAsc("sort_num"));
+        Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniList.stream().collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId, Collectors.groupingBy(CompanyMiniapp::getType)));
 
 
         //排序
         //排序
         int sort = 0;
         int sort = 0;
@@ -955,7 +1008,8 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 log.error("员工信息用户不存在:" + key.getKey()+",企业id:"+key.getValue());
                 log.error("员工信息用户不存在:" + key.getKey()+",企业id:"+key.getValue());
             }else {
             }else {
 
 
-                List<QwSopLogs> sopLogsList = processInsertSopUserLogsInfo(logs, qwUser, param, words, config, qwCompany, finalSort, finalSendType);
+                List<QwSopLogs> sopLogsList = processInsertSopUserLogsInfo(logs, qwUser, param, words, config, qwCompany, finalSort,
+                        finalSendType,miniMap );
 
 
                 //批量插入 发送记录
                 //批量插入 发送记录
                 if (!sopLogsList.isEmpty()) {
                 if (!sopLogsList.isEmpty()) {
@@ -972,7 +1026,8 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
 
     private List<QwSopLogs> processInsertSopUserLogsInfo(List<SopUserLogsInfo> sopUserLogsInfos,QwUser qwUser,
     private List<QwSopLogs> processInsertSopUserLogsInfo(List<SopUserLogsInfo> sopUserLogsInfos,QwUser qwUser,
                                                          SendUserLogsInfoMsgParam param,List<FastGptChatReplaceWords> words,
                                                          SendUserLogsInfoMsgParam param,List<FastGptChatReplaceWords> words,
-                                                         CourseConfig config,QwCompany qwCompany,int finalSort,int finalSendType){
+                                                         CourseConfig config,QwCompany qwCompany,int finalSort,int finalSendType,
+                                                         Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap){
 
 
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
 
@@ -1026,7 +1081,8 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 
 
             switch (finalSendType){
             switch (finalSendType){
                 case 5:
                 case 5:
-                    List<QwSopCourseFinishTempSetting.Setting> list = processSetting(item,qwUser, param, words, config, qwCompany,companyUserId,companyId,contact,dataTime, finalDomainName);
+                    List<QwSopCourseFinishTempSetting.Setting> list = processSetting(item,qwUser, param, words, config, qwCompany,companyUserId,companyId,
+                            contact,dataTime, finalDomainName,miniMap);
                     setting.setSetting(list);
                     setting.setSetting(list);
                     break;
                     break;
                 case 9:
                 case 9:
@@ -1088,7 +1144,8 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
     private List<QwSopCourseFinishTempSetting.Setting> processSetting(SopUserLogsInfo item, QwUser qwUser,
     private List<QwSopCourseFinishTempSetting.Setting> processSetting(SopUserLogsInfo item, QwUser qwUser,
                                                                       SendUserLogsInfoMsgParam param,List<FastGptChatReplaceWords> words,
                                                                       SendUserLogsInfoMsgParam param,List<FastGptChatReplaceWords> words,
                                                                       CourseConfig config,QwCompany qwCompany,String companyUserId, String companyId,
                                                                       CourseConfig config,QwCompany qwCompany,String companyUserId, String companyId,
-                                                                      QwExternalContact contact,Date dataTime,String domainName){
+                                                                      QwExternalContact contact,Date dataTime,String domainName,
+                                                                      Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap){
         List<QwSopCourseFinishTempSetting.Setting> list = JSONArray.parseArray(param.getSetting(),QwSopCourseFinishTempSetting.Setting.class);
         List<QwSopCourseFinishTempSetting.Setting> list = JSONArray.parseArray(param.getSetting(),QwSopCourseFinishTempSetting.Setting.class);
 
 
         for (QwSopCourseFinishTempSetting.Setting st : list) {
         for (QwSopCourseFinishTempSetting.Setting st : list) {
@@ -1158,13 +1215,27 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
 //                    }else {
 //                    }else {
 //                        st.setMiniprogramAppid(config.getMiniprogramAppid());
 //                        st.setMiniprogramAppid(config.getMiniprogramAppid());
 //                    }
 //                    }
-                    if (StringUtil.strIsNullOrEmpty(qwCompany.getMiniAppId())){
-                        log.error("企业未配置小程序-"+param.getCorpId());
-
-                    }else {
-                        //置换各自的小程序
+                    if (!miniMap.isEmpty() && qwUser.getSendMsgType() == 1) {
+                        Map<Integer, List<CompanyMiniapp>> integerListMap = miniMap.get(Long.valueOf(companyId));
+                        if (integerListMap != null) {
+
+                            int effectiveGrade = (item.getGrade() == null) ? 5 : item.getGrade();
+                            int listIndex = (effectiveGrade == 1 || effectiveGrade == 2) ? 0 : 1;
+                            List<CompanyMiniapp> miniapps = integerListMap.get(listIndex);
+
+                            if (miniapps != null && !miniapps.isEmpty()) {
+                                CompanyMiniapp companyMiniapp = miniapps.get(0);
+                                if (companyMiniapp != null && !StringUtil.strIsNullOrEmpty(companyMiniapp.getAppId())) {
+                                    st.setMiniprogramAppid(companyMiniapp.getAppId());
+                                }
+                            }
+                        }
+                    } else if (!StringUtil.strIsNullOrEmpty(qwCompany.getMiniAppId())) {
                         st.setMiniprogramAppid(qwCompany.getMiniAppId());
                         st.setMiniprogramAppid(qwCompany.getMiniAppId());
+                    } else {
+                        log.error("企业未配置小程序-" + param.getCorpId());
                     }
                     }
+
                     st.setMiniprogramPage(linkByMiniApp);
                     st.setMiniprogramPage(linkByMiniApp);
                     break;
                     break;
                 default:
                 default:

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

@@ -955,6 +955,11 @@ public class SopUserLogsServiceImpl  implements ISopUserLogsService {
         sopUserLogsMapper.batchInsertSopUserLogs(list);
         sopUserLogsMapper.batchInsertSopUserLogs(list);
     }
     }
 
 
+    @Override
+    public List<SopUserLogs> meetsTherestoreByIsDaysNotStudy(int offset, int pageSize, Integer notStudyDays) {
+        return sopUserLogsMapper.meetsTherestoreByIsDaysNotStudy(offset,pageSize,notStudyDays);
+    }
+
     //批量更新
     //批量更新
     private void batchUpdateQwExternalContact(List<QwExternalContact> notInExternalUseridList) {
     private void batchUpdateQwExternalContact(List<QwExternalContact> notInExternalUseridList) {
         // 定义批量插入的大小
         // 定义批量插入的大小

+ 1 - 1
fs-service/src/main/java/com/fs/sop/vo/QwRatingVO.java

@@ -16,7 +16,7 @@ public class QwRatingVO {
     */
     */
     private Integer allDuration;
     private Integer allDuration;
 
 
-
+    private Integer level;
 
 
     /**
     /**
      * 1升 2降 3未变动
      * 1升 2降 3未变动

+ 16 - 13
fs-service/src/main/java/com/fs/system/mapper/SysRoleMapper.java

@@ -2,17 +2,18 @@ package com.fs.system.mapper;
 
 
 import java.util.List;
 import java.util.List;
 import com.fs.common.core.domain.entity.SysRole;
 import com.fs.common.core.domain.entity.SysRole;
+import org.apache.ibatis.annotations.Param;
 
 
 /**
 /**
  * 角色表 数据层
  * 角色表 数据层
- * 
+ *
 
 
  */
  */
 public interface SysRoleMapper
 public interface SysRoleMapper
 {
 {
     /**
     /**
      * 根据条件分页查询角色数据
      * 根据条件分页查询角色数据
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 角色数据集合信息
      * @return 角色数据集合信息
      */
      */
@@ -20,7 +21,7 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 根据用户ID查询角色
      * 根据用户ID查询角色
-     * 
+     *
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 角色列表
      * @return 角色列表
      */
      */
@@ -28,14 +29,14 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 查询所有角色
      * 查询所有角色
-     * 
+     *
      * @return 角色列表
      * @return 角色列表
      */
      */
     public List<SysRole> selectRoleAll();
     public List<SysRole> selectRoleAll();
 
 
     /**
     /**
      * 根据用户ID获取角色选择框列表
      * 根据用户ID获取角色选择框列表
-     * 
+     *
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 选中角色ID列表
      * @return 选中角色ID列表
      */
      */
@@ -43,7 +44,7 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 通过角色ID查询角色
      * 通过角色ID查询角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @return 角色对象信息
      * @return 角色对象信息
      */
      */
@@ -51,7 +52,7 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 根据用户ID查询角色
      * 根据用户ID查询角色
-     * 
+     *
      * @param userName 用户名
      * @param userName 用户名
      * @return 角色列表
      * @return 角色列表
      */
      */
@@ -59,7 +60,7 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 校验角色名称是否唯一
      * 校验角色名称是否唯一
-     * 
+     *
      * @param roleName 角色名称
      * @param roleName 角色名称
      * @return 角色信息
      * @return 角色信息
      */
      */
@@ -67,7 +68,7 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 校验角色权限是否唯一
      * 校验角色权限是否唯一
-     * 
+     *
      * @param roleKey 角色权限
      * @param roleKey 角色权限
      * @return 角色信息
      * @return 角色信息
      */
      */
@@ -75,7 +76,7 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 修改角色信息
      * 修改角色信息
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -83,7 +84,7 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 新增角色信息
      * 新增角色信息
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -91,7 +92,7 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 通过角色ID删除角色
      * 通过角色ID删除角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @return 结果
      * @return 结果
      */
      */
@@ -99,9 +100,11 @@ public interface SysRoleMapper
 
 
     /**
     /**
      * 批量删除角色信息
      * 批量删除角色信息
-     * 
+     *
      * @param roleIds 需要删除的角色ID
      * @param roleIds 需要删除的角色ID
      * @return 结果
      * @return 结果
      */
      */
     public int deleteRoleByIds(Long[] roleIds);
     public int deleteRoleByIds(Long[] roleIds);
+
+    List<SysRole> selectSysRoleByIds(Long[] roleIds);
 }
 }

+ 23 - 21
fs-service/src/main/java/com/fs/system/service/ISysRoleService.java

@@ -7,14 +7,14 @@ import com.fs.system.domain.SysUserRole;
 
 
 /**
 /**
  * 角色业务层
  * 角色业务层
- * 
+ *
 
 
  */
  */
 public interface ISysRoleService
 public interface ISysRoleService
 {
 {
     /**
     /**
      * 根据条件分页查询角色数据
      * 根据条件分页查询角色数据
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 角色数据集合信息
      * @return 角色数据集合信息
      */
      */
@@ -22,7 +22,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 根据用户ID查询角色列表
      * 根据用户ID查询角色列表
-     * 
+     *
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 角色列表
      * @return 角色列表
      */
      */
@@ -30,7 +30,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 根据用户ID查询角色权限
      * 根据用户ID查询角色权限
-     * 
+     *
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 权限列表
      * @return 权限列表
      */
      */
@@ -38,14 +38,14 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 查询所有角色
      * 查询所有角色
-     * 
+     *
      * @return 角色列表
      * @return 角色列表
      */
      */
     public List<SysRole> selectRoleAll();
     public List<SysRole> selectRoleAll();
 
 
     /**
     /**
      * 根据用户ID获取角色选择框列表
      * 根据用户ID获取角色选择框列表
-     * 
+     *
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 选中角色ID列表
      * @return 选中角色ID列表
      */
      */
@@ -53,7 +53,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 通过角色ID查询角色
      * 通过角色ID查询角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @return 角色对象信息
      * @return 角色对象信息
      */
      */
@@ -61,7 +61,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 校验角色名称是否唯一
      * 校验角色名称是否唯一
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -69,7 +69,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 校验角色权限是否唯一
      * 校验角色权限是否唯一
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -77,21 +77,21 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 校验角色是否允许操作
      * 校验角色是否允许操作
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      */
      */
     public void checkRoleAllowed(SysRole role);
     public void checkRoleAllowed(SysRole role);
 
 
     /**
     /**
      * 校验角色是否有数据权限
      * 校验角色是否有数据权限
-     * 
+     *
      * @param roleId 角色id
      * @param roleId 角色id
      */
      */
     public void checkRoleDataScope(Long roleId);
     public void checkRoleDataScope(Long roleId);
 
 
     /**
     /**
      * 通过角色ID查询角色使用数量
      * 通过角色ID查询角色使用数量
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @return 结果
      * @return 结果
      */
      */
@@ -99,7 +99,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 新增保存角色信息
      * 新增保存角色信息
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -107,7 +107,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 修改保存角色信息
      * 修改保存角色信息
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -115,7 +115,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 修改角色状态
      * 修改角色状态
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -123,7 +123,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 修改数据权限信息
      * 修改数据权限信息
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -131,7 +131,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 通过角色ID删除角色
      * 通过角色ID删除角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @return 结果
      * @return 结果
      */
      */
@@ -139,7 +139,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 批量删除角色信息
      * 批量删除角色信息
-     * 
+     *
      * @param roleIds 需要删除的角色ID
      * @param roleIds 需要删除的角色ID
      * @return 结果
      * @return 结果
      */
      */
@@ -147,7 +147,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 取消授权用户角色
      * 取消授权用户角色
-     * 
+     *
      * @param userRole 用户和角色关联信息
      * @param userRole 用户和角色关联信息
      * @return 结果
      * @return 结果
      */
      */
@@ -155,7 +155,7 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 批量取消授权用户角色
      * 批量取消授权用户角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @param userIds 需要取消授权的用户数据ID
      * @param userIds 需要取消授权的用户数据ID
      * @return 结果
      * @return 结果
@@ -164,10 +164,12 @@ public interface ISysRoleService
 
 
     /**
     /**
      * 批量选择授权用户角色
      * 批量选择授权用户角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @param userIds 需要删除的用户数据ID
      * @param userIds 需要删除的用户数据ID
      * @return 结果
      * @return 结果
      */
      */
     public int insertAuthUsers(Long roleId, Long[] userIds);
     public int insertAuthUsers(Long roleId, Long[] userIds);
+
+    boolean getIsCheckPhone(Long[] roleIds);
 }
 }

+ 37 - 22
fs-service/src/main/java/com/fs/system/service/impl/SysRoleServiceImpl.java

@@ -27,7 +27,7 @@ import com.fs.system.service.ISysRoleService;
 
 
 /**
 /**
  * 角色 业务层处理
  * 角色 业务层处理
- * 
+ *
 
 
  */
  */
 @Service
 @Service
@@ -47,7 +47,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 根据条件分页查询角色数据
      * 根据条件分页查询角色数据
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 角色数据集合信息
      * @return 角色数据集合信息
      */
      */
@@ -60,7 +60,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 根据用户ID查询角色
      * 根据用户ID查询角色
-     * 
+     *
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 角色列表
      * @return 角色列表
      */
      */
@@ -85,7 +85,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 根据用户ID查询权限
      * 根据用户ID查询权限
-     * 
+     *
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 权限列表
      * @return 权限列表
      */
      */
@@ -106,7 +106,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 查询所有角色
      * 查询所有角色
-     * 
+     *
      * @return 角色列表
      * @return 角色列表
      */
      */
     @Override
     @Override
@@ -117,7 +117,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 根据用户ID获取角色选择框列表
      * 根据用户ID获取角色选择框列表
-     * 
+     *
      * @param userId 用户ID
      * @param userId 用户ID
      * @return 选中角色ID列表
      * @return 选中角色ID列表
      */
      */
@@ -129,7 +129,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 通过角色ID查询角色
      * 通过角色ID查询角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @return 角色对象信息
      * @return 角色对象信息
      */
      */
@@ -141,7 +141,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 校验角色名称是否唯一
      * 校验角色名称是否唯一
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -159,7 +159,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 校验角色权限是否唯一
      * 校验角色权限是否唯一
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -177,7 +177,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 校验角色是否允许操作
      * 校验角色是否允许操作
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      */
      */
     @Override
     @Override
@@ -191,7 +191,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 校验角色是否有数据权限
      * 校验角色是否有数据权限
-     * 
+     *
      * @param roleId 角色id
      * @param roleId 角色id
      */
      */
     @Override
     @Override
@@ -211,7 +211,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 通过角色ID查询角色使用数量
      * 通过角色ID查询角色使用数量
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @return 结果
      * @return 结果
      */
      */
@@ -223,7 +223,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 新增保存角色信息
      * 新增保存角色信息
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -238,7 +238,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 修改保存角色信息
      * 修改保存角色信息
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -255,7 +255,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 修改角色状态
      * 修改角色状态
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -267,7 +267,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 修改数据权限信息
      * 修改数据权限信息
-     * 
+     *
      * @param role 角色信息
      * @param role 角色信息
      * @return 结果
      * @return 结果
      */
      */
@@ -285,7 +285,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 新增角色菜单信息
      * 新增角色菜单信息
-     * 
+     *
      * @param role 角色对象
      * @param role 角色对象
      */
      */
     public int insertRoleMenu(SysRole role)
     public int insertRoleMenu(SysRole role)
@@ -333,7 +333,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 通过角色ID删除角色
      * 通过角色ID删除角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @return 结果
      * @return 结果
      */
      */
@@ -350,7 +350,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 批量删除角色信息
      * 批量删除角色信息
-     * 
+     *
      * @param roleIds 需要删除的角色ID
      * @param roleIds 需要删除的角色ID
      * @return 结果
      * @return 结果
      */
      */
@@ -376,7 +376,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 取消授权用户角色
      * 取消授权用户角色
-     * 
+     *
      * @param userRole 用户和角色关联信息
      * @param userRole 用户和角色关联信息
      * @return 结果
      * @return 结果
      */
      */
@@ -388,7 +388,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 批量取消授权用户角色
      * 批量取消授权用户角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @param userIds 需要取消授权的用户数据ID
      * @param userIds 需要取消授权的用户数据ID
      * @return 结果
      * @return 结果
@@ -401,7 +401,7 @@ public class SysRoleServiceImpl implements ISysRoleService
 
 
     /**
     /**
      * 批量选择授权用户角色
      * 批量选择授权用户角色
-     * 
+     *
      * @param roleId 角色ID
      * @param roleId 角色ID
      * @param userIds 需要删除的用户数据ID
      * @param userIds 需要删除的用户数据ID
      * @return 结果
      * @return 结果
@@ -420,4 +420,19 @@ public class SysRoleServiceImpl implements ISysRoleService
         }
         }
         return userRoleMapper.batchUserRole(list);
         return userRoleMapper.batchUserRole(list);
     }
     }
+
+    @Override
+    public boolean getIsCheckPhone(Long[] roleIds) {
+        if (roleIds != null && roleIds.length > 0){
+            List<SysRole> roles = roleMapper.selectSysRoleByIds(roleIds);
+            if (roles != null && !roles.isEmpty()){
+                for (SysRole role : roles) {
+                    if (role.getIsCheckPhone() == 1){
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
 }
 }

+ 1 - 1
fs-service/src/main/resources/application-common.yml

@@ -36,7 +36,7 @@ server:
 # 日志配置
 # 日志配置
 logging:
 logging:
   level:
   level:
-    com.fs: DEBUG
+    com.fs: info
     org.springframework: warn
     org.springframework: warn
 
 
 express:
 express:

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

@@ -58,6 +58,10 @@ wx:
         secret: 8adb2a7533921449ef6e60814c2ff075
         secret: 8adb2a7533921449ef6e60814c2ff075
         token: PPKOdAlCoMO # 接口配置里的Token值
         token: PPKOdAlCoMO # 接口配置里的Token值
         aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
         aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
+  # 开放平台app微信授权配置
+  open:
+    app-id: wxcb1e78baf03c0662
+    secret: d041d3e2392a68a3e86dc22d976ed4a0
 aifabu:  #爱链接
 aifabu:  #爱链接
   appKey: 7b471be905ab17e00f3b858c6710dd117601d008
   appKey: 7b471be905ab17e00f3b858c6710dd117601d008
 watch:
 watch:

+ 1 - 1
fs-service/src/main/resources/application-config-druid-hcl.yml

@@ -85,7 +85,7 @@ cloud_host:
   company_name: 恒春来
   company_name: 恒春来
 #看课授权时显示的头像
 #看课授权时显示的头像
 headerImg:
 headerImg:
-  imgUrl: https://hcl-1b2b.obs.cn-south-1.myhuaweicloud.com/fs/20250803/1754213762409.png
+  imgUrl: http://hcl-1b2b.obs.cn-south-1.myhuaweicloud.com/fs/20250808/1754640068227.png
 ipad:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   ipadUrl: http://ipad.cdwjyyh.com
   aiApi:
   aiApi:

+ 1 - 1
fs-service/src/main/resources/application-config-druid-sxjz.yml

@@ -79,7 +79,7 @@ cloud_host:
 headerImg:
 headerImg:
   imgUrl: https://jz-cos-1356808054.cos.ap-chengdu.myqcloud.com/fs/20250515/0877754b59814ea8a428fa3697b20e68.png
   imgUrl: https://jz-cos-1356808054.cos.ap-chengdu.myqcloud.com/fs/20250515/0877754b59814ea8a428fa3697b20e68.png
 ipad:
 ipad:
-  ipadUrl: http://ipad.cdwjyyh.com
+  ipadUrl: http://ipad.xintaihl.cn
   aiApi:
   aiApi:
 wx_miniapp_temp:
 wx_miniapp_temp:
   pay_order_temp_id:
   pay_order_temp_id:

+ 1 - 0
fs-service/src/main/resources/application-config-druid-xzt.yml

@@ -82,6 +82,7 @@ headerImg:
   imgUrl: https://drk-1363981074.cos.ap-chongqing.myqcloud.com/fs/logo/30d7a0d1ec31e5ac16c6e96d5ca76ad.png
   imgUrl: https://drk-1363981074.cos.ap-chongqing.myqcloud.com/fs/logo/30d7a0d1ec31e5ac16c6e96d5ca76ad.png
 ipad:
 ipad:
   ipadUrl: http://ipad.cdwjyyh.com
   ipadUrl: http://ipad.cdwjyyh.com
+  aiApi: 1212121212
 wx_miniapp_temp:
 wx_miniapp_temp:
   pay_order_temp_id:
   pay_order_temp_id:
   inquiry_temp_id:
   inquiry_temp_id:

+ 7 - 1
fs-service/src/main/resources/application-druid-sxjz.yml

@@ -146,7 +146,13 @@ rocketmq:
         group: voice-group
         group: voice-group
         access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
         access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
         secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
         secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
-
+custom:
+    token: "1o62d3YxvdHd4LEUiltnu7sK"
+    encoding-aes-key: "UJfTQ5qKTKlegjkXtp1YuzJzxeHlUKvq5GyFbERN1iU"
+    corp-id: "ww51717e2b71d5e2d3"
+    secret: "6ODAmw-8W4t6h9mdzHh2Z4Apwj8mnsyRnjEDZOHdA7k"
+    private-key-path: "privatekey.pem"
+    webhook-url: "https://your-server.com/wecom/archive"
 # token配置
 # token配置
 token:
 token:
     # 令牌自定义标识
     # 令牌自定义标识

+ 3 - 0
fs-service/src/main/resources/application-druid-xzt.yml

@@ -139,3 +139,6 @@ rocketmq:
         group: test-group
         group: test-group
         access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
         access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
         secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
         secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+openIM:
+    secret: openIM123
+    userID: imAdmin

+ 91 - 0
fs-service/src/main/resources/mapper/CompanyMiniappMapper.xml

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.company.mapper.CompanyMiniappMapper">
+    
+    <resultMap type="CompanyMiniapp" id="CompanyMiniappResult">
+        <result property="id"    column="id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="appId"    column="app_id"    />
+        <result property="type"    column="type"    />
+        <result property="sortNum"    column="sort_num"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="remark"    column="remark"    />
+    </resultMap>
+
+    <sql id="selectCompanyMiniappVo">
+        select id, company_id, app_id, type, sort_num, create_time, create_by, update_by, update_time, remark from company_miniapp
+    </sql>
+
+    <select id="selectCompanyMiniappList" parameterType="CompanyMiniapp" resultMap="CompanyMiniappResult">
+        <include refid="selectCompanyMiniappVo"/>
+        <where>  
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="appId != null  and appId != ''"> and app_id = #{appId}</if>
+            <if test="type != null "> and type = #{type}</if>
+            <if test="sortNum != null "> and sort_num = #{sortNum}</if>
+        </where>
+    </select>
+    
+    <select id="selectCompanyMiniappById" parameterType="Long" resultMap="CompanyMiniappResult">
+        <include refid="selectCompanyMiniappVo"/>
+        where id = #{id}
+    </select>
+        
+    <insert id="insertCompanyMiniapp" parameterType="CompanyMiniapp" useGeneratedKeys="true" keyProperty="id">
+        insert into company_miniapp
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">company_id,</if>
+            <if test="appId != null">app_id,</if>
+            <if test="type != null">type,</if>
+            <if test="sortNum != null">sort_num,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="remark != null">remark,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">#{companyId},</if>
+            <if test="appId != null">#{appId},</if>
+            <if test="type != null">#{type},</if>
+            <if test="sortNum != null">#{sortNum},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="remark != null">#{remark},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCompanyMiniapp" parameterType="CompanyMiniapp">
+        update company_miniapp
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="appId != null">app_id = #{appId},</if>
+            <if test="type != null">type = #{type},</if>
+            <if test="sortNum != null">sort_num = #{sortNum},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="remark != null">remark = #{remark},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCompanyMiniappById" parameterType="Long">
+        delete from company_miniapp where id = #{id}
+    </delete>
+
+    <delete id="deleteCompanyMiniappByIds" parameterType="String">
+        delete from company_miniapp where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 12 - 0
fs-service/src/main/resources/mapper/company/CompanyMapper.xml

@@ -35,6 +35,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="packageCateIds"    column="package_cate_ids"    />
         <result property="packageCateIds"    column="package_cate_ids"    />
         <result property="courseMaAppId"    column="course_ma_app_id"    />
         <result property="courseMaAppId"    column="course_ma_app_id"    />
         <result property="courseMiniAppId"    column="course_mini_app_id"    />
         <result property="courseMiniAppId"    column="course_mini_app_id"    />
+        <result property="repeat"    column="repeat"    />
+        <result property="sendIfType"    column="send_if_type"    />
+        <result property="ifNum"    column="if_num"    />
     </resultMap>
     </resultMap>
 
 
     <sql id="selectCompanyVo">
     <sql id="selectCompanyVo">
@@ -105,6 +108,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="packageCateIds != null">package_cate_ids,</if>
             <if test="packageCateIds != null">package_cate_ids,</if>
             <if test="courseMaAppId != null">course_ma_app_id,</if>
             <if test="courseMaAppId != null">course_ma_app_id,</if>
             <if test="courseMiniAppId != null">course_mini_app_id,</if>
             <if test="courseMiniAppId != null">course_mini_app_id,</if>
+            <if test="repeat != null">`repeat`,</if>
+            <if test="sendIfType != null">send_if_type,</if>
+            <if test="ifNum != null">if_num,</if>
         </trim>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="companyName != null">#{companyName},</if>
             <if test="companyName != null">#{companyName},</if>
@@ -134,6 +140,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="packageCateIds != null">#{packageCateIds},</if>
             <if test="packageCateIds != null">#{packageCateIds},</if>
             <if test="courseMaAppId != null">#{courseMaAppId},</if>
             <if test="courseMaAppId != null">#{courseMaAppId},</if>
             <if test="courseMiniAppId != null">#{courseMiniAppId},</if>
             <if test="courseMiniAppId != null">#{courseMiniAppId},</if>
+            <if test="repeat != null">#{repeat},</if>
+            <if test="sendIfType != null">#{sendIfType},</if>
+            <if test="ifNum != null">#{ifNum},</if>
          </trim>
          </trim>
     </insert>
     </insert>
 
 
@@ -169,6 +178,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="courseMaAppId != null">course_ma_app_id = #{courseMaAppId},</if>
             <if test="courseMaAppId != null">course_ma_app_id = #{courseMaAppId},</if>
             <if test="courseMiniAppId != null">course_mini_app_id = #{courseMiniAppId},</if>
             <if test="courseMiniAppId != null">course_mini_app_id = #{courseMiniAppId},</if>
             <if test="fsUserIsDefaultBlack != null ">fs_user_is_default_black = #{fsUserIsDefaultBlack},</if>
             <if test="fsUserIsDefaultBlack != null ">fs_user_is_default_black = #{fsUserIsDefaultBlack},</if>
+            <if test="repeat != null">`repeat` = #{repeat},</if>
+            <if test="sendIfType != null">send_if_type = #{sendIfType},</if>
+            <if test="ifNum != null">if_num = #{ifNum},</if>
         </trim>
         </trim>
         where company_id = #{companyId}
         where company_id = #{companyId}
     </update>
     </update>

+ 24 - 0
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -201,6 +201,30 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
         </foreach>
     </update>
     </update>
 
 
+    <update id="batchUpdateQwExternalContactByMoreStudy" parameterType="map">
+        UPDATE qw_external_contact
+        SET
+        level = CASE id
+        <foreach collection="list" item="item">
+            WHEN #{item.id} THEN #{item.level}
+        </foreach>
+        ELSE level
+        END
+        WHERE id IN
+        <foreach collection="list" item="item" open="(" separator="," close=")">
+            #{item.id}
+        </foreach>
+    </update>
+
+    <update id="batchUpdateQwExternalByIsDaysNotStudy" parameterType="map">
+        UPDATE qw_external_contact
+        SET level =  NULL
+        WHERE id IN
+        <foreach collection="list" item="item" open="(" separator="," close=")">
+            #{item.id}
+        </foreach>
+    </update>
+
 
 
     <insert id="insertQwExternalContact" parameterType="QwExternalContact" useGeneratedKeys="true" keyProperty="id" >
     <insert id="insertQwExternalContact" parameterType="QwExternalContact" useGeneratedKeys="true" keyProperty="id" >
         insert into qw_external_contact
         insert into qw_external_contact

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

@@ -20,6 +20,7 @@
         <result property="updateTime" column="update_time" jdbcType="VARCHAR" />
         <result property="updateTime" column="update_time" jdbcType="VARCHAR" />
         <result property="tagIds" column="tag_ids" jdbcType="VARCHAR" />
         <result property="tagIds" column="tag_ids" jdbcType="VARCHAR" />
         <result property="isDaysNotStudy" column="is_days_not_study"/>
         <result property="isDaysNotStudy" column="is_days_not_study"/>
+        <result property="grade" column="grade"/>
     </resultMap>
     </resultMap>
 
 
     <sql id="selectSopUserLogsInfoVo">
     <sql id="selectSopUserLogsInfoVo">
@@ -185,7 +186,7 @@
 
 
     <!-- 根据ID查询单条记录 -->
     <!-- 根据ID查询单条记录 -->
     <select id="selectById" parameterType="String" resultMap="SopUserLogsInfoResult">
     <select id="selectById" parameterType="String" resultMap="SopUserLogsInfoResult">
-        SELECT id, sop_id, user_logs_id, external_contact_id,qw_user_id,corp_id,external_id, fs_user_id, external_user_name,create_time,crt_Time,update_time
+        SELECT id, sop_id, user_logs_id, external_contact_id,qw_user_id,corp_id,external_id, fs_user_id, external_user_name,create_time,crt_Time,update_time,grade
         FROM sop_user_logs_info
         FROM sop_user_logs_info
         WHERE id = #{id}
         WHERE id = #{id}
     </select>
     </select>
@@ -235,6 +236,16 @@
         from sop_user_logs_info where sop_id = #{sopId} and user_logs_id=#{userLogsId}
         from sop_user_logs_info where sop_id = #{sopId} and user_logs_id=#{userLogsId}
     </select>
     </select>
 
 
+    <select id="selectRestoreByIsDaysNotStudy" parameterType="String" resultMap="SopUserLogsInfoResult">
+        select
+            id,external_id
+        from sop_user_logs_info
+        where sop_id = #{sopId}
+          and user_logs_id=#{userLogsId}
+          and is_days_not_study=1
+    </select>
+
+
     <!-- 查询所有记录 -->
     <!-- 查询所有记录 -->
     <select id="selectAll" resultMap="SopUserLogsInfoResult">
     <select id="selectAll" resultMap="SopUserLogsInfoResult">
         SELECT id, sop_id, user_logs_id, external_contact_id,qw_user_id,corp_id,external_id,
         SELECT id, sop_id, user_logs_id, external_contact_id,qw_user_id,corp_id,external_id,

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

@@ -253,6 +253,47 @@
             LIMIT #{offset}, #{pageSize}
             LIMIT #{offset}, #{pageSize}
     </select>
     </select>
 
 
+
+    <select id="meetsTheRatingByUserInfoWithPaginationStudyDays" resultType="Integer"  resultMap="SopUserLogsResult">
+        SELECT
+            ul.id,
+            ul.sop_id,
+            ul.sop_temp_id,
+            ul.qw_user_id,
+            ul.corp_id,
+            ul.start_time,
+            ul.`status`,
+            ul.user_id,
+            DATEDIFF( CURRENT_DATE, ul.start_time ) AS count_days
+        FROM
+            sop_user_logs ul  LEFT JOIN qw_sop qs on ul.sop_id=qs.id
+        WHERE
+            ul.`status` = '1'
+          and qs.type=2
+          and qs.send_type=2
+          and qs.`status` in (2,3)
+          AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) >= #{notStudyDays}
+        ORDER BY id ASC
+            LIMIT #{offset}, #{pageSize}
+    </select>
+
+    <select id="meetsTherestoreByIsDaysNotStudy" resultType="Integer"  resultMap="SopUserLogsResult">
+        SELECT
+            ul.id,
+            ul.sop_id
+        FROM
+            sop_user_logs ul  LEFT JOIN qw_sop qs on ul.sop_id=qs.id
+        WHERE
+            ul.`status` = '1'
+          and qs.type=2
+          and qs.send_type=2
+          and qs.`status` in (2,3)
+          AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) &lt; #{notStudyDays}
+        ORDER BY id ASC
+            LIMIT #{offset}, #{pageSize}
+    </select>
+
+
     <select id="meetsTheRatingByUserInfoBySopId" resultType="String"  resultMap="SopUserLogsResult">
     <select id="meetsTheRatingByUserInfoBySopId" resultType="String"  resultMap="SopUserLogsResult">
         SELECT
         SELECT
             ul.id,
             ul.id,

+ 31 - 18
fs-service/src/main/resources/mapper/system/SysRoleMapper.xml

@@ -19,17 +19,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		<result property="updateBy"           column="update_by"             />
 		<result property="updateBy"           column="update_by"             />
 		<result property="updateTime"         column="update_time"           />
 		<result property="updateTime"         column="update_time"           />
 		<result property="remark"             column="remark"                />
 		<result property="remark"             column="remark"                />
+		<result property="isCheckPhone"       column="is_check_phone"                />
 	</resultMap>
 	</resultMap>
-	
+
 	<sql id="selectRoleVo">
 	<sql id="selectRoleVo">
 	    select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly,
 	    select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly,
-            r.status, r.del_flag, r.create_time, r.remark 
+            r.status, r.del_flag, r.create_time, r.remark,r.is_check_phone
         from sys_role r
         from sys_role r
 	        left join sys_user_role ur on ur.role_id = r.role_id
 	        left join sys_user_role ur on ur.role_id = r.role_id
 	        left join sys_user u on u.user_id = ur.user_id
 	        left join sys_user u on u.user_id = ur.user_id
 	        left join sys_dept d on u.dept_id = d.dept_id
 	        left join sys_dept d on u.dept_id = d.dept_id
     </sql>
     </sql>
-    
+
     <select id="selectRoleList" parameterType="SysRole" resultMap="SysRoleResult">
     <select id="selectRoleList" parameterType="SysRole" resultMap="SysRoleResult">
 		<include refid="selectRoleVo"/>
 		<include refid="selectRoleVo"/>
 		where r.del_flag = '0'
 		where r.del_flag = '0'
@@ -51,20 +52,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
 		<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
 			and date_format(r.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
 			and date_format(r.create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
 		</if>
 		</if>
+		<if test="params.isCheckPhone != null"><!-- 结束时间检索 -->
+			and r.is_check_phone = #{params.isCheckPhone}
+		</if>
 		<!-- 数据范围过滤 -->
 		<!-- 数据范围过滤 -->
 		${params.dataScope}
 		${params.dataScope}
 		order by r.role_sort
 		order by r.role_sort
 	</select>
 	</select>
-    
+
 	<select id="selectRolePermissionByUserId" parameterType="Long" resultMap="SysRoleResult">
 	<select id="selectRolePermissionByUserId" parameterType="Long" resultMap="SysRoleResult">
 		<include refid="selectRoleVo"/>
 		<include refid="selectRoleVo"/>
 		WHERE r.del_flag = '0' and ur.user_id = #{userId}
 		WHERE r.del_flag = '0' and ur.user_id = #{userId}
 	</select>
 	</select>
-	
+
 	<select id="selectRoleAll" resultMap="SysRoleResult">
 	<select id="selectRoleAll" resultMap="SysRoleResult">
 		<include refid="selectRoleVo"/>
 		<include refid="selectRoleVo"/>
 	</select>
 	</select>
-	
+
 	<select id="selectRoleListByUserId" parameterType="Long" resultType="Integer">
 	<select id="selectRoleListByUserId" parameterType="Long" resultType="Integer">
 		select r.role_id
 		select r.role_id
         from sys_role r
         from sys_role r
@@ -72,28 +76,34 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	        left join sys_user u on u.user_id = ur.user_id
 	        left join sys_user u on u.user_id = ur.user_id
 	    where u.user_id = #{userId}
 	    where u.user_id = #{userId}
 	</select>
 	</select>
-	
+
 	<select id="selectRoleById" parameterType="Long" resultMap="SysRoleResult">
 	<select id="selectRoleById" parameterType="Long" resultMap="SysRoleResult">
 		<include refid="selectRoleVo"/>
 		<include refid="selectRoleVo"/>
 		where r.role_id = #{roleId}
 		where r.role_id = #{roleId}
 	</select>
 	</select>
-	
+
 	<select id="selectRolesByUserName" parameterType="String" resultMap="SysRoleResult">
 	<select id="selectRolesByUserName" parameterType="String" resultMap="SysRoleResult">
 		<include refid="selectRoleVo"/>
 		<include refid="selectRoleVo"/>
 		WHERE r.del_flag = '0' and u.user_name = #{userName}
 		WHERE r.del_flag = '0' and u.user_name = #{userName}
 	</select>
 	</select>
-	
+
 	<select id="checkRoleNameUnique" parameterType="String" resultMap="SysRoleResult">
 	<select id="checkRoleNameUnique" parameterType="String" resultMap="SysRoleResult">
 		<include refid="selectRoleVo"/>
 		<include refid="selectRoleVo"/>
 		 where r.role_name=#{roleName} limit 1
 		 where r.role_name=#{roleName} limit 1
 	</select>
 	</select>
-	
+
 	<select id="checkRoleKeyUnique" parameterType="String" resultMap="SysRoleResult">
 	<select id="checkRoleKeyUnique" parameterType="String" resultMap="SysRoleResult">
 		<include refid="selectRoleVo"/>
 		<include refid="selectRoleVo"/>
 		 where r.role_key=#{roleKey} limit 1
 		 where r.role_key=#{roleKey} limit 1
 	</select>
 	</select>
-	
- 	<insert id="insertRole" parameterType="SysRole" useGeneratedKeys="true" keyProperty="roleId">
+	<select id="selectSysRoleByIds" resultType="com.fs.common.core.domain.entity.SysRole">
+		select role_id,is_check_phone from sys_role where role_id in
+		<foreach collection="array" item="roleId" open="(" separator="," close=")">
+			#{roleId}
+		</foreach>
+	</select>
+
+	<insert id="insertRole" parameterType="SysRole" useGeneratedKeys="true" keyProperty="roleId">
  		insert into sys_role(
  		insert into sys_role(
  			<if test="roleId != null and roleId != 0">role_id,</if>
  			<if test="roleId != null and roleId != 0">role_id,</if>
  			<if test="roleName != null and roleName != ''">role_name,</if>
  			<if test="roleName != null and roleName != ''">role_name,</if>
@@ -105,6 +115,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  			<if test="status != null and status != ''">status,</if>
  			<if test="status != null and status != ''">status,</if>
  			<if test="remark != null and remark != ''">remark,</if>
  			<if test="remark != null and remark != ''">remark,</if>
  			<if test="createBy != null and createBy != ''">create_by,</if>
  			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			<if test="isCheckPhone != null">is_check_phone,</if>
  			create_time
  			create_time
  		)values(
  		)values(
  			<if test="roleId != null and roleId != 0">#{roleId},</if>
  			<if test="roleId != null and roleId != 0">#{roleId},</if>
@@ -117,10 +128,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  			<if test="status != null and status != ''">#{status},</if>
  			<if test="status != null and status != ''">#{status},</if>
  			<if test="remark != null and remark != ''">#{remark},</if>
  			<if test="remark != null and remark != ''">#{remark},</if>
  			<if test="createBy != null and createBy != ''">#{createBy},</if>
  			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			<if test="isCheckPhone != null">#{isCheckPhone},</if>
  			sysdate()
  			sysdate()
  		)
  		)
 	</insert>
 	</insert>
-	
+
 	<update id="updateRole" parameterType="SysRole">
 	<update id="updateRole" parameterType="SysRole">
  		update sys_role
  		update sys_role
  		<set>
  		<set>
@@ -133,20 +145,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  			<if test="status != null and status != ''">status = #{status},</if>
  			<if test="status != null and status != ''">status = #{status},</if>
  			<if test="remark != null">remark = #{remark},</if>
  			<if test="remark != null">remark = #{remark},</if>
  			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
  			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			<if test="isCheckPhone != null">is_check_phone = #{isCheckPhone},</if>
  			update_time = sysdate()
  			update_time = sysdate()
  		</set>
  		</set>
  		where role_id = #{roleId}
  		where role_id = #{roleId}
 	</update>
 	</update>
-	
+
 	<delete id="deleteRoleById" parameterType="Long">
 	<delete id="deleteRoleById" parameterType="Long">
 		update sys_role set del_flag = '2' where role_id = #{roleId}
 		update sys_role set del_flag = '2' where role_id = #{roleId}
  	</delete>
  	</delete>
- 	
+
  	<delete id="deleteRoleByIds" parameterType="Long">
  	<delete id="deleteRoleByIds" parameterType="Long">
  	    update sys_role set del_flag = '2' where role_id in
  	    update sys_role set del_flag = '2' where role_id in
  		<foreach collection="array" item="roleId" open="(" separator="," close=")">
  		<foreach collection="array" item="roleId" open="(" separator="," close=")">
  			#{roleId}
  			#{roleId}
-        </foreach> 
+        </foreach>
  	</delete>
  	</delete>
- 	
-</mapper> 
+
+</mapper>

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

@@ -58,6 +58,7 @@ public class CourseController extends  AppBaseController{
             return R.error("操作异常");
             return R.error("操作异常");
         }
         }
     }
     }
+
     @Cacheable(value = "getProductCateByPid", key = "#pid")
     @Cacheable(value = "getProductCateByPid", key = "#pid")
     @ApiOperation("获取子分类")
     @ApiOperation("获取子分类")
     @GetMapping("/getProductCateByPid")
     @GetMapping("/getProductCateByPid")

+ 4 - 0
fs-user-app/src/main/java/com/fs/app/controller/course/CourseQwLoginController.java

@@ -100,8 +100,12 @@ public class CourseQwLoginController extends AppBaseController {
             final WxMaService wxService = wxServiceSupplier.get();
             final WxMaService wxService = wxServiceSupplier.get();
             WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
             WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
             this.logger.info("获取{} Session:{}", logName, session);
             this.logger.info("获取{} Session:{}", logName, session);
+            if (session.getUnionid()==null){
+                return R.error("未绑定开放平台");
+            }
 
 
             FsUser user = userService.selectFsUserByUnionid(session.getUnionid());
             FsUser user = userService.selectFsUserByUnionid(session.getUnionid());
+
             boolean isNewUser = false;
             boolean isNewUser = false;
 
 
             // 用户存在时的更新逻辑
             // 用户存在时的更新逻辑