فهرست منبع

侧边栏语音分组/修改客户等级/评级/按照客户等级限制发送次数/催课看板显示课节

三七 6 روز پیش
والد
کامیت
93afe9f70e
53فایلهای تغییر یافته به همراه1100 افزوده شده و 117 حذف شده
  1. 4 13
      fs-admin/src/main/java/com/fs/company/controller/CompanyUserAllController.java
  2. 77 4
      fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java
  3. 3 0
      fs-company-app/src/main/java/com/fs/app/controller/UserController.java
  4. 68 6
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java
  5. 1 1
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopTempVoiceController.java
  6. 97 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopTempVoiceGroupController.java
  7. 124 38
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  8. 4 4
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  9. 19 1
      fs-qwhook-sop/src/main/java/com/fs/app/controller/QwUserController.java
  10. 8 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java
  11. 5 0
      fs-service/src/main/java/com/fs/course/param/FsCourseListBySidebarParam.java
  12. 12 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  13. 8 0
      fs-service/src/main/java/com/fs/his/param/FindUserByParam.java
  14. 23 0
      fs-service/src/main/java/com/fs/his/vo/ExchangeDetailVo.java
  15. 24 0
      fs-service/src/main/java/com/fs/qw/domain/QwSopTempVoiceGroup.java
  16. 4 0
      fs-service/src/main/java/com/fs/qw/domain/QwWorkTask.java
  17. 1 1
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  18. 6 0
      fs-service/src/main/java/com/fs/qw/mapper/QwRestrictionPushRecordMapper.java
  19. 66 0
      fs-service/src/main/java/com/fs/qw/mapper/QwSopTempVoiceGroupMapper.java
  20. 16 0
      fs-service/src/main/java/com/fs/qw/param/QwExternalContactUpdateLevelParam.java
  21. 63 0
      fs-service/src/main/java/com/fs/qw/service/IQwSopTempVoiceGroupService.java
  22. 3 4
      fs-service/src/main/java/com/fs/qw/service/IQwUserServiceAsyncHelper.java
  23. 95 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwSopTempVoiceGroupServiceImpl.java
  24. 12 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceAsyncHelperImpl.java
  25. 26 10
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  26. 31 1
      fs-service/src/main/java/com/fs/qw/service/impl/QwWorkTaskServiceImpl.java
  27. 1 0
      fs-service/src/main/java/com/fs/qw/vo/QwExternalContactVO.java
  28. 2 0
      fs-service/src/main/java/com/fs/qw/vo/QwWorkTaskListVO.java
  29. 6 0
      fs-service/src/main/java/com/fs/sop/domain/QwSopLogs.java
  30. 2 0
      fs-service/src/main/java/com/fs/sop/domain/QwSopTempVoice.java
  31. 1 1
      fs-service/src/main/java/com/fs/sop/mapper/QwSopTempVoiceMapper.java
  32. 20 1
      fs-service/src/main/java/com/fs/sop/mapper/SopUserLogsInfoMapper.java
  33. 1 1
      fs-service/src/main/java/com/fs/sop/mapper/SopUserLogsMapper.java
  34. 4 0
      fs-service/src/main/java/com/fs/sop/params/QwRatingConfig.java
  35. 1 1
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempVoiceService.java
  36. 5 0
      fs-service/src/main/java/com/fs/sop/service/ISopUserLogsInfoService.java
  37. 3 2
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempVoiceServiceImpl.java
  38. 10 2
      fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  39. 8 0
      fs-service/src/main/resources/db/changelog/dictData/dict_data_update.sql
  40. 17 0
      fs-service/src/main/resources/db/changelog/sop/changes/change_table.sql
  41. 2 0
      fs-service/src/main/resources/db/changelog/sop/db.changelog-master.xml
  42. 12 0
      fs-service/src/main/resources/db/changelog/sop/table/create_table.sql
  43. 8 0
      fs-service/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml
  44. 18 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  45. 66 0
      fs-service/src/main/resources/mapper/qw/QwSopTempVoiceGroupMapper.xml
  46. 1 1
      fs-service/src/main/resources/mapper/qw/QwUserMapper.xml
  47. 8 5
      fs-service/src/main/resources/mapper/sop/QwSopLogsMapper.xml
  48. 15 10
      fs-service/src/main/resources/mapper/sop/QwSopTempVoiceMapper.xml
  49. 1 0
      fs-service/src/main/resources/mapper/sop/SopUserLogsInfoMapper.xml
  50. 6 6
      fs-service/src/main/resources/mapper/sop/SopUserLogsMapper.xml
  51. 43 2
      fs-user-app/src/main/java/com/fs/app/controller/CommonController.java
  52. 17 0
      fs-user-app/src/main/java/com/fs/app/controller/UserController.java
  53. 22 2
      fs-user-app/src/main/java/com/fs/app/controller/app/ImController.java

+ 4 - 13
fs-admin/src/main/java/com/fs/company/controller/CompanyUserAllController.java

@@ -46,7 +46,6 @@ import com.fs.voice.utils.StringUtil;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
-import liquibase.pro.packaged.S;
 import org.json.JSONArray;
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.json.JSONObject;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -160,8 +159,8 @@ public class CompanyUserAllController extends BaseController {
     /**
     /**
      * 获取所有 没有 有语音席位的账号
      * 获取所有 没有 有语音席位的账号
      */
      */
-    @GetMapping("/getAllNotVoiceList")
-    public TableDataInfo getAllNotVoiceList(VcCompanyUserParam param) {
+    @PostMapping("/getAllNotVoiceList")
+    public TableDataInfo getAllNotVoiceList(@RequestBody VcCompanyUserParam param) {
         startPage();
         startPage();
 
 
         List<VcCompanyUser> vcCompanyUsers = companyUserService.getAllNotVoiceList(param.getCompanyUserId());
         List<VcCompanyUser> vcCompanyUsers = companyUserService.getAllNotVoiceList(param.getCompanyUserId());
@@ -172,8 +171,8 @@ public class CompanyUserAllController extends BaseController {
     /**
     /**
     * 获取所有 已经 有语音席位的账号
     * 获取所有 已经 有语音席位的账号
     */
     */
-    @GetMapping("/getAllVoiceList")
-    public TableDataInfo getAllVoiceList(VcCompanyUserParam param) {
+    @PostMapping("/getAllVoiceList")
+    public TableDataInfo getAllVoiceList(@RequestBody VcCompanyUserParam param) {
         startPage();
         startPage();
 
 
         List<VcCompanyUser> vcCompanyUsers = companyUserService.selectAllVcCompanyUser(param.getCompanyUserId());
         List<VcCompanyUser> vcCompanyUsers = companyUserService.selectAllVcCompanyUser(param.getCompanyUserId());
@@ -309,14 +308,6 @@ public class CompanyUserAllController extends BaseController {
         user.setCreateBy(SecurityUtils.getUsername());
         user.setCreateBy(SecurityUtils.getUsername());
         user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
         user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
         user.setCreateTime(new Date());
         user.setCreateTime(new Date());
-
-        Date createTime = new Date();
-        Calendar cal = Calendar.getInstance();
-        cal.setTime(createTime);
-        cal.add(Calendar.YEAR, 1); // 加1年
-        Date expirationTime = cal.getTime();
-        user.setExpirationTime(expirationTime);
-
         user.setUserType("01");//一般用户
         user.setUserType("01");//一般用户
         user.setIsAudit(1);
         user.setIsAudit(1);
         return toAjax(companyUserService.insertUser(user));
         return toAjax(companyUserService.insertUser(user));

+ 77 - 4
fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java

@@ -1,5 +1,7 @@
 package com.fs.qw.controller;
 package com.fs.qw.controller;
 
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
@@ -7,14 +9,14 @@ import java.util.stream.Collectors;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.PubFun;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.ServletUtils;
-import com.fs.qw.param.QwExternalContactParam;
-import com.fs.qw.param.QwTagSearchParam;
-import com.fs.qw.param.ResignedTransferParam;
-import com.fs.qw.param.TransferParam;
+import com.fs.qw.param.*;
 import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwTagService;
 import com.fs.qw.service.IQwTagService;
+import com.fs.qw.service.IQwUserServiceAsyncHelper;
 import com.fs.qw.vo.QwExternalContactUnionIdExportVO;
 import com.fs.qw.vo.QwExternalContactUnionIdExportVO;
 import com.fs.qw.vo.QwExternalContactVO;
 import com.fs.qw.vo.QwExternalContactVO;
 import com.google.gson.Gson;
 import com.google.gson.Gson;
@@ -51,6 +53,8 @@ public class QwExternalContactController extends BaseController
 {
 {
     private IQwExternalContactService qwExternalContactService;
     private IQwExternalContactService qwExternalContactService;
     private IQwTagService iQwTagService;
     private IQwTagService iQwTagService;
+    @Autowired
+    private IQwUserServiceAsyncHelper qwUserServiceAsyncHelper;
 
 
     @Autowired
     @Autowired
     private IQwExternalContactInfoService qwExternalContactInfoService;
     private IQwExternalContactInfoService qwExternalContactInfoService;
@@ -230,4 +234,73 @@ public class QwExternalContactController extends BaseController
         }
         }
         return qwExternalContactService.transfer(param);
         return qwExternalContactService.transfer(param);
     }
     }
+
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:changeLevel')")
+    @Log(title = "批量修改用户等级", businessType = BusinessType.UPDATE)
+    @PostMapping("/changeLevel")
+    public R changeLevel(@RequestBody QwExternalContactUpdateLevelParam param)
+    {
+
+        if(param.isFilter()){
+            param.setUserIds(getList(0, param.getParam()));
+        }
+        if(param.getUserIds() == null || param.getUserIds().isEmpty()){
+            return R.error("修改用户为空");
+        }
+
+        // 获取所有用户ID
+        List<Long> allUserIds = param.getUserIds();
+        int batchSize = 500;
+        int totalSize = allUserIds.size();
+
+        // 记录处理结果
+        int successCount = 0;
+        List<Long> failedUserIds = new ArrayList<>();
+
+        // 分批处理
+        for (int i = 0; i < totalSize; i += batchSize) {
+            int end = Math.min(i + batchSize, totalSize);
+            List<Long> batchUserIds = allUserIds.subList(i, end);
+
+            try {
+                // 创建新的参数对象,只包含当前批次的数据
+                QwExternalContactUpdateLevelParam batchParam = new QwExternalContactUpdateLevelParam();
+                batchParam.setLevel(param.getLevel());
+                batchParam.setFilter(param.isFilter());
+                batchParam.setUserIds(batchUserIds);
+
+                // 调用异步方法
+                qwUserServiceAsyncHelper.batchUpdateExternalContactLevelById(batchParam);
+
+                successCount += batchUserIds.size();
+
+                // 如果不是最后一批,可以稍微延迟一下,避免压力过大
+                if (end < totalSize) {
+                    Thread.sleep(100); // 100毫秒延迟,可根据需要调整
+                }
+            } catch (Exception e) {
+                // 记录失败的批次
+                logger.error("批次处理失败,用户IDs: {}, 错误: {}", batchUserIds, e.getMessage());
+                failedUserIds.addAll(batchUserIds);
+            }
+        }
+
+        // 返回处理结果
+        if (failedUserIds.isEmpty()) {
+            return R.ok("成功处理 " + successCount + " 个用户");
+        } else {
+            return R.ok("部分成功: 成功 " + successCount + " 个, 失败 " + failedUserIds.size() + " 个");
+        }
+    }
+
+
+    private List<Long> getList(Integer addType, QwExternalContactParam param){
+        if(addType == null){
+            return Collections.emptyList();
+        }
+        List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(param);
+        if(list == null || list.isEmpty()) return Collections.emptyList();
+        return PubFun.listToNewList(list, QwExternalContactVO::getId);
+    }
+
 }
 }

+ 3 - 0
fs-company-app/src/main/java/com/fs/app/controller/UserController.java

@@ -22,6 +22,7 @@ import com.fs.common.utils.PinYinUtil;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.bean.BeanUtils;
 import com.fs.common.utils.bean.BeanUtils;
 import com.fs.common.utils.http.HttpUtils;
 import com.fs.common.utils.http.HttpUtils;
+import com.fs.common.utils.sign.Md5Utils;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUserCard;
 import com.fs.company.domain.CompanyUserCard;
@@ -209,10 +210,12 @@ public class UserController extends AppBaseController {
 
 
             //redisCache.setCacheObject("perms:"+companyUser.getUserId(), JSONUtil.toJsonStr(perms),2592000, TimeUnit.SECONDS);
             //redisCache.setCacheObject("perms:"+companyUser.getUserId(), JSONUtil.toJsonStr(perms),2592000, TimeUnit.SECONDS);
             redisCache.setCacheObject("perms:" + companyUser.getUserId(), JSONUtil.toJsonStr(perms), 604800, TimeUnit.SECONDS);
             redisCache.setCacheObject("perms:" + companyUser.getUserId(), JSONUtil.toJsonStr(perms), 604800, TimeUnit.SECONDS);
+            redisCache.setCacheObject("company-user-token:" + Md5Utils.hash(companyUser.getUserId().toString()), companyUser.getUserId(), 100, TimeUnit.DAYS);
 
 
             Map<String, Object> result = new HashMap<>();
             Map<String, Object> result = new HashMap<>();
             result.put("token", token);
             result.put("token", token);
             result.put("user", companyUser);
             result.put("user", companyUser);
+            result.put("companyUserToken", Md5Utils.hash(companyUser.getUserId().toString()));
             result.put("perms", perms);
             result.put("perms", perms);
             return R.ok("登录成功").put("data", result);
             return R.ok("登录成功").put("data", result);
         } catch (Exception e) {
         } catch (Exception e) {

+ 68 - 6
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java

@@ -101,6 +101,15 @@ public class QwExternalContactController extends BaseController
     @Autowired
     @Autowired
     private IFsUserCompanyBindService fsUserCompanyBindService;
     private IFsUserCompanyBindService fsUserCompanyBindService;
 
 
+    @Autowired
+    private IFsUserCoursePeriodService userCoursePeriodService;
+
+    @Autowired
+    private IFsUserCoursePeriodDaysService userCoursePeriodDaysService;
+
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+
     /**
     /**
      * 查询企业微信客户列表
      * 查询企业微信客户列表
      */
      */
@@ -977,14 +986,67 @@ public class QwExternalContactController extends BaseController
         return R.ok("正在批量修改备注中");
         return R.ok("正在批量修改备注中");
     }
     }
 
 
-    @Autowired
-    private IFsUserCoursePeriodService userCoursePeriodService;
 
 
-    @Autowired
-    private IFsUserCoursePeriodDaysService userCoursePeriodDaysService;
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:changeLevel')")
+    @Log(title = "批量修改用户等级", businessType = BusinessType.UPDATE)
+    @PostMapping("/changeLevel")
+    public R changeLevel(@RequestBody QwExternalContactUpdateLevelParam param)
+    {
+
+        if(param.isFilter()){
+            param.setUserIds(getList(0, param.getParam()));
+        }
+        if(param.getUserIds() == null || param.getUserIds().isEmpty()){
+            return R.error("修改用户为空");
+        }
+
+        // 获取所有用户ID
+        List<Long> allUserIds = param.getUserIds();
+        int batchSize = 500;
+        int totalSize = allUserIds.size();
+
+        // 记录处理结果
+        int successCount = 0;
+        List<Long> failedUserIds = new ArrayList<>();
+
+        // 分批处理
+        for (int i = 0; i < totalSize; i += batchSize) {
+            int end = Math.min(i + batchSize, totalSize);
+            List<Long> batchUserIds = allUserIds.subList(i, end);
+
+            try {
+                // 创建新的参数对象,只包含当前批次的数据
+                QwExternalContactUpdateLevelParam batchParam = new QwExternalContactUpdateLevelParam();
+                batchParam.setLevel(param.getLevel());
+                batchParam.setFilter(param.isFilter());
+                batchParam.setUserIds(batchUserIds);
+
+                // 调用异步方法
+                qwUserServiceAsyncHelper.batchUpdateExternalContactLevelById(batchParam);
+
+                successCount += batchUserIds.size();
+
+                // 如果不是最后一批,可以稍微延迟一下,避免压力过大
+                if (end < totalSize) {
+                    Thread.sleep(100); // 100毫秒延迟,可根据需要调整
+                }
+            } catch (Exception e) {
+                // 记录失败的批次
+                logger.error("批次处理失败,用户IDs: {}, 错误: {}", batchUserIds, e.getMessage());
+                failedUserIds.addAll(batchUserIds);
+            }
+        }
+
+        // 返回处理结果
+        if (failedUserIds.isEmpty()) {
+            return R.ok("成功处理 " + successCount + " 个用户");
+        } else {
+            return R.ok("部分成功: 成功 " + successCount + " 个, 失败 " + failedUserIds.size() + " 个");
+        }
+    }
+
+
 
 
-    @Autowired
-    private IFsCourseWatchLogService fsCourseWatchLogService;
     /**
     /**
      * 用于适配看课批量修改标签【根据过滤条件版】
      * 用于适配看课批量修改标签【根据过滤条件版】
      * @param fromMyList
      * @param fromMyList

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/qw/QwSopTempVoiceController.java

@@ -46,7 +46,7 @@ public class QwSopTempVoiceController extends BaseController
         startPage();
         startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
 
 
-        List<QwSopTempVoice> voiceList = qwSopTempVoiceService.selectQwSopTempVoiceByCompanyUserId(loginUser.getUser().getUserId(),param.getKeyword());
+        List<QwSopTempVoice> voiceList = qwSopTempVoiceService.selectQwSopTempVoiceByCompanyUserId(loginUser.getUser().getUserId(),param.getKeyword(),param.getGroupId());
         return getDataTable(voiceList);
         return getDataTable(voiceList);
     }
     }
 
 

+ 97 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwSopTempVoiceGroupController.java

@@ -0,0 +1,97 @@
+package com.fs.company.controller.qw;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.qw.domain.QwSopTempVoiceGroup;
+import com.fs.qw.service.IQwSopTempVoiceGroupService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 语音分类-主要是区分侧边栏语音类型Controller
+ *
+ * @author fs
+ * @date 2026-07-16
+ */
+@RestController
+@RequestMapping("/qw/QwSopTempVoiceGroup")
+public class QwSopTempVoiceGroupController extends BaseController
+{
+    @Autowired
+    private IQwSopTempVoiceGroupService qwSopTempVoiceGroupService;
+
+    /**
+     * 查询语音分类-主要是区分侧边栏语音类型列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwSopTempVoiceGroup:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwSopTempVoiceGroup qwSopTempVoiceGroup)
+    {
+        startPage();
+        List<QwSopTempVoiceGroup> list = qwSopTempVoiceGroupService.selectQwSopTempVoiceGroupList(qwSopTempVoiceGroup);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出语音分类-主要是区分侧边栏语音类型列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwSopTempVoiceGroup:export')")
+    @Log(title = "语音分类-主要是区分侧边栏语音类型", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwSopTempVoiceGroup qwSopTempVoiceGroup)
+    {
+        List<QwSopTempVoiceGroup> list = qwSopTempVoiceGroupService.selectQwSopTempVoiceGroupList(qwSopTempVoiceGroup);
+        ExcelUtil<QwSopTempVoiceGroup> util = new ExcelUtil<QwSopTempVoiceGroup>(QwSopTempVoiceGroup.class);
+        return util.exportExcel(list, "语音分类-主要是区分侧边栏语音类型数据");
+    }
+
+    /**
+     * 获取语音分类-主要是区分侧边栏语音类型详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwSopTempVoiceGroup:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwSopTempVoiceGroupService.selectQwSopTempVoiceGroupById(id));
+    }
+
+    /**
+     * 新增语音分类-主要是区分侧边栏语音类型
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwSopTempVoiceGroup:add')")
+    @Log(title = "语音分类-主要是区分侧边栏语音类型", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwSopTempVoiceGroup qwSopTempVoiceGroup)
+    {
+        return toAjax(qwSopTempVoiceGroupService.insertQwSopTempVoiceGroup(qwSopTempVoiceGroup));
+    }
+
+    /**
+     * 修改语音分类-主要是区分侧边栏语音类型
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwSopTempVoiceGroup:edit')")
+    @Log(title = "语音分类-主要是区分侧边栏语音类型", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwSopTempVoiceGroup qwSopTempVoiceGroup)
+    {
+        return toAjax(qwSopTempVoiceGroupService.updateQwSopTempVoiceGroup(qwSopTempVoiceGroup));
+    }
+
+    /**
+     * 删除语音分类-主要是区分侧边栏语音类型
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwSopTempVoiceGroup:remove')")
+    @Log(title = "语音分类-主要是区分侧边栏语音类型", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwSopTempVoiceGroupService.deleteQwSopTempVoiceGroupByIds(ids));
+    }
+}

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

@@ -28,9 +28,11 @@ import com.fs.qw.vo.QwSopCourseFinishTempSetting;
 import com.fs.qw.vo.QwSopTempSetting;
 import com.fs.qw.vo.QwSopTempSetting;
 import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.QwSopLogsMapper;
+import com.fs.sop.params.QwRatingConfig;
 import com.fs.sop.service.IQwSopLogsService;
 import com.fs.sop.service.IQwSopLogsService;
 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.ISysConfigService;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -68,6 +70,7 @@ public class SendMsg {
     private final AiAddwxSopUserWorkflowMapper aiAddwxSopUserWorkflowMapper;
     private final AiAddwxSopUserWorkflowMapper aiAddwxSopUserWorkflowMapper;
     private final AiAddwxSopUserNodeRuleMapper aiAddwxSopUserNodeRuleMapper;
     private final AiAddwxSopUserNodeRuleMapper aiAddwxSopUserNodeRuleMapper;
     private final QwExternalContactMapper qwExternalContactMapper;
     private final QwExternalContactMapper qwExternalContactMapper;
+    private final ISysConfigService sysConfigService;
 
 
     @Value("${group-no}")
     @Value("${group-no}")
     private String groupNo;
     private String groupNo;
@@ -101,7 +104,7 @@ public class SendMsg {
     @Qualifier("customThreadPool")
     @Qualifier("customThreadPool")
     private ThreadPoolTaskExecutor customThreadPool;
     private ThreadPoolTaskExecutor customThreadPool;
 
 
-    public SendMsg(QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, IpadSendServer sendServer, SysConfigMapper sysConfigMapper, IQwSopLogsService qwSopLogsService, QwIpadServerMapper qwIpadServerMapper, RedisCacheT<Long> redisCache, AsyncSopTestService asyncSopTestService, ICompanyMiniappService companyMiniappService, IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService, QwPushCountMapper qwPushCountMapper, QwRestrictionPushRecordMapper qwRestrictionPushRecordMapper, IAiAddwxSopUserNodeService aiAddwxSopUserNodeService, AiAddwxSopUserWorkflowMapper aiAddwxSopUserWorkflowMapper, AiAddwxSopUserNodeRuleMapper aiAddwxSopUserNodeRuleMapper, QwExternalContactMapper qwExternalContactMapper) {
+    public SendMsg(QwUserMapper qwUserMapper, QwSopLogsMapper qwSopLogsMapper, IpadSendServer sendServer, SysConfigMapper sysConfigMapper, IQwSopLogsService qwSopLogsService, QwIpadServerMapper qwIpadServerMapper, RedisCacheT<Long> redisCache, AsyncSopTestService asyncSopTestService, ICompanyMiniappService companyMiniappService, IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService, QwPushCountMapper qwPushCountMapper, QwRestrictionPushRecordMapper qwRestrictionPushRecordMapper, IAiAddwxSopUserNodeService aiAddwxSopUserNodeService, AiAddwxSopUserWorkflowMapper aiAddwxSopUserWorkflowMapper, AiAddwxSopUserNodeRuleMapper aiAddwxSopUserNodeRuleMapper, QwExternalContactMapper qwExternalContactMapper, ISysConfigService sysConfigService) {
         this.qwUserMapper = qwUserMapper;
         this.qwUserMapper = qwUserMapper;
         this.qwSopLogsMapper = qwSopLogsMapper;
         this.qwSopLogsMapper = qwSopLogsMapper;
         this.sendServer = sendServer;
         this.sendServer = sendServer;
@@ -118,6 +121,7 @@ public class SendMsg {
         this.aiAddwxSopUserWorkflowMapper = aiAddwxSopUserWorkflowMapper;
         this.aiAddwxSopUserWorkflowMapper = aiAddwxSopUserWorkflowMapper;
         this.aiAddwxSopUserNodeRuleMapper = aiAddwxSopUserNodeRuleMapper;
         this.aiAddwxSopUserNodeRuleMapper = aiAddwxSopUserNodeRuleMapper;
         this.qwExternalContactMapper = qwExternalContactMapper;
         this.qwExternalContactMapper = qwExternalContactMapper;
+        this.sysConfigService = sysConfigService;
     }
     }
     private List<QwUser> getQwUserList() {
     private List<QwUser> getQwUserList() {
         if (qwUserList.isEmpty()) {
         if (qwUserList.isEmpty()) {
@@ -156,6 +160,10 @@ public class SendMsg {
         // 看课配置文件获取
         // 看课配置文件获取
         SysConfig courseConfig = sysConfigMapper.selectConfigByConfigKey("course.config");
         SysConfig courseConfig = sysConfigMapper.selectConfigByConfigKey("course.config");
         CourseConfig config = JSON.parseObject(courseConfig.getConfigValue(), CourseConfig.class);
         CourseConfig config = JSON.parseObject(courseConfig.getConfigValue(), CourseConfig.class);
+
+        String ratingJson = sysConfigService.selectConfigByKey("qwRating:config");
+        QwRatingConfig ratingConfig = JSON.parseObject(ratingJson, QwRatingConfig.class);
+
         // 消息发送延迟
         // 消息发送延迟
         int delayStart;
         int delayStart;
         int delayEnd;
         int delayEnd;
@@ -190,7 +198,7 @@ public class SendMsg {
             CompletableFuture.runAsync(() -> {
             CompletableFuture.runAsync(() -> {
                 try {
                 try {
                     log.info("开始任务:{}", e.getQwUserName());
                     log.info("开始任务:{}", e.getQwUserName());
-                    processUser(e, delayStart, delayEnd, miniMap, newCtx);
+                    processUser(e, delayStart, delayEnd, miniMap, newCtx,ratingConfig);
                 } catch (Exception exception) {
                 } catch (Exception exception) {
                     log.error("发送错误:", exception);
                     log.error("发送错误:", exception);
                 } finally {
                 } finally {
@@ -213,7 +221,7 @@ public class SendMsg {
      * @param miniMap    小程序配置
      * @param miniMap    小程序配置
      * @param ctx        任务上下文(用于取消检查)
      * @param ctx        任务上下文(用于取消检查)
      */
      */
-    private void processUser(QwUser qwUser, int delayStart, int delayEnd, Map<String, FsCoursePlaySourceConfig> miniMap, TaskContext ctx) {
+    private void processUser(QwUser qwUser, int delayStart, int delayEnd, Map<String, FsCoursePlaySourceConfig> miniMap, TaskContext ctx,QwRatingConfig ratingConfig) {
         long start1 = System.currentTimeMillis();
         long start1 = System.currentTimeMillis();
         // 获取当前企微待发送记录
         // 获取当前企微待发送记录
         List<QwSopLogs> qwSopLogList = qwSopLogsMapper.selectByQwUserId(qwUser.getId());
         List<QwSopLogs> qwSopLogList = qwSopLogsMapper.selectByQwUserId(qwUser.getId());
@@ -306,51 +314,51 @@ public class SendMsg {
                         }
                         }
                     }
                     }
                 }
                 }
+
                 //查询是否有设置限制客服推送消息次数
                 //查询是否有设置限制客服推送消息次数
 //                    Integer pushCount=pushCountMap.containsKey(String.valueOf(companyId)) ? pushCountMap.get(String.valueOf(companyId)): pushCountMap.getOrDefault(String.valueOf(type), -99);
 //                    Integer pushCount=pushCountMap.containsKey(String.valueOf(companyId)) ? pushCountMap.get(String.valueOf(companyId)): pushCountMap.getOrDefault(String.valueOf(type), -99);
                 int salesPushCustomerMessageCount = qwRestrictionPushRecordMapper.selectQwRestrictionPushRecord(qwUserId, customerId, type, DateUtils.toStartTime(), DateUtils.toEndTime());
                 int salesPushCustomerMessageCount = qwRestrictionPushRecordMapper.selectQwRestrictionPushRecord(qwUserId, customerId, type, DateUtils.toStartTime(), DateUtils.toEndTime());
-                if (pushCount != -99 && salesPushCustomerMessageCount >= pushCount) {
-                    content.setSendStatus(2);//设置发送失败状态
-                    content.setSendRemarks("发送次数达到上限");
-                } else {
-                    // 发送
-                    sendServer.send(content, user, qwSopLogs, miniMap, parentVo);
-                    //判断销售推送成功:保存记录
-                    if (content.getSendStatus() != 2) {
-                        QwRestrictionPushRecord qrpr = new QwRestrictionPushRecord();
-                        qrpr.setType(type);
-                        qrpr.setQwUserId(qwUserId);
-                        qrpr.setQwExternalId(customerId);
-                        qrpr.setCompanyId(companyId);
-                        qrpr.setStatus(1);
-                        qrpr.setCreateTime(DateUtils.getTime());
-                        qrpr.setTime(System.currentTimeMillis());
-                        qwRestrictionPushRecordMapper.insert(qrpr);
+
+                if (CloudHostUtils.hasCloudHostName("今正科技")){
+                    //今正要 通过客户等级 限制发送次数 只管 3个类型的
+                    Integer sendType = qwSopLogs.getSendType();
+                    Integer grade = qwSopLogs.getGrade();
+                    boolean shouldSend = true;
+
+                    if (sendType == 2 || sendType == 5 || sendType == 11) {
+                        if (grade != null) {
+                            Integer gradeLimit = getGradeLimit(grade, ratingConfig);
+                            if (gradeLimit != null) {
+                                if (salesPushCustomerMessageCount >= gradeLimit) {
+                                    content.setSendStatus(2);
+                                    content.setSendRemarks(getGradeLabel(grade) + "级客户发送次数达到上限");
+                                    shouldSend = false;
+                                }
+                            }
+                        }
                     }
                     }
-                    long end4 = System.currentTimeMillis();
-                    log.info("请求pad发送完成:{}, {}, 时长4:{}", user.getQwUserName(), qwSopLogs.getId(), end4 - start4);
-                    if(content.getSendStatus() == 2 && ("请求失败:消息发送过于频繁,请稍后再试".equals(content.getSendRemarks()) || "请求失败:请求频率异常".equals(content.getSendRemarks()))){
-                        QwUser update = new QwUser();
-                        update.setRemark("请求频率异常,暂停发送,三小时后恢复继续发送");
-                        update.setUpdateTime(new Date());
-                        qwUserMapper.update(update, new QueryWrapper<QwUser>().eq("id", user.getId()));
-                        redisCache.setCacheObject("qw:user:id:" + user.getId(), user.getId(), 3, TimeUnit.HOURS);
-                        return;
+
+                    if (shouldSend) {
+                        if (!doSendAndPostProcess(content, user, qwSopLogs, miniMap, parentVo,
+                                type, qwUserId, customerId, companyId, start4, ctx)) {
+                            return;
+                        }
+                    }
+                }
+                else {
+                    if (pushCount != -99 && salesPushCustomerMessageCount >= pushCount) {
+                        content.setSendStatus(2);
+                        content.setSendRemarks("发送次数达到上限");
                     }
                     }
-                    try {
-                        if (ctx.isCancelled()) {
+                    else {
+                        if (!doSendAndPostProcess(content, user, qwSopLogs, miniMap, parentVo,
+                                type, qwUserId, customerId, companyId, start4, ctx)) {
                             return;
                             return;
                         }
                         }
-                        int delay = ThreadLocalRandom.current().nextInt(300, 1000);
-                        log.debug("pad发送消息等待:{}ms", delay);
-                        Thread.sleep(delay);
-                    } catch (InterruptedException e) {
-                        log.error("线程等待错误!");
-                        Thread.currentThread().interrupt();
-                        return;
                     }
                     }
                 }
                 }
             }
             }
+
             // 推送 APP
             // 推送 APP
             boolean hasAppSend = false;
             boolean hasAppSend = false;
             int successCount = 0;
             int successCount = 0;
@@ -487,6 +495,84 @@ public class SendMsg {
         long end3 = System.currentTimeMillis();
         long end3 = System.currentTimeMillis();
         log.info("销售执行完成:{}, 耗时:{}", user.getQwUserName(), end3 - start3);
         log.info("销售执行完成:{}, 耗时:{}", user.getQwUserName(), end3 - start3);
     }
     }
+
+    /**
+     * 发送消息并处理后续逻辑(保存发送记录、频率异常检测、随机等待)
+     * @return true 继续执行,false 需要中断(频率异常或被取消)
+     */
+    private boolean doSendAndPostProcess(QwSopCourseFinishTempSetting.Setting content, QwUser user,
+                                          QwSopLogs qwSopLogs, Map<String, FsCoursePlaySourceConfig> miniMap,
+                                          BaseVo parentVo, Integer type, Long qwUserId, Long customerId,
+                                          Long companyId, long startTime, TaskContext ctx) {
+        sendServer.send(content, user, qwSopLogs, miniMap, parentVo);
+
+        if (content.getSendStatus() != 2) {
+            QwRestrictionPushRecord qrpr = new QwRestrictionPushRecord();
+            qrpr.setType(type);
+            qrpr.setQwUserId(qwUserId);
+            qrpr.setQwExternalId(customerId);
+            qrpr.setCompanyId(companyId);
+            qrpr.setStatus(1);
+            qrpr.setCreateTime(DateUtils.getTime());
+            qrpr.setTime(System.currentTimeMillis());
+            qwRestrictionPushRecordMapper.insert(qrpr);
+        }
+
+        long end4 = System.currentTimeMillis();
+        log.info("请求pad发送完成:{}, {}, 时长4:{}", user.getQwUserName(), qwSopLogs.getId(), end4 - startTime);
+
+        if (content.getSendStatus() == 2 && ("请求失败:消息发送过于频繁,请稍后再试".equals(content.getSendRemarks())
+                || "请求失败:请求频率异常".equals(content.getSendRemarks()))) {
+            QwUser update = new QwUser();
+            update.setRemark("请求频率异常,暂停发送,三小时后恢复继续发送");
+            update.setUpdateTime(new Date());
+            qwUserMapper.update(update, new QueryWrapper<QwUser>().eq("id", user.getId()));
+            redisCache.setCacheObject("qw:user:id:" + user.getId(), user.getId(), 3, TimeUnit.HOURS);
+            return false;
+        }
+
+        try {
+            if (ctx.isCancelled()) {
+                return false;
+            }
+            int delay = ThreadLocalRandom.current().nextInt(300, 1000);
+            log.debug("pad发送消息等待:{}ms", delay);
+            Thread.sleep(delay);
+        } catch (InterruptedException e) {
+            log.error("线程等待错误!");
+            Thread.currentThread().interrupt();
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 根据客户等级获取发送次数上限
+     * @return 上限值,null 表示不限制(非A/B/C/D级客户)
+     */
+    private Integer getGradeLimit(Integer grade, QwRatingConfig ratingConfig) {
+        switch (grade) {
+            case 1: return ratingConfig.getALevelSend();
+            case 2: return ratingConfig.getBLevelSend();
+            case 3: return ratingConfig.getCLevelSend();
+            case 4: return ratingConfig.getDLevelSend();
+            default: return null;
+        }
+    }
+
+    /**
+     * 获取客户等级名称
+     */
+    private String getGradeLabel(Integer grade) {
+        switch (grade) {
+            case 1: return "A";
+            case 2: return "B";
+            case 3: return "C";
+            case 4: return "D";
+            default: return "";
+        }
+    }
+
     /**
     /**
      * 今正cid加微后定时发消息
      * 今正cid加微后定时发消息
      */
      */

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

@@ -813,7 +813,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 }
                 }
                 ruleTimeVO.setType(2);
                 ruleTimeVO.setType(2);
 
 
-                QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null, isOfficial, null, null);
+                QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null, isOfficial, null, null,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,
@@ -845,7 +845,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     String externalUserName = contactId.getExternalUserName();
                     String externalUserName = contactId.getExternalUserName();
                     Long fsUserId = contactId.getFsUserId();
                     Long fsUserId = contactId.getFsUserId();
                     Integer grade = contactId.getGrade();
                     Integer grade = contactId.getGrade();
-                    QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId, isOfficial, contactId.getExternalId(), contactId.getIsDaysNotStudy());
+                    QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId, isOfficial, contactId.getExternalId(), contactId.getIsDaysNotStudy(),grade);
                     handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                     handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                             type, qwUserId, companyUserId, companyId, externalId, welcomeText, qwUserName, fsUserId, false, miniAppId,
                             type, qwUserId, companyUserId, companyId, externalId, welcomeText, qwUserName, fsUserId, false, miniAppId,
                             null, config, miniMap, grade, sendMsgType, companies, liveId,serverId);
                             null, config, miniMap, grade, sendMsgType, companies, liveId,serverId);
@@ -891,14 +891,14 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     private QwSopLogs createBaseLog(String formattedSendTime, SopUserLogsVo logVo,
     private QwSopLogs createBaseLog(String formattedSendTime, SopUserLogsVo logVo,
                                     QwSopRuleTimeVO ruleTimeVO, String externalContactId,
                                     QwSopRuleTimeVO ruleTimeVO, String externalContactId,
                                     String externalUserName, Long fsUserId, Integer isOfficial,
                                     String externalUserName, Long fsUserId, Integer isOfficial,
-                                    Long externalId, Integer isDaysNotStudy) {
+                                    Long externalId, Integer isDaysNotStudy,Integer grade) {
         QwSopLogs sopLogs = new QwSopLogs();
         QwSopLogs sopLogs = new QwSopLogs();
         sopLogs.setSendTime(formattedSendTime);
         sopLogs.setSendTime(formattedSendTime);
         sopLogs.setQwUserid(logVo.getQwUserId());
         sopLogs.setQwUserid(logVo.getQwUserId());
         sopLogs.setCorpId(logVo.getCorpId());
         sopLogs.setCorpId(logVo.getCorpId());
         sopLogs.setLogType(ruleTimeVO.getType());
         sopLogs.setLogType(ruleTimeVO.getType());
         sopLogs.setTakeRecords(0);
         sopLogs.setTakeRecords(0);
-
+        sopLogs.setGrade(grade);
         if (isOfficial != 1 && Integer.valueOf(1).equals(isDaysNotStudy)) {
         if (isOfficial != 1 && Integer.valueOf(1).equals(isDaysNotStudy)) {
             sopLogs.setSendStatus(5L);
             sopLogs.setSendStatus(5L);
             sopLogs.setRemark("E级客户不发送");
             sopLogs.setRemark("E级客户不发送");

+ 19 - 1
fs-qwhook-sop/src/main/java/com/fs/app/controller/QwUserController.java

@@ -5,6 +5,7 @@ import com.fs.common.BeanCopyUtils;
 import com.fs.common.annotation.Log;
 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.R;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.exception.base.BaseException;
@@ -18,6 +19,7 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.param.FsCourseListBySidebarParam;
 import com.fs.course.param.FsCourseListBySidebarParam;
 import com.fs.qw.domain.QwExternalContactInfo;
 import com.fs.qw.domain.QwExternalContactInfo;
+import com.fs.qw.domain.QwSopTempVoiceGroup;
 import com.fs.qw.domain.QwTagGroup;
 import com.fs.qw.domain.QwTagGroup;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.param.ExternalContactDetailsParam;
 import com.fs.qw.param.ExternalContactDetailsParam;
@@ -27,6 +29,7 @@ import com.fs.qw.param.sidebar.TagGroupListParam;
 import com.fs.qw.param.sidebar.TagGroupUpdateParam;
 import com.fs.qw.param.sidebar.TagGroupUpdateParam;
 import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwSopTempVoiceGroupService;
 import com.fs.qw.service.IQwTagGroupService;
 import com.fs.qw.service.IQwTagGroupService;
 import com.fs.qw.vo.ExternalContactDetailsVO;
 import com.fs.qw.vo.ExternalContactDetailsVO;
 import com.fs.qw.vo.QwTagGroupListVO;
 import com.fs.qw.vo.QwTagGroupListVO;
@@ -43,6 +46,7 @@ import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiParam;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
@@ -56,6 +60,9 @@ import java.util.Map;
 @RequestMapping("/app/qw/user")
 @RequestMapping("/app/qw/user")
 public class QwUserController extends BaseController {
 public class QwUserController extends BaseController {
 
 
+    @Autowired
+    private IQwSopTempVoiceGroupService qwSopTempVoiceGroupService;
+
     @Autowired
     @Autowired
     private IQwExternalContactService qwExternalContactService;
     private IQwExternalContactService qwExternalContactService;
 
 
@@ -248,9 +255,20 @@ public class QwUserController extends BaseController {
         }
         }
 
 
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
-        List<QwSopTempVoice> voiceList = sopTempVoiceService.selectQwSopTempVoiceByCompanyUserId(qwUser.getCompanyUserId(),param.getKeyword());
+        List<QwSopTempVoice> voiceList = sopTempVoiceService.selectQwSopTempVoiceByCompanyUserId(qwUser.getCompanyUserId(),param.getKeyword(),param.getGroupId());
         PageInfo<QwSopTempVoice> result = new PageInfo<>(voiceList);
         PageInfo<QwSopTempVoice> result = new PageInfo<>(voiceList);
         return R.ok().put("data", result);
         return R.ok().put("data", result);
     }
     }
 
 
+    /**
+     * 查询语音分类-主要是区分侧边栏语音类型列表
+     */
+    @PostMapping("/voiceGroupList")
+    public TableDataInfo list(@RequestBody QwSopTempVoiceGroup qwSopTempVoiceGroup)
+    {
+        startPage();
+        List<QwSopTempVoiceGroup> list = qwSopTempVoiceGroupService.selectQwSopTempVoiceGroupList(qwSopTempVoiceGroup);
+        return getDataTable(list);
+    }
+
 }
 }

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

@@ -34,6 +34,14 @@ public interface FsUserCourseVideoMapper extends BaseMapper<FsUserCourseVideo> {
      */
      */
     public FsUserCourseVideo selectFsUserCourseVideoByVideoId(Long videoId);
     public FsUserCourseVideo selectFsUserCourseVideoByVideoId(Long videoId);
 
 
+    /**
+     * 批量查询课堂视频
+     *
+     * @param videoIds 课堂视频主键集合
+     * @return 课堂视频集合
+     */
+    List<FsUserCourseVideo> selectFsUserCourseVideoByVideoIds(@Param("videoIds") List<Long> videoIds);
+
     @Select("select video_id,course_id from fs_user_course_video " +
     @Select("select video_id,course_id from fs_user_course_video " +
             "where is_del = #{data.isDel} " +
             "where is_del = #{data.isDel} " +
             "and is_pause = #{data.isPause} " +
             "and is_pause = #{data.isPause} " +

+ 5 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseListBySidebarParam.java

@@ -46,4 +46,9 @@ public class FsCourseListBySidebarParam extends BaseEntity {
 
 
     // 是否上架 0:上架,1:下架
     // 是否上架 0:上架,1:下架
     private Integer isOnPut;
     private Integer isOnPut;
+
+    /**
+    * 分组
+    */
+    private Long groupId;
 }
 }

+ 12 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -304,6 +304,14 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         return fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
         return fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
     }
     }
 
 
+    @Override
+    public List<FsUserCourseVideo> selectFsUserCourseVideoByVideoIdS(List<Long> videoIds) {
+        if (videoIds == null || videoIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoIds(videoIds);
+    }
+
     @Override
     @Override
     public FsUserCourseVideoTagVO selectFsUserCourseVideoByCourseVideoId(Long videoId) {
     public FsUserCourseVideoTagVO selectFsUserCourseVideoByCourseVideoId(Long videoId) {
         return fsUserCourseVideoMapper.selectFsUserCourseVideoByCourseVideoId(videoId);
         return fsUserCourseVideoMapper.selectFsUserCourseVideoByCourseVideoId(videoId);
@@ -2547,6 +2555,8 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         FsUser userMap = new FsUser();
         FsUser userMap = new FsUser();
         userMap.setUserId(user.getUserId());
         userMap.setUserId(user.getUserId());
         userMap.setIntegral(user.getIntegral() + answerIntegral);
         userMap.setIntegral(user.getIntegral() + answerIntegral);
+        // 增加可提现积分
+        userMap.setWithdrawIntegral(user.getWithdrawIntegral() + answerIntegral);
         fsUserMapper.updateFsUser(userMap);
         fsUserMapper.updateFsUser(userMap);
 
 
         // 记录积分日志
         // 记录积分日志
@@ -2557,6 +2567,8 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         integralLogs.setLogType(17);
         integralLogs.setLogType(17);
         integralLogs.setBusinessId(StringUtils.isNotEmpty(log.getLogId().toString()) ? log.getLogId().toString() : null);
         integralLogs.setBusinessId(StringUtils.isNotEmpty(log.getLogId().toString()) ? log.getLogId().toString() : null);
         integralLogs.setCreateTime(new Date());
         integralLogs.setCreateTime(new Date());
+        integralLogs.setNickName(user.getNickName());
+        integralLogs.setPhone(user.getPhone());
         fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
         fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
 
 
         //更新看课记录的奖励类型
         //更新看课记录的奖励类型

+ 8 - 0
fs-service/src/main/java/com/fs/his/param/FindUserByParam.java

@@ -9,4 +9,12 @@ public class FindUserByParam implements Serializable {
     private Integer pageNum;
     private Integer pageNum;
     private Integer pageSize;
     private Integer pageSize;
     private String keywords;
     private String keywords;
+
+    /** 是否仅查询销售/医生:客户搜索时为 true,销售/其他用户不受影响 */
+    private Boolean onlySales;
+    /**
+     * 客户搜索前缀:C=销售,D=医生。
+     * 仅 onlySales=true 时生效,由服务端根据 keywords 解析设置
+     */
+    private String searchPrefix;
 }
 }

+ 23 - 0
fs-service/src/main/java/com/fs/his/vo/ExchangeDetailVo.java

@@ -0,0 +1,23 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 积分兑换佣金明细
+ */
+
+@Data
+public class ExchangeDetailVo {
+    private  Long id;
+    private  Long  userId;
+    private Integer logType;
+    private Long integral;
+    private Long balance;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+    private BigDecimal commission;
+}

+ 24 - 0
fs-service/src/main/java/com/fs/qw/domain/QwSopTempVoiceGroup.java

@@ -0,0 +1,24 @@
+package com.fs.qw.domain;
+
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 语音分类-主要是区分侧边栏语音类型对象 qw_sop_temp_voice_group
+ *
+ * @author fs
+ * @date 2026-07-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class QwSopTempVoiceGroup extends BaseEntity {
+
+    private Long id;
+
+    private String voiceGroupName;
+
+
+}

+ 4 - 0
fs-service/src/main/java/com/fs/qw/domain/QwWorkTask.java

@@ -1,5 +1,6 @@
 package com.fs.qw.domain;
 package com.fs.qw.domain;
 
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
 import com.fs.common.annotation.Excel;
 import io.swagger.models.auth.In;
 import io.swagger.models.auth.In;
@@ -82,4 +83,7 @@ public class QwWorkTask extends BaseEntity{
 
 
     private Long videoId;
     private Long videoId;
 
 
+    @TableField(exist = false)
+    private String videoName;
+
 }
 }

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

@@ -86,7 +86,7 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
 
 
     public int batchUpdateQwExternalContactByMoreStudy(List<QwExternalContact> qwExternalContact);
     public int batchUpdateQwExternalContactByMoreStudy(List<QwExternalContact> qwExternalContact);
     public int batchUpdateQwExternalByIsDaysNotStudy(List<QwExternalContact> qwExternalContact);
     public int batchUpdateQwExternalByIsDaysNotStudy(List<QwExternalContact> qwExternalContact);
-
+    public int batchUpdateQwExternalLevelById(QwExternalContactUpdateLevelParam param);
 
 
     @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/qw/mapper/QwRestrictionPushRecordMapper.java

@@ -37,6 +37,12 @@ public interface QwRestrictionPushRecordMapper extends BaseMapper<QwRestrictionP
                                       @Param("startTime") Long startTime,
                                       @Param("startTime") Long startTime,
                                       @Param("endTime") Long endTime);
                                       @Param("endTime") Long endTime);
 
 
+    @Select("select count(id) from qw_restriction_push_record where qw_user_id=#{qwUserId} and qw_external_id=#{qwExternalId} and time>#{startTime} and time<=#{endTime}")
+    int selectQwRestrictionPushRecordByExternalId(@Param("qwUserId") Long qwUserId,
+                                      @Param("qwExternalId") Long qwExternalId,
+                                      @Param("startTime") Long startTime,
+                                      @Param("endTime") Long endTime);
+
     @Select("select id from qw_restriction_push_record where time<#{time}")
     @Select("select id from qw_restriction_push_record where time<#{time}")
     List<Long> selectExpirePushRecord(@Param("time") Long time);
     List<Long> selectExpirePushRecord(@Param("time") Long time);
 
 

+ 66 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwSopTempVoiceGroupMapper.java

@@ -0,0 +1,66 @@
+package com.fs.qw.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
+import com.fs.qw.domain.QwSopTempVoiceGroup;
+
+import java.util.List;
+
+/**
+ * 语音分类-主要是区分侧边栏语音类型Mapper接口
+ *
+ * @author fs
+ * @date 2026-07-16
+ */
+@DataSource(DataSourceType.SOP)
+public interface QwSopTempVoiceGroupMapper extends BaseMapper<QwSopTempVoiceGroup> {
+    /**
+     * 查询语音分类-主要是区分侧边栏语音类型
+     *
+     * @param id 语音分类-主要是区分侧边栏语音类型主键
+     * @return 语音分类-主要是区分侧边栏语音类型
+     */
+    QwSopTempVoiceGroup selectQwSopTempVoiceGroupById(Long id);
+
+    /**
+     * 查询语音分类-主要是区分侧边栏语音类型列表
+     *
+     * @param qwSopTempVoiceGroup 语音分类-主要是区分侧边栏语音类型
+     * @return 语音分类-主要是区分侧边栏语音类型集合
+     */
+    List<QwSopTempVoiceGroup> selectQwSopTempVoiceGroupList(QwSopTempVoiceGroup qwSopTempVoiceGroup);
+
+    /**
+     * 新增语音分类-主要是区分侧边栏语音类型
+     *
+     * @param qwSopTempVoiceGroup 语音分类-主要是区分侧边栏语音类型
+     * @return 结果
+     */
+    int insertQwSopTempVoiceGroup(QwSopTempVoiceGroup qwSopTempVoiceGroup);
+
+    /**
+     * 修改语音分类-主要是区分侧边栏语音类型
+     *
+     * @param qwSopTempVoiceGroup 语音分类-主要是区分侧边栏语音类型
+     * @return 结果
+     */
+    int updateQwSopTempVoiceGroup(QwSopTempVoiceGroup qwSopTempVoiceGroup);
+
+    /**
+     * 删除语音分类-主要是区分侧边栏语音类型
+     *
+     * @param id 语音分类-主要是区分侧边栏语音类型主键
+     * @return 结果
+     */
+    int deleteQwSopTempVoiceGroupById(Long id);
+
+    /**
+     * 批量删除语音分类-主要是区分侧边栏语音类型
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteQwSopTempVoiceGroupByIds(Long[] ids);
+}

+ 16 - 0
fs-service/src/main/java/com/fs/qw/param/QwExternalContactUpdateLevelParam.java

@@ -0,0 +1,16 @@
+package com.fs.qw.param;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class QwExternalContactUpdateLevelParam {
+
+    private List<Long> userIds;
+    private Integer level;
+
+    private boolean filter;
+    private QwExternalContactParam param;
+
+}

+ 63 - 0
fs-service/src/main/java/com/fs/qw/service/IQwSopTempVoiceGroupService.java

@@ -0,0 +1,63 @@
+package com.fs.qw.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.qw.domain.QwSopTempVoiceGroup;
+
+import java.util.List;
+
+/**
+ * 语音分类-主要是区分侧边栏语音类型Service接口
+ *
+ * @author fs
+ * @date 2026-07-16
+ */
+public interface IQwSopTempVoiceGroupService extends IService<QwSopTempVoiceGroup> {
+    /**
+     * 查询语音分类-主要是区分侧边栏语音类型
+     *
+     * @param id 语音分类-主要是区分侧边栏语音类型主键
+     * @return 语音分类-主要是区分侧边栏语音类型
+     */
+    QwSopTempVoiceGroup selectQwSopTempVoiceGroupById(Long id);
+
+    /**
+     * 查询语音分类-主要是区分侧边栏语音类型列表
+     *
+     * @param qwSopTempVoiceGroup 语音分类-主要是区分侧边栏语音类型
+     * @return 语音分类-主要是区分侧边栏语音类型集合
+     */
+    List<QwSopTempVoiceGroup> selectQwSopTempVoiceGroupList(QwSopTempVoiceGroup qwSopTempVoiceGroup);
+
+    /**
+     * 新增语音分类-主要是区分侧边栏语音类型
+     *
+     * @param qwSopTempVoiceGroup 语音分类-主要是区分侧边栏语音类型
+     * @return 结果
+     */
+    int insertQwSopTempVoiceGroup(QwSopTempVoiceGroup qwSopTempVoiceGroup);
+
+    /**
+     * 修改语音分类-主要是区分侧边栏语音类型
+     *
+     * @param qwSopTempVoiceGroup 语音分类-主要是区分侧边栏语音类型
+     * @return 结果
+     */
+    int updateQwSopTempVoiceGroup(QwSopTempVoiceGroup qwSopTempVoiceGroup);
+
+    /**
+     * 批量删除语音分类-主要是区分侧边栏语音类型
+     *
+     * @param ids 需要删除的语音分类-主要是区分侧边栏语音类型主键集合
+     * @return 结果
+     */
+    int deleteQwSopTempVoiceGroupByIds(Long[] ids);
+
+    /**
+     * 删除语音分类-主要是区分侧边栏语音类型信息
+     *
+     * @param id 语音分类-主要是区分侧边栏语音类型主键
+     * @return 结果
+     */
+    int deleteQwSopTempVoiceGroupById(Long id);
+}

+ 3 - 4
fs-service/src/main/java/com/fs/qw/service/IQwUserServiceAsyncHelper.java

@@ -1,10 +1,7 @@
 package com.fs.qw.service;
 package com.fs.qw.service;
 
 
 import com.fs.company.domain.Company;
 import com.fs.company.domain.Company;
-import com.fs.qw.param.QwExternalContactAddTagParam;
-import com.fs.qw.param.QwExternalContactUpdateNoteParam;
-import com.fs.qw.param.ResignedTransferParam;
-import com.fs.qw.param.TransferParam;
+import com.fs.qw.param.*;
 import org.codehaus.jettison.json.JSONException;
 import org.codehaus.jettison.json.JSONException;
 
 
 import java.io.IOException;
 import java.io.IOException;
@@ -32,6 +29,8 @@ public interface IQwUserServiceAsyncHelper {
      */
      */
     void batchUpdateExternalContactNotes(QwExternalContactUpdateNoteParam param);
     void batchUpdateExternalContactNotes(QwExternalContactUpdateNoteParam param);
 
 
+    void batchUpdateExternalContactLevelById(QwExternalContactUpdateLevelParam param);
+
     /**
     /**
      * 添加标签
      * 添加标签
      * @param param
      * @param param

+ 95 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwSopTempVoiceGroupServiceImpl.java

@@ -0,0 +1,95 @@
+package com.fs.qw.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.qw.domain.QwSopTempVoiceGroup;
+import com.fs.qw.mapper.QwSopTempVoiceGroupMapper;
+import com.fs.qw.service.IQwSopTempVoiceGroupService;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 语音分类-主要是区分侧边栏语音类型Service业务层处理
+ *
+ * @author fs
+ * @date 2026-07-16
+ */
+@Service
+public class QwSopTempVoiceGroupServiceImpl extends ServiceImpl<QwSopTempVoiceGroupMapper, QwSopTempVoiceGroup> implements IQwSopTempVoiceGroupService {
+
+    /**
+     * 查询语音分类-主要是区分侧边栏语音类型
+     *
+     * @param id 语音分类-主要是区分侧边栏语音类型主键
+     * @return 语音分类-主要是区分侧边栏语音类型
+     */
+    @Override
+    public QwSopTempVoiceGroup selectQwSopTempVoiceGroupById(Long id)
+    {
+        return baseMapper.selectQwSopTempVoiceGroupById(id);
+    }
+
+    /**
+     * 查询语音分类-主要是区分侧边栏语音类型列表
+     *
+     * @param qwSopTempVoiceGroup 语音分类-主要是区分侧边栏语音类型
+     * @return 语音分类-主要是区分侧边栏语音类型
+     */
+    @Override
+    public List<QwSopTempVoiceGroup> selectQwSopTempVoiceGroupList(QwSopTempVoiceGroup qwSopTempVoiceGroup)
+    {
+        return baseMapper.selectQwSopTempVoiceGroupList(qwSopTempVoiceGroup);
+    }
+
+    /**
+     * 新增语音分类-主要是区分侧边栏语音类型
+     *
+     * @param qwSopTempVoiceGroup 语音分类-主要是区分侧边栏语音类型
+     * @return 结果
+     */
+    @Override
+    public int insertQwSopTempVoiceGroup(QwSopTempVoiceGroup qwSopTempVoiceGroup)
+    {
+        qwSopTempVoiceGroup.setCreateTime(new Date());
+        return baseMapper.insertQwSopTempVoiceGroup(qwSopTempVoiceGroup);
+    }
+
+    /**
+     * 修改语音分类-主要是区分侧边栏语音类型
+     *
+     * @param qwSopTempVoiceGroup 语音分类-主要是区分侧边栏语音类型
+     * @return 结果
+     */
+    @Override
+    public int updateQwSopTempVoiceGroup(QwSopTempVoiceGroup qwSopTempVoiceGroup)
+    {
+        qwSopTempVoiceGroup.setUpdateTime(new Date());
+        return baseMapper.updateQwSopTempVoiceGroup(qwSopTempVoiceGroup);
+    }
+
+    /**
+     * 批量删除语音分类-主要是区分侧边栏语音类型
+     *
+     * @param ids 需要删除的语音分类-主要是区分侧边栏语音类型主键
+     * @return 结果
+     */
+    @Override
+    public int deleteQwSopTempVoiceGroupByIds(Long[] ids)
+    {
+        return baseMapper.deleteQwSopTempVoiceGroupByIds(ids);
+    }
+
+    /**
+     * 删除语音分类-主要是区分侧边栏语音类型信息
+     *
+     * @param id 语音分类-主要是区分侧边栏语音类型主键
+     * @return 结果
+     */
+    @Override
+    public int deleteQwSopTempVoiceGroupById(Long id)
+    {
+        return baseMapper.deleteQwSopTempVoiceGroupById(id);
+    }
+}

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

@@ -28,6 +28,7 @@ import com.fs.sop.mapper.SopUserLogsInfoMapper;
 import com.fs.sop.params.DeleteQwSopParam;
 import com.fs.sop.params.DeleteQwSopParam;
 import com.fs.sop.params.QwSopAutoByTags;
 import com.fs.sop.params.QwSopAutoByTags;
 import com.fs.sop.params.SopUserLogsParamByDate;
 import com.fs.sop.params.SopUserLogsParamByDate;
+import com.fs.sop.service.ISopUserLogsInfoService;
 import com.fs.voice.utils.StringUtil;
 import com.fs.voice.utils.StringUtil;
 import com.fs.watch.mapper.WatchCompanyUserMapper;
 import com.fs.watch.mapper.WatchCompanyUserMapper;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
@@ -102,6 +103,9 @@ public class QwUserServiceAsyncHelperImpl implements IQwUserServiceAsyncHelper {
     @Autowired
     @Autowired
     private AsyncQwAiChatSopService asyncQwAiChatSopService;
     private AsyncQwAiChatSopService asyncQwAiChatSopService;
 
 
+    @Autowired
+    private ISopUserLogsInfoService iSopUserLogsInfoService;
+
     org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());
     org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());
 
 
     /**
     /**
@@ -299,6 +303,14 @@ public class QwUserServiceAsyncHelperImpl implements IQwUserServiceAsyncHelper {
         }
         }
     }
     }
 
 
+    @Override
+    @Async("qwHandleExec")
+    public void batchUpdateExternalContactLevelById(QwExternalContactUpdateLevelParam param) {
+
+        qwExternalContactMapper.batchUpdateQwExternalLevelById(param);
+        iSopUserLogsInfoService.batchUpdateSopUserLogsInfoByIsLevel(param);
+    }
+
     private String getErrorMsg(Integer code) {
     private String getErrorMsg(Integer code) {
 
 
         String msg = "";
         String msg = "";

+ 26 - 10
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -23,6 +23,8 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.config.ai.AiHostProper;
 import com.fs.config.ai.AiHostProper;
 import com.fs.config.cloud.CloudHostProper;
 import com.fs.config.cloud.CloudHostProper;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.domain.FsDfAccount;
 import com.fs.his.domain.FsDfAccount;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.ConfigUtil;
@@ -123,6 +125,9 @@ public class QwUserServiceImpl implements IQwUserService
     @Autowired
     @Autowired
     IQwExternalContactService externalContactService;
     IQwExternalContactService externalContactService;
 
 
+    @Autowired
+    IFsUserCourseVideoService iFsUserCourseVideoService;
+
     @Autowired
     @Autowired
     IpadSendUtils ipadSendUtils;
     IpadSendUtils ipadSendUtils;
 
 
@@ -254,16 +259,27 @@ public class QwUserServiceImpl implements IQwUserService
         }
         }
 
 
 
 
-//        for (QwWorkTask qwWorkTask : qwWorkTasks) {
-//            if(qwWorkTask.getExtId() != null) {
-//                QwExternalContact qwExternalContact = externalContactService.selectQwExternalContactById(qwWorkTask.getExtId());
-//                if(qwExternalContact != null) {
-//                    qwWorkTask.setName(qwExternalContact.getName());
-//                    qwWorkTask.setAvatar(qwExternalContact.getAvatar());
-//                    qwWorkTask.setExternalContactId(qwExternalContact.getExternalUserId());
-//                }
-//            }
-//        }
+        // 提取非空的 videoId 列表,批量查询视频标题并填充 videoName
+        List<Long> videoIds = qwWorkTasks.stream()
+                .map(QwWorkTask::getVideoId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (!videoIds.isEmpty()) {
+            Map<Long, String> videoTitleMap = iFsUserCourseVideoService.selectFsUserCourseVideoByVideoIdS(videoIds)
+                    .stream()
+                    .collect(Collectors.toMap(
+                            FsUserCourseVideo::getVideoId,
+                            FsUserCourseVideo::getTitle
+                    ));
+
+            qwWorkTasks.forEach(task -> {
+                Optional.ofNullable(task.getVideoId())
+                        .map(videoTitleMap::get)
+                        .ifPresent(task::setVideoName);
+            });
+        }
+
         return qwWorkTasks;
         return qwWorkTasks;
     }
     }
 
 

+ 31 - 1
fs-service/src/main/java/com/fs/qw/service/impl/QwWorkTaskServiceImpl.java

@@ -2,8 +2,10 @@ package com.fs.qw.service.impl;
 
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.DateUtils;
+import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
+import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.vo.FsCourseWatchLogTaskVO;
 import com.fs.course.vo.FsCourseWatchLogTaskVO;
 import com.fs.course.vo.FsQwCourseWatchLogVO;
 import com.fs.course.vo.FsQwCourseWatchLogVO;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwExternalContact;
@@ -50,6 +52,10 @@ public class QwWorkTaskServiceImpl extends ServiceImpl<QwWorkTaskMapper, QwWorkT
     SopUserLogsInfoMapper sopUserLogsInfoMapper;
     SopUserLogsInfoMapper sopUserLogsInfoMapper;
     @Autowired
     @Autowired
     QwExternalContactMapper qwExternalContactMapper;
     QwExternalContactMapper qwExternalContactMapper;
+
+    @Autowired
+    IFsUserCourseVideoService iFsUserCourseVideoService;
+
     /**
     /**
      * 查询企微任务看板
      * 查询企微任务看板
      *
      *
@@ -246,7 +252,31 @@ public class QwWorkTaskServiceImpl extends ServiceImpl<QwWorkTaskMapper, QwWorkT
 
 
     @Override
     @Override
     public List<QwWorkTaskListVO> selectQwWorkTaskListVO(QwWorkTaskListParam qwWorkTask) {
     public List<QwWorkTaskListVO> selectQwWorkTaskListVO(QwWorkTaskListParam qwWorkTask) {
-        return qwWorkTaskMapper.selectQwWorkTaskListVO(qwWorkTask);
+
+        List<QwWorkTaskListVO> qwWorkTaskListVOS = qwWorkTaskMapper.selectQwWorkTaskListVO(qwWorkTask);
+
+        // 提取非空的 videoId 列表,批量查询视频标题并填充 videoName
+        List<Long> videoIds = qwWorkTaskListVOS.stream()
+                .map(QwWorkTaskListVO::getVideoId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (!videoIds.isEmpty()) {
+            Map<Long, String> videoTitleMap = iFsUserCourseVideoService.selectFsUserCourseVideoByVideoIdS(videoIds)
+                    .stream()
+                    .collect(Collectors.toMap(
+                            FsUserCourseVideo::getVideoId,
+                            FsUserCourseVideo::getTitle
+                    ));
+
+            qwWorkTaskListVOS.forEach(task -> {
+                Optional.ofNullable(task.getVideoId())
+                        .map(videoTitleMap::get)
+                        .ifPresent(task::setVideoName);
+            });
+        }
+
+        return qwWorkTaskListVOS;
     }
     }
 
 
     //看课中断
     //看课中断

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

@@ -166,6 +166,7 @@ public class QwExternalContactVO {
     /**
     /**
      * 是否下载APP
      * 是否下载APP
      */
      */
+    @Excel(name = "是否下载APP 1是 2否")
     private Integer isDownloadApp;
     private Integer isDownloadApp;
 
 
     /**
     /**

+ 2 - 0
fs-service/src/main/java/com/fs/qw/vo/QwWorkTaskListVO.java

@@ -50,6 +50,8 @@ public class QwWorkTaskListVO {
 
 
     /** 用户id */
     /** 用户id */
     private Long companyUserId;
     private Long companyUserId;
+    private Long videoId;
+    private String videoName;
     /**
     /**
      * 销售名称
      * 销售名称
      */
      */

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

@@ -67,6 +67,7 @@ public class QwSopLogs implements Serializable {
     */
     */
     private Integer sendType;
     private Integer sendType;
 
 
+
     /**
     /**
     *  qw_sop表主键
     *  qw_sop表主键
     */
     */
@@ -117,6 +118,11 @@ public class QwSopLogs implements Serializable {
      */
      */
     private Long smsLogsId;
     private Long smsLogsId;
 
 
+    /**
+    * 客户等级
+    */
+    private Integer grade;
+
     // 构造函数
     // 构造函数
 //    public QwSopLogs() {
 //    public QwSopLogs() {
 //        this.id = UUID.randomUUID().toString();
 //        this.id = UUID.randomUUID().toString();

+ 2 - 0
fs-service/src/main/java/com/fs/sop/domain/QwSopTempVoice.java

@@ -66,6 +66,8 @@ public class QwSopTempVoice{
 
 
     private Long qwUserId;
     private Long qwUserId;
 
 
+    private Long groupId;
+
     private String voicePrintUrl;
     private String voicePrintUrl;
 
 
 }
 }

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

@@ -111,7 +111,7 @@ public interface QwSopTempVoiceMapper extends BaseMapper<QwSopTempVoice>{
     int updateBatchWithDataSource(@Param("list") List<QwSopTempVoice> list);
     int updateBatchWithDataSource(@Param("list") List<QwSopTempVoice> list);
 
 
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
-    List<QwSopTempVoice> selectQwSopTempVoiceByCompanyUserId(@Param("companyUserId") Long companyUserId,@Param("keyword") String keyword);
+    List<QwSopTempVoice> selectQwSopTempVoiceByCompanyUserId(@Param("companyUserId") Long companyUserId,@Param("keyword") String keyword,@Param("groupId") Long groupId);
 
 
 
 
 }
 }

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

@@ -4,6 +4,7 @@ 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.domain.QwExternalContact;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
+import com.fs.qw.param.QwExternalContactUpdateLevelParam;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.params.BatchSopUserLogsInfoParamU;
 import com.fs.sop.params.BatchSopUserLogsInfoParamU;
 import com.fs.sop.params.DeleteQwSopParam;
 import com.fs.sop.params.DeleteQwSopParam;
@@ -206,7 +207,25 @@ public interface SopUserLogsInfoMapper {
             "</script>")
             "</script>")
     void batchUpdateSopUserLogsInfoByLevel(@Param("contactList") List<QwExternalContact> contactList);
     void batchUpdateSopUserLogsInfoByLevel(@Param("contactList") List<QwExternalContact> contactList);
 
 
-
+    @DataSource(DataSourceType.SOP)
+    @Update("<script>" +
+            "UPDATE sop_user_logs_info " +
+            "SET " +
+            "    is_days_not_study = CASE " +
+            "        WHEN #{map.level} = 5 THEN 1 " +
+            "        WHEN #{map.level} = 9 THEN 0 " +
+            "        ELSE 0 " +
+            "    END, " +
+            "    grade = CASE " +
+            "        WHEN #{map.level} = 9 THEN 0 " +
+            "        ELSE #{map.level} " +
+            "    END " +
+            "WHERE external_id IN " +
+            "<foreach collection='map.userIds' item='item' open='(' separator=',' close=')'>" +
+            "    #{item} " +
+            "</foreach>" +
+            "</script>")
+    void batchUpdateSopUserLogsInfoByIsLevel(@Param("map") QwExternalContactUpdateLevelParam param);
 
 
 
 
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)

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

@@ -118,7 +118,7 @@ public interface SopUserLogsMapper {
 
 
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
     @Select("<script>" +
     @Select("<script>" +
-            "select li.sop_id,li.external_contact_id,li.qw_user_id,li.corp_id,li.external_id,li.external_user_name,li.fs_user_id,ul.start_time,li.is_days_not_study " +
+            "select li.sop_id,li.external_contact_id,li.qw_user_id,li.corp_id,li.external_id,li.external_user_name,li.fs_user_id,ul.start_time,li.is_days_not_study,li.grade " +
             "from sop_user_logs ul  " +
             "from sop_user_logs ul  " +
             "INNER join  sop_user_logs_info li on ul.id=li.user_logs_id " +
             "INNER join  sop_user_logs_info li on ul.id=li.user_logs_id " +
             "        WHERE ul.id IN\n" +
             "        WHERE ul.id IN\n" +

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

@@ -8,10 +8,14 @@ public class QwRatingConfig {
     private Integer notStudyDays;
     private Integer notStudyDays;
     private Integer aLevelMin;
     private Integer aLevelMin;
     private Integer aLevelMax;
     private Integer aLevelMax;
+    private Integer aLevelSend;
     private Integer bLevelMin;
     private Integer bLevelMin;
     private Integer bLevelMax;
     private Integer bLevelMax;
+    private Integer bLevelSend;
     private Integer cLevelMin;
     private Integer cLevelMin;
     private Integer cLevelMax;
     private Integer cLevelMax;
+    private Integer cLevelSend;
     private Integer dLevelMin;
     private Integer dLevelMin;
     private Integer dLevelMax;
     private Integer dLevelMax;
+    private Integer dLevelSend;
 }
 }

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

@@ -103,5 +103,5 @@ public interface IQwSopTempVoiceService extends IService<QwSopTempVoice>{
 
 
     int updateBatchWithDataSource(List<QwSopTempVoice> list);
     int updateBatchWithDataSource(List<QwSopTempVoice> list);
 
 
-    List<QwSopTempVoice> selectQwSopTempVoiceByCompanyUserId(Long companyUserId,String keyword);
+    List<QwSopTempVoice> selectQwSopTempVoiceByCompanyUserId(Long companyUserId,String keyword,Long groupId);
 }
 }

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

@@ -3,6 +3,7 @@ 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.domain.QwExternalContact;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
+import com.fs.qw.param.QwExternalContactUpdateLevelParam;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.params.BatchSopUserLogsInfoParam;
 import com.fs.sop.params.BatchSopUserLogsInfoParam;
 import com.fs.sop.params.SendUserLogsInfoMsgParam;
 import com.fs.sop.params.SendUserLogsInfoMsgParam;
@@ -57,9 +58,13 @@ public interface ISopUserLogsInfoService {
     void batchInsertSopUserLogsInfo(List<SopUserLogsInfo> logsToInsert);
     void batchInsertSopUserLogsInfo(List<SopUserLogsInfo> logsToInsert);
 
 
     void batchUpdateSopUserLogsInfoByMoreStudy(List<QwExternalContact> contactList);
     void batchUpdateSopUserLogsInfoByMoreStudy(List<QwExternalContact> contactList);
+
     void batchUpdateSopUserLogsInfoByIsDaysNotStudy(List<QwExternalContact> contactList);
     void batchUpdateSopUserLogsInfoByIsDaysNotStudy(List<QwExternalContact> contactList);
+
     void batchUpdateSopUserLogsInfoByLevel(List<QwExternalContact> contactList);
     void batchUpdateSopUserLogsInfoByLevel(List<QwExternalContact> contactList);
 
 
+    void batchUpdateSopUserLogsInfoByIsLevel(QwExternalContactUpdateLevelParam param);
+
     void insertSopUserLogsInfo(SopUserLogsInfo logsToInsert);
     void insertSopUserLogsInfo(SopUserLogsInfo logsToInsert);
     /**
     /**
      * 查询sopUserLogsInfo
      * 查询sopUserLogsInfo

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

@@ -113,6 +113,7 @@ public class QwSopTempVoiceServiceImpl extends ServiceImpl<QwSopTempVoiceMapper,
             newVoice.setCompanyId(companyUser.getCompanyId());
             newVoice.setCompanyId(companyUser.getCompanyId());
             newVoice.setVoiceTxt(tempVoice.getVoiceTxt());
             newVoice.setVoiceTxt(tempVoice.getVoiceTxt());
             newVoice.setRecordType(0);
             newVoice.setRecordType(0);
+            newVoice.setGroupId(tempVoice.getGroupId());
             insertList.add(newVoice);
             insertList.add(newVoice);
 
 
             // 分批插入,避免SQL过长
             // 分批插入,避免SQL过长
@@ -427,7 +428,7 @@ public class QwSopTempVoiceServiceImpl extends ServiceImpl<QwSopTempVoiceMapper,
 
 
     @Override
     @Override
     @DataSource(DataSourceType.SOP)
     @DataSource(DataSourceType.SOP)
-    public List<QwSopTempVoice> selectQwSopTempVoiceByCompanyUserId(Long companyUserId,String keyword) {
-        return qwSopTempVoiceMapper.selectQwSopTempVoiceByCompanyUserId(companyUserId ,keyword);
+    public List<QwSopTempVoice> selectQwSopTempVoiceByCompanyUserId(Long companyUserId,String keyword,Long groupId) {
+        return qwSopTempVoiceMapper.selectQwSopTempVoiceByCompanyUserId(companyUserId ,keyword,groupId);
     }
     }
 }
 }

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

@@ -42,6 +42,7 @@ import com.fs.live.mapper.LiveWatchLogMapper;
 import com.fs.qw.domain.*;
 import com.fs.qw.domain.*;
 import com.fs.qw.mapper.*;
 import com.fs.qw.mapper.*;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
 import com.fs.qw.param.QwExtCourseSopWatchLog;
+import com.fs.qw.param.QwExternalContactUpdateLevelParam;
 import com.fs.qw.param.QwExternalContactVOTime;
 import com.fs.qw.param.QwExternalContactVOTime;
 import com.fs.qw.param.QwTagSearchParam;
 import com.fs.qw.param.QwTagSearchParam;
 import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.service.IQwCompanyService;
@@ -307,6 +308,11 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
         sopUserLogsInfoMapper.batchUpdateSopUserLogsInfoByLevel(contactList);
         sopUserLogsInfoMapper.batchUpdateSopUserLogsInfoByLevel(contactList);
     }
     }
 
 
+    @Override
+    public void batchUpdateSopUserLogsInfoByIsLevel(QwExternalContactUpdateLevelParam param) {
+        sopUserLogsInfoMapper.batchUpdateSopUserLogsInfoByIsLevel(param);
+    }
+
     @Override
     @Override
     public void insertSopUserLogsInfo(SopUserLogsInfo logsToInsert) {
     public void insertSopUserLogsInfo(SopUserLogsInfo logsToInsert) {
         sopUserLogsInfoMapper.insertSopUserLogsInfo(logsToInsert);
         sopUserLogsInfoMapper.insertSopUserLogsInfo(logsToInsert);
@@ -635,7 +641,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     }
                     }
                     sopLogs.setExternalUserName(groupUser.getName());
                     sopLogs.setExternalUserName(groupUser.getName());
                     sopLogs.setQwUserKey(qwUser.getId());
                     sopLogs.setQwUserKey(qwUser.getId());
-
+                    sopLogs.setGrade(null);
                     // 设置实际发送人
                     // 设置实际发送人
                     updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId(),groupUser.getChatId());
                     updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId(),groupUser.getChatId());
                     //域名
                     //域名
@@ -1006,6 +1012,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                     }
                     }
                     sopLogs.setExternalUserName(groupChat.getName());
                     sopLogs.setExternalUserName(groupChat.getName());
                     sopLogs.setQwUserKey(qwUser.getId());
                     sopLogs.setQwUserKey(qwUser.getId());
+                    sopLogs.setGrade(null);
                     // 设置实际发送人
                     // 设置实际发送人
                     updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId(),groupChat.getChatId());
                     updateQwUserKey(sopLogs,qwUser.getQwUserId(),param.getSopId(),groupChat.getChatId());
 
 
@@ -1302,6 +1309,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                 sopLogs.setSort(30000000);
                 sopLogs.setSort(30000000);
                 sopLogs.setSendType(5);
                 sopLogs.setSendType(5);
                 sopLogs.setExternalUserName(item.getExternalUserName());
                 sopLogs.setExternalUserName(item.getExternalUserName());
+                sopLogs.setGrade(item.getGrade());
                 if(sendLiveMsgFinal){
                 if(sendLiveMsgFinal){
                     sopLogs.setSendType(20);
                     sopLogs.setSendType(20);
                 }
                 }
@@ -2083,7 +2091,7 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
             sopLogs.setSendType(finalSendType);
             sopLogs.setSendType(finalSendType);
 
 
             sopLogs.setExternalUserName(item.getExternalUserName());
             sopLogs.setExternalUserName(item.getExternalUserName());
-
+            sopLogs.setGrade(item.getGrade());
 
 
             Long msgNum = Long.valueOf(generateRandomNumberWithLock());
             Long msgNum = Long.valueOf(generateRandomNumberWithLock());
             sopLogs.setSmsLogsId(msgNum);
             sopLogs.setSmsLogsId(msgNum);

+ 8 - 0
fs-service/src/main/resources/db/changelog/dictData/dict_data_update.sql

@@ -151,3 +151,11 @@ VALUES
 --rollback DELETE FROM sys_dict_data WHERE dict_type = 'sys_integral_log_type' AND dict_value = '35';
 --rollback DELETE FROM sys_dict_data WHERE dict_type = 'sys_integral_log_type' AND dict_value = '35';
 
 
 
 
+--changeset sgw:20250701-sys-integral-log-integral-withdraw
+--preconditions onFail:MARK_RAN
+--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM sys_dict_data WHERE dict_type = 'sys_integral_log_type' AND dict_value = '36'
+INSERT INTO `sys_dict_data`
+(`dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`)
+VALUES
+    (36, '看课积分提现', '36', 'sys_integral_log_type', NULL, 'default', 'N', '0', 'admin', NOW(), '', NULL, NULL);
+--rollback DELETE FROM sys_dict_data WHERE dict_type = 'sys_integral_log_type' AND dict_value = '36';

+ 17 - 0
fs-service/src/main/resources/db/changelog/sop/changes/change_table.sql

@@ -0,0 +1,17 @@
+--liquibase formatted sql
+
+--changeset sgw:20260716-qw_sop_temp_voice-group_id
+--preconditions onFail:MARK_RAN
+--precondition-sql-check expectedResult:1 SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'qw_sop_temp_voice'
+--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'qw_sop_temp_voice' AND column_name = 'group_id'
+ALTER TABLE qw_sop_temp_voice
+    ADD COLUMN group_id BIGINT  DEFAULT NULL COMMENT '分组id,主要是侧边栏语音的';
+--rollback ALTER TABLE qw_sop_temp_voice DROP COLUMN group_id;
+
+--changeset sgw:20260719-qw_sop_logs-grade
+--preconditions onFail:MARK_RAN
+--precondition-sql-check expectedResult:1 SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'qw_sop_logs'
+--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'qw_sop_logs' AND column_name = 'grade'
+ALTER TABLE qw_sop_logs
+    ADD COLUMN grade BIGINT  DEFAULT NULL COMMENT '客户评级的等级';
+--rollback ALTER TABLE qw_sop_logs DROP COLUMN grade;

+ 2 - 0
fs-service/src/main/resources/db/changelog/sop/db.changelog-master.xml

@@ -9,6 +9,8 @@
     <!-- SOP 数据源 changelog:仅在此目录下的文件会在 sopDataSource 执行 -->
     <!-- SOP 数据源 changelog:仅在此目录下的文件会在 sopDataSource 执行 -->
     <include file="baseline/init-meta.sql" relativeToChangelogFile="true"/>
     <include file="baseline/init-meta.sql" relativeToChangelogFile="true"/>
     <include file="baseline/baseline.sql" relativeToChangelogFile="true"/>
     <include file="baseline/baseline.sql" relativeToChangelogFile="true"/>
+    <include file="table/create_table.sql" relativeToChangelogFile="true"/>
+    <include file="changes/change_table.sql" relativeToChangelogFile="true"/>
 
 
     <!-- 新增 SOP 库变更示例:
     <!-- 新增 SOP 库变更示例:
     <include file="changes/YYYYMMDD-模块-简述.sql" relativeToChangelogFile="true"/>
     <include file="changes/YYYYMMDD-模块-简述.sql" relativeToChangelogFile="true"/>

+ 12 - 0
fs-service/src/main/resources/db/changelog/sop/table/create_table.sql

@@ -0,0 +1,12 @@
+--liquibase formatted sql
+
+--changeset sgw:20260716-qw_sop_temp_voice_group
+--preconditions onFail:MARK_RAN
+--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'qw_sop_temp_voice_group'
+CREATE TABLE `qw_sop_temp_voice_group` (
+                                           `id` int NOT NULL AUTO_INCREMENT,
+                                           `voice_group_name` varchar(255) DEFAULT NULL,
+                                           `create_time` datetime DEFAULT NULL,
+                                           `update_time` datetime DEFAULT NULL,
+                                           PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='语音分类-主要是区分侧边栏语音类型';

+ 8 - 0
fs-service/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml

@@ -74,6 +74,14 @@
         where video_id = #{videoId}
         where video_id = #{videoId}
     </select>
     </select>
 
 
+    <select id="selectFsUserCourseVideoByVideoIds" resultMap="FsUserCourseVideoResult">
+        <include refid="selectFsUserCourseVideoVo"/>
+        where video_id in
+        <foreach collection="videoIds" item="videoId" open="(" separator="," close=")">
+            #{videoId}
+        </foreach>
+    </select>
+
     <insert id="insertFsUserCourseVideo" parameterType="FsUserCourseVideo" useGeneratedKeys="true" keyProperty="videoId">
     <insert id="insertFsUserCourseVideo" parameterType="FsUserCourseVideo" useGeneratedKeys="true" keyProperty="videoId">
         insert into fs_user_course_video
         insert into fs_user_course_video
         <trim prefix="(" suffix=")" suffixOverrides=",">
         <trim prefix="(" suffix=")" suffixOverrides=",">

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

@@ -242,6 +242,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
         </foreach>
     </update>
     </update>
 
 
+    <update id="batchUpdateQwExternalLevelById" parameterType="map">
+        UPDATE qw_external_contact
+        SET level =
+        <choose>
+            <when test="level == 9">
+                NULL
+            </when>
+            <otherwise>
+                #{level}
+            </otherwise>
+        </choose>
+        WHERE id IN
+        <foreach collection="userIds" item="item" open="(" separator="," close=")">
+            #{item}
+        </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

+ 66 - 0
fs-service/src/main/resources/mapper/qw/QwSopTempVoiceGroupMapper.xml

@@ -0,0 +1,66 @@
+<?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.qw.mapper.QwSopTempVoiceGroupMapper">
+
+    <resultMap type="QwSopTempVoiceGroup" id="QwSopTempVoiceGroupResult">
+        <result property="id"    column="id"    />
+        <result property="voiceGroupName"    column="voice_group_name"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectQwSopTempVoiceGroupVo">
+        select id, voice_group_name, create_time, update_time from qw_sop_temp_voice_group
+    </sql>
+
+    <select id="selectQwSopTempVoiceGroupList" parameterType="QwSopTempVoiceGroup" resultMap="QwSopTempVoiceGroupResult">
+        <include refid="selectQwSopTempVoiceGroupVo"/>
+        <where>
+            <if test="voiceGroupName != null  and voiceGroupName != ''"> and voice_group_name like concat('%', #{voiceGroupName}, '%')</if>
+        </where>
+    </select>
+
+    <select id="selectQwSopTempVoiceGroupById" parameterType="Long" resultMap="QwSopTempVoiceGroupResult">
+        <include refid="selectQwSopTempVoiceGroupVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertQwSopTempVoiceGroup" parameterType="QwSopTempVoiceGroup">
+        insert into qw_sop_temp_voice_group
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="voiceGroupName != null">voice_group_name,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="voiceGroupName != null">#{voiceGroupName},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateQwSopTempVoiceGroup" parameterType="QwSopTempVoiceGroup">
+        update qw_sop_temp_voice_group
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="voiceGroupName != null">voice_group_name = #{voiceGroupName},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteQwSopTempVoiceGroupById" parameterType="Long">
+        delete from qw_sop_temp_voice_group where id = #{id}
+    </delete>
+
+    <delete id="deleteQwSopTempVoiceGroupByIds" parameterType="String">
+        delete from qw_sop_temp_voice_group where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

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

@@ -249,7 +249,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectQwWorkTaskList" resultType="com.fs.qw.domain.QwWorkTask">
     <select id="selectQwWorkTaskList" resultType="com.fs.qw.domain.QwWorkTask">
         select
         select
             qwt.id,qwt.ext_id, qwt.qw_user_id, qwt.status, qwt.track_type, qwt.type, qwt.title, qwt.remark, qwt.score, qwt.sop_id,
             qwt.id,qwt.ext_id, qwt.qw_user_id, qwt.status, qwt.track_type, qwt.type, qwt.title, qwt.remark, qwt.score, qwt.sop_id,
-            qwt.company_id, qwt.company_user_id, qwt.duration, qwt.create_time, qwt.update_time
+            qwt.company_id, qwt.company_user_id, qwt.duration, qwt.create_time, qwt.update_time,qwt.video_id
         from qw_work_task qwt
         from qw_work_task qwt
         where qwt.qw_user_id = #{userId}
         where qwt.qw_user_id = #{userId}
           and date(qwt.create_time) = #{date}
           and date(qwt.create_time) = #{date}

+ 8 - 5
fs-service/src/main/resources/mapper/sop/QwSopLogsMapper.xml

@@ -31,10 +31,11 @@
         <result property="userLogsId"    column="user_logs_id"    />
         <result property="userLogsId"    column="user_logs_id"    />
         <result property="appSendStatus"    column="app_send_status"    />
         <result property="appSendStatus"    column="app_send_status"    />
         <result property="appSendRemark"    column="app_send_remark"    />
         <result property="appSendRemark"    column="app_send_remark"    />
+        <result property="grade"    column="grade"    />
     </resultMap>
     </resultMap>
 
 
     <sql id="selectQwSopLogsVo">
     <sql id="selectQwSopLogsVo">
-        select id,sop_id,customer_id, qw_userid,corp_id,external_user_id,external_id,external_user_name, log_type, content_json, send_status,receiving_status,msg_id, send_time, real_send_time, company_id, send_type,remark,fs_user_id,take_records,qw_user_key,user_logs_id,app_send_status,app_send_remark from qw_sop_logs
+        select id,sop_id,customer_id, qw_userid,corp_id,external_user_id,external_id,external_user_name, log_type, content_json, send_status,receiving_status,msg_id, send_time, real_send_time, company_id, send_type,remark,fs_user_id,take_records,qw_user_key,user_logs_id,app_send_status,app_send_remark,grade from qw_sop_logs
     </sql>
     </sql>
 
 
     <select id="selectQwSopLogsListVO" parameterType="QwSopLogs" resultMap="QwSopLogsResult">
     <select id="selectQwSopLogsListVO" parameterType="QwSopLogs" resultMap="QwSopLogsResult">
@@ -600,7 +601,7 @@
         qw_userid, external_user_id,external_id, external_user_name, log_type,
         qw_userid, external_user_id,external_id, external_user_name, log_type,
         content_json, send_status, send_time, real_send_time, send_type,
         content_json, send_status, send_time, real_send_time, send_type,
         company_id, receiving_status, msg_id, sop_id, remark,
         company_id, receiving_status, msg_id, sop_id, remark,
-        corp_id, customer_id,fs_user_id,sort,qw_user_key,app_send_status,sms_logs_id
+        corp_id, customer_id,fs_user_id,sort,qw_user_key,app_send_status,sms_logs_id,grade
         )
         )
         VALUES
         VALUES
         <foreach collection="qwSopLogs" item="log" separator=",">
         <foreach collection="qwSopLogs" item="log" separator=",">
@@ -626,7 +627,8 @@
             #{log.sort},
             #{log.sort},
             #{log.qwUserKey},
             #{log.qwUserKey},
             #{log.appSendStatus},
             #{log.appSendStatus},
-            #{log.smsLogsId}
+            #{log.smsLogsId},
+            #{log.grade}
             )
             )
         </foreach>
         </foreach>
     </insert>
     </insert>
@@ -638,7 +640,7 @@
         content_json, send_status, send_time, real_send_time, send_type,
         content_json, send_status, send_time, real_send_time, send_type,
         company_id, receiving_status, msg_id, sop_id, remark,
         company_id, receiving_status, msg_id, sop_id, remark,
         corp_id, customer_id, fs_user_id, expiration_time,sort,user_logs_id,take_records,
         corp_id, customer_id, fs_user_id, expiration_time,sort,user_logs_id,take_records,
-         qw_user_key,app_send_status,app_send_remark,sms_logs_id
+         qw_user_key,app_send_status,app_send_remark,sms_logs_id,grade
         )
         )
         VALUES
         VALUES
         <foreach collection="qwSopLogs" item="log" separator=",">
         <foreach collection="qwSopLogs" item="log" separator=",">
@@ -668,7 +670,8 @@
             #{log.qwUserKey},
             #{log.qwUserKey},
             #{log.appSendStatus},
             #{log.appSendStatus},
             #{log.appSendRemark},
             #{log.appSendRemark},
-            #{log.smsLogsId}
+            #{log.smsLogsId},
+            #{log.grade}
             )
             )
         </foreach>
         </foreach>
     </insert>
     </insert>

+ 15 - 10
fs-service/src/main/resources/mapper/sop/QwSopTempVoiceMapper.xml

@@ -16,10 +16,11 @@
         <result property="userVoiceUrl"    column="user_voice_url"    />
         <result property="userVoiceUrl"    column="user_voice_url"    />
         <result property="recordType"    column="record_type"    />
         <result property="recordType"    column="record_type"    />
         <result property="createTime"    column="create_time"    />
         <result property="createTime"    column="create_time"    />
+        <result property="groupId"    column="group_id"    />
     </resultMap>
     </resultMap>
 
 
     <sql id="selectQwSopTempVoiceVo">
     <sql id="selectQwSopTempVoiceVo">
-        select id,company_user_id, temp_id, rule_id, content_id, voice_txt, voice_url, create_time from qw_sop_temp_voice
+        select id,company_user_id, temp_id, rule_id, content_id, voice_txt, voice_url, create_time,group_id from qw_sop_temp_voice
     </sql>
     </sql>
 
 
     <select id="selectQwSopTempVoiceList" parameterType="QwSopTempVoice" resultMap="QwSopTempVoiceResult">
     <select id="selectQwSopTempVoiceList" parameterType="QwSopTempVoice" resultMap="QwSopTempVoiceResult">
@@ -34,7 +35,7 @@
     </select>
     </select>
 
 
     <select id="selectQwSopTempVoiceById" parameterType="Long" resultMap="QwSopTempVoiceResult">
     <select id="selectQwSopTempVoiceById" parameterType="Long" resultMap="QwSopTempVoiceResult">
-        select id,company_user_id,duration, temp_id, rule_id, content_id, voice_txt, voice_url,user_voice_url,record_type, create_time from qw_sop_temp_voice
+        select id,company_user_id,duration, temp_id, rule_id, content_id, voice_txt, voice_url,user_voice_url,record_type, create_time,group_id from qw_sop_temp_voice
         where id = #{id}
         where id = #{id}
     </select>
     </select>
     <select id="listVoice" resultType="com.fs.sop.domain.QwSopTempVoice">
     <select id="listVoice" resultType="com.fs.sop.domain.QwSopTempVoice">
@@ -62,15 +63,15 @@
     </select>
     </select>
 
 
     <select id="selectQwSopTempVoiceByIdAndText" resultType="com.fs.sop.domain.QwSopTempVoice">
     <select id="selectQwSopTempVoiceByIdAndText" resultType="com.fs.sop.domain.QwSopTempVoice">
-        select id,company_user_id companyUserId,voice_txt voiceTxt from qw_sop_temp_voice where id = #{id}
+        select id,company_user_id companyUserId,voice_txt voiceTxt,group_id from qw_sop_temp_voice where id = #{id}
                                                                                             and voice_txt = #{voiceTxt} and record_type in (0,1) limit 1
                                                                                             and voice_txt = #{voiceTxt} and record_type in (0,1) limit 1
     </select>
     </select>
     <select id="selectQwSopTempVoiceByIdAndUserVoiceUrl" resultType="com.fs.sop.domain.QwSopTempVoice" >
     <select id="selectQwSopTempVoiceByIdAndUserVoiceUrl" resultType="com.fs.sop.domain.QwSopTempVoice" >
-        select id,company_user_id companyUserId,voice_txt voiceTxt from qw_sop_temp_voice where id = #{id} and record_type in (0,1,2) limit 1
+        select id,company_user_id companyUserId,voice_txt voiceTxt,group_id from qw_sop_temp_voice where id = #{id} and record_type in (0,1,2) limit 1
     </select>
     </select>
     <select id="selectQwSopTempVoiceNewList" resultType="com.fs.sop.domain.QwSopTempVoice">
     <select id="selectQwSopTempVoiceNewList" resultType="com.fs.sop.domain.QwSopTempVoice">
         select id,voice_txt voiceTxt,company_user_id companyUserId,voice_url voiceUrl,duration,record_type recordType,
         select id,voice_txt voiceTxt,company_user_id companyUserId,voice_url voiceUrl,duration,record_type recordType,
-        user_voice_url userVoiceUrl from qw_sop_temp_voice
+        user_voice_url userVoiceUrl,group_id from qw_sop_temp_voice
         <where>
         <where>
             <if test="companyUserId != null"> and company_user_id = #{companyUserId}</if>
             <if test="companyUserId != null"> and company_user_id = #{companyUserId}</if>
             <if test="recordType != null"> and record_type = #{recordType}</if>
             <if test="recordType != null"> and record_type = #{recordType}</if>
@@ -85,18 +86,18 @@
         </foreach>
         </foreach>
     </select>
     </select>
     <select id="selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt" resultType="com.fs.sop.domain.QwSopTempVoice">
     <select id="selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt" resultType="com.fs.sop.domain.QwSopTempVoice">
-        select qw_user_id qwUserId,voice_url voiceUrl,voice_txt voiceTxt,user_voice_url userVoiceUrl,company_user_id companyUserId,
+        select qw_user_id qwUserId,voice_url voiceUrl,voice_txt voiceTxt,user_voice_url userVoiceUrl,company_user_id companyUserId,group_id,
                record_type recordType,duration from qw_sop_temp_voice where company_user_id = #{companyUserId} and voice_txt = #{voiceTxt}
                record_type recordType,duration from qw_sop_temp_voice where company_user_id = #{companyUserId} and voice_txt = #{voiceTxt}
     </select>
     </select>
     <select id="selectQwSopTempVoiceByCompanyUserIdAndQwUserId" resultType="com.fs.sop.domain.QwSopTempVoice">
     <select id="selectQwSopTempVoiceByCompanyUserIdAndQwUserId" resultType="com.fs.sop.domain.QwSopTempVoice">
-        select id,company_user_id companyUserId,voice_url voiceUrl,voice_txt voiceTxt,duration
+        select id,company_user_id companyUserId,voice_url voiceUrl,voice_txt voiceTxt,duration,group_id
         from qw_sop_temp_voice
         from qw_sop_temp_voice
         where company_user_id = #{companyUserId} and qw_user_id = #{qwUserId}
         where company_user_id = #{companyUserId} and qw_user_id = #{qwUserId}
           and voice_url  is not null
           and voice_url  is not null
             limit 1
             limit 1
     </select>
     </select>
     <select id="selectQwSopTempVoiceListLimit" resultType="com.fs.sop.domain.QwSopTempVoice">
     <select id="selectQwSopTempVoiceListLimit" resultType="com.fs.sop.domain.QwSopTempVoice">
-        select id,voice_txt voiceTxt,company_user_id companyUserId,voice_url voiceUrl,duration,record_type recordType,
+        select id,voice_txt voiceTxt,company_user_id companyUserId,voice_url voiceUrl,duration,record_type recordType,group_id,
                user_voice_url userVoiceUrl from qw_sop_temp_voice
                user_voice_url userVoiceUrl from qw_sop_temp_voice
         where record_type = #{recordType} and company_user_id is not null
         where record_type = #{recordType} and company_user_id is not null
         order by date_format(create_time,'%y%m%d') desc,company_user_id desc limit 10
         order by date_format(create_time,'%y%m%d') desc,company_user_id desc limit 10
@@ -214,6 +215,7 @@
         where company_user_id = #{companyUserId}
         where company_user_id = #{companyUserId}
         and record_type =1
         and record_type =1
         <if test="keyword != null and keyword!='' ">  and voice_txt like concat('%', #{keyword}, '%') </if>
         <if test="keyword != null and keyword!='' ">  and voice_txt like concat('%', #{keyword}, '%') </if>
+        <if test="groupId != null and groupId!='' ">  and group_id =#{groupId} </if>
         order by create_time desc
         order by create_time desc
     </select>
     </select>
 
 
@@ -226,6 +228,7 @@
             and temp_id is null
             and temp_id is null
             <if test="data.companyId != null "> and company_id = #{data.companyId}</if>
             <if test="data.companyId != null "> and company_id = #{data.companyId}</if>
             <if test="data.recordType != null"> and record_type= #{data.recordType}</if>
             <if test="data.recordType != null"> and record_type= #{data.recordType}</if>
+            <if test="data.groupId != null"> and group_id= #{data.groupId}</if>
         </where>
         </where>
         GROUP BY company_id, voice_txt
         GROUP BY company_id, voice_txt
     </select>
     </select>
@@ -243,14 +246,16 @@
         company_user_id,
         company_user_id,
         company_id,
         company_id,
         voice_txt,
         voice_txt,
-        record_type
+        record_type,
+        group_id
         ) values
         ) values
         <foreach collection="list" item="item" separator=",">
         <foreach collection="list" item="item" separator=",">
             (
             (
             #{item.companyUserId},
             #{item.companyUserId},
             #{item.companyId},
             #{item.companyId},
             #{item.voiceTxt},
             #{item.voiceTxt},
-            #{item.recordType}
+            #{item.recordType},
+            #{item.groupId}
             )
             )
         </foreach>
         </foreach>
         ON DUPLICATE KEY UPDATE
         ON DUPLICATE KEY UPDATE

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

@@ -264,6 +264,7 @@
             id, sop_id, user_logs_id, external_contact_id,qw_user_id,corp_id,external_id,
             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
             fs_user_id, external_user_name,create_time,crt_Time,update_time
         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}
+        and (grade != 6 or grade is null)
     </select>
     </select>
 
 
     <select id="selectRestoreByIsDaysNotStudy" parameterType="String" resultMap="SopUserLogsInfoResult">
     <select id="selectRestoreByIsDaysNotStudy" parameterType="String" resultMap="SopUserLogsInfoResult">

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

@@ -253,7 +253,7 @@
             ul.`status` = '1'
             ul.`status` = '1'
           and qs.is_rating=1
           and qs.is_rating=1
           and qs.`status` in (2,3)
           and qs.`status` in (2,3)
-          AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) % 7 = 0
+          AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) % 4 = 0
           AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) &gt; 0
           AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) &gt; 0
     </select>
     </select>
 
 
@@ -274,9 +274,9 @@
             ul.`status` = '1'
             ul.`status` = '1'
           and qs.is_rating=1
           and qs.is_rating=1
           and qs.type=2
           and qs.type=2
-          and qs.send_type=2
+          and qs.send_type in (2,11)
           and qs.`status` in (2,3)
           and qs.`status` in (2,3)
-          AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) >= 7
+          AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) >= 4
         ORDER BY id ASC
         ORDER BY id ASC
             LIMIT #{offset}, #{pageSize}
             LIMIT #{offset}, #{pageSize}
     </select>
     </select>
@@ -298,7 +298,7 @@
         WHERE
         WHERE
             ul.`status` = '1'
             ul.`status` = '1'
           and qs.type=2
           and qs.type=2
-          and qs.send_type=2
+          and qs.send_type in (2,11)
           and qs.`status` in (2,3)
           and qs.`status` in (2,3)
           AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) >= #{notStudyDays}
           AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) >= #{notStudyDays}
         ORDER BY id ASC
         ORDER BY id ASC
@@ -314,7 +314,7 @@
         WHERE
         WHERE
             ul.`status` = '1'
             ul.`status` = '1'
           and qs.type=2
           and qs.type=2
-          and qs.send_type=2
+          and qs.send_type in (2,11)
           and qs.`status` in (2,3)
           and qs.`status` in (2,3)
           AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) &lt; #{notStudyDays}
           AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) &lt; #{notStudyDays}
         ORDER BY id ASC
         ORDER BY id ASC
@@ -342,7 +342,7 @@
           and qs.send_type=2
           and qs.send_type=2
           and qs.`status` in (2,3)
           and qs.`status` in (2,3)
           and ul.sop_id= #{sopId}
           and ul.sop_id= #{sopId}
-          AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) >= 7
+          AND ( DATEDIFF( CURRENT_DATE, ul.start_time ) ) >= 4
 
 
     </select>
     </select>
 
 

+ 43 - 2
fs-user-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -8,6 +8,7 @@ import com.baidubce.appbuilder.model.dataset.DocumentListResponse;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.ai.service.IBaiduAIService;
 import com.fs.ai.service.IBaiduAIService;
 import com.fs.ai.vo.BaiduAIMsgResultVO;
 import com.fs.ai.vo.BaiduAIMsgResultVO;
+import com.fs.app.annotation.Login;
 import com.fs.app.param.SignParam;
 import com.fs.app.param.SignParam;
 import com.fs.app.param.TestMoneyParam;
 import com.fs.app.param.TestMoneyParam;
 import com.fs.app.utils.CityTreeUtil;
 import com.fs.app.utils.CityTreeUtil;
@@ -98,7 +99,7 @@ import static com.fs.common.utils.SecurityUtils.getUserId;
 @Api("公共接口")
 @Api("公共接口")
 @RestController
 @RestController
 @RequestMapping(value="/app/common")
 @RequestMapping(value="/app/common")
-public class CommonController {
+public class CommonController extends AppBaseController {
 	@Autowired
 	@Autowired
 	private RedisCache redisCache;
 	private RedisCache redisCache;
 	@Autowired
 	@Autowired
@@ -714,7 +715,7 @@ public class CommonController {
 	@GetMapping("/getAppContactWay")
 	@GetMapping("/getAppContactWay")
 	public R getAppContactWay()
 	public R getAppContactWay()
 	{
 	{
-		Long userId = getUserId();
+		Long userId = Long.parseLong(getUserId());
 
 
 		String appContactWayImgCode = qwAppContactWayService.getAppContactWayImgCode(userId);
 		String appContactWayImgCode = qwAppContactWayService.getAppContactWayImgCode(userId);
 		return R.ok().put("data",appContactWayImgCode);
 		return R.ok().put("data",appContactWayImgCode);
@@ -802,4 +803,44 @@ public class CommonController {
 				.put("storeProductTuiCate",storeProductTuiCate);
 				.put("storeProductTuiCate",storeProductTuiCate);
 	}
 	}
 
 
+	/**
+	 * 获取用户钱包明细
+	 */
+	@Login
+	@GetMapping("/getWallet/{userId}")
+	public R getWallet(@PathVariable("userId") Long userId){
+		R denied = denyIfNotCurrentUser(userId);
+		if (denied != null) {
+			return denied;
+		}
+		return userService.getWallet(userId);
+	}
+
+	/**
+	 * 用户钱包 积分兑换佣金
+	 */
+	@Login
+	@GetMapping("/integralExchange/{userId}")
+	public R integralExchange(@PathVariable("userId") Long userId){
+		R denied = denyIfNotCurrentUser(userId);
+		if (denied != null) {
+			return denied;
+		}
+		return userService.integralExchange(userId);
+	}
+
+	/**
+	 * 用户钱包 获取兑换明细
+	 */
+	@Login
+	@PostMapping("/exchangDetail")
+	public R exchangDetail(@RequestBody Map<String,Object> params){
+		Long userId = Long.valueOf(params.get("userId").toString());
+		R denied = denyIfNotCurrentUser(userId);
+		if (denied != null) {
+			return denied;
+		}
+		return userService.exchangDetail(params);
+	}
+
 }
 }

+ 17 - 0
fs-user-app/src/main/java/com/fs/app/controller/UserController.java

@@ -141,6 +141,23 @@ public class UserController extends  AppBaseController {
         if(StringUtils.isBlank(param.getKeywords())) {
         if(StringUtils.isBlank(param.getKeywords())) {
             return R.ok().put("data",new ArrayList<>());
             return R.ok().put("data",new ArrayList<>());
         }
         }
+
+        // 有销售身份(companyUserToken)可搜全部;否则按客户限制,仅允许 C/D 前缀
+        if (getCompanyUserIdOrNull() == null) {
+            String keywords = param.getKeywords().trim();
+            if (keywords.length() < 2) {
+                return R.ok().put("data", new ArrayList<>());
+            }
+            char prefix = Character.toUpperCase(keywords.charAt(0));
+            if (prefix != 'C' && prefix != 'D') {
+                // 非 C/D 开头(如 U 或其他)不给展示
+                return R.ok().put("data", new ArrayList<>());
+            }
+            param.setOnlySales(true);
+            param.setSearchPrefix(String.valueOf(prefix));
+            param.setKeywords(keywords.substring(1));
+        }
+
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
 
 
         if(StringUtils.isNotBlank(param.getKeywords())){
         if(StringUtils.isNotBlank(param.getKeywords())){

+ 22 - 2
fs-user-app/src/main/java/com/fs/app/controller/app/ImController.java

@@ -1,6 +1,8 @@
 package com.fs.app.controller.app;
 package com.fs.app.controller.app;
 
 
+import com.fs.app.controller.AppBaseController;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
+import com.fs.common.utils.StringUtils;
 import com.fs.im.dto.OpenImResponseDTO;
 import com.fs.im.dto.OpenImResponseDTO;
 import com.fs.im.service.OpenIMService;
 import com.fs.im.service.OpenIMService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
@@ -20,7 +22,7 @@ import org.springframework.web.bind.annotation.RestController;
 @RestController
 @RestController
 @RequestMapping(value = "/app/im")
 @RequestMapping(value = "/app/im")
 @Slf4j
 @Slf4j
-public class ImController {
+public class ImController extends AppBaseController {
 
 
     @Autowired
     @Autowired
     private OpenIMService openIMService;
     private OpenIMService openIMService;
@@ -33,8 +35,26 @@ public class ImController {
      */
      */
     @GetMapping("/getUserInfo")
     @GetMapping("/getUserInfo")
     public R getUserInfo(String id) {
     public R getUserInfo(String id) {
+        if (StringUtils.isBlank(id)) {
+            return R.ok().put("data", null);
+        }
+
+        // 有销售身份可查全部;否则按客户限制,仅允许 C/D(纯数字视为 U)
+        if (getCompanyUserIdOrNull() == null) {
+            String checkId = id.trim();
+            char prefix;
+            if (checkId.matches("\\d+")) {
+                prefix = 'U';
+            } else {
+                prefix = Character.toUpperCase(checkId.charAt(0));
+            }
+            if (prefix != 'C' && prefix != 'D') {
+                return R.ok().put("data", null);
+            }
+        }
+
         OpenImResponseDTO responseDTO = openIMService.getUserInfo(id);
         OpenImResponseDTO responseDTO = openIMService.getUserInfo(id);
-        return R.ok().put("data",responseDTO);
+        return R.ok().put("data", responseDTO);
     }
     }
 
 
 }
 }