Bladeren bron

Merge remote-tracking branch 'origin/master' into matser

吴树波 1 week geleden
bovenliggende
commit
d0b2cb9eab

+ 1 - 1
fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java

@@ -100,7 +100,7 @@ public class FsCourseWatchLogController extends BaseController
     {
         logger.info("会员课程数据汇总 参数: {}",param);
 
-        if(param.getCompanyId() == null){
+        if(param.getCompanyId() == null && (param.getUserIds() == null || param.getUserIds().isEmpty())){
             throw new CustomException("必须选择公司!");
         }
         if (param.getSTime()==null||param.getETime()==null){

+ 97 - 1
fs-admin/src/main/java/com/fs/his/controller/FsIntegralOrderController.java

@@ -148,6 +148,7 @@ public class FsIntegralOrderController extends BaseController
     @PreAuthorize("@ss.hasPermi('his:integralOrder:export')")
     @Log(title = "积分商品订单", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
+
     public AjaxResult export(FsIntegralOrderParam fsIntegralOrder) {
         List<FsIntegralOrderListVO> fsIntegralOrderListVOS = new ArrayList<>();
         if (CloudHostUtils.hasCloudHostName("金牛明医")){
@@ -162,6 +163,101 @@ public class FsIntegralOrderController extends BaseController
         }
         SysRole sysRole = isCheckPermission();
         // 处理商品名称:解析item_json并设置goodsName
+        for (FsIntegralOrderListVO vo : fsIntegralOrderListVOS) {
+            if (StringUtils.isNotEmpty(vo.getItemJson())) {
+                try {
+                    // 尝试解析JSON格式的商品信息
+                    if (vo.getItemJson().startsWith("[")) {
+                        // 数组格式,遍历所有商品,用换行符分隔
+                        com.alibaba.fastjson.JSONArray jsonArray = com.alibaba.fastjson.JSONArray.parseArray(vo.getItemJson());
+                        if (jsonArray != null && !jsonArray.isEmpty()) {
+                            StringBuilder goodsNameBuilder = new StringBuilder();
+                            StringBuilder barCodeBuilder = new StringBuilder();
+
+                            for (int i = 0; i < jsonArray.size(); i++) {
+                                com.alibaba.fastjson.JSONObject goods = jsonArray.getJSONObject(i);
+
+                                // 处理商品名称和数量
+                                if (goods != null && goods.getString("goodsName") != null) {
+                                    String name = goods.getString("goodsName");
+                                    String num = goods.getString("num");
+                                    if (StringUtils.isEmpty(num)) {
+                                        num = "1"; // 默认数量为1
+                                    }
+                                    if (goodsNameBuilder.length() > 0) {
+                                        goodsNameBuilder.append("\r\n");
+                                    }
+                                    goodsNameBuilder.append(name).append(",数量:").append(num).append("个");
+                                }
+
+                                // 处理商品编码
+                                if (goods != null && goods.getString("barCode") != null) {
+                                    String barCode = goods.getString("barCode");
+                                    if (barCodeBuilder.length() > 0) {
+                                        barCodeBuilder.append("\r\n");
+                                    }
+                                    barCodeBuilder.append(barCode);
+                                }
+                            }
+
+                            // 设置商品名称
+                            if (goodsNameBuilder.length() > 0) {
+                                vo.setGoodsName(goodsNameBuilder.toString());
+                            }
+
+                            // 设置商品编码
+                            if (barCodeBuilder.length() > 0) {
+                                vo.setBarCode(barCodeBuilder.toString());
+                            }
+                        }
+                    } else if (vo.getItemJson().startsWith("{")) {
+                        // 对象格式
+                        com.alibaba.fastjson.JSONObject goods = com.alibaba.fastjson.JSONObject.parseObject(vo.getItemJson());
+
+                        // 处理商品名称和数量
+                        if (goods != null && goods.getString("goodsName") != null) {
+                            String name = goods.getString("goodsName");
+                            String num = goods.getString("num");
+                            if (StringUtils.isEmpty(num)) {
+                                num = "1"; // 默认数量为1
+                            }
+                            vo.setGoodsName(name + ",数量:" + num + "个");
+                        }
+
+                        // 处理商品编码
+                        if (goods != null && goods.getString("barCode") != null) {
+                            vo.setBarCode(goods.getString("barCode"));
+                        }
+                    }
+                } catch (Exception e) {
+                    // 解析失败时保持goodsName为空,避免导出出错
+                    log.warn("解析商品信息失败,订单编号:{}, 商品信息:{}", vo.getOrderCode(), vo.getItemJson());
+                }
+            }
+            if (!(sysRole.getIsCheckPhone()==1)){
+                // 加密手机号
+                vo.setUserPhone(decryptAutoPhoneMk(vo.getUserPhone()));
+            }
+
+        }
+        ExcelUtil<FsIntegralOrderListVO> util = new ExcelUtil<>(FsIntegralOrderListVO.class);
+        return util.exportExcel(new ArrayList<>(fsIntegralOrderListVOS), "积分商品订单数据");
+    }
+
+    /*public AjaxResult export(FsIntegralOrderParam fsIntegralOrder) {
+        List<FsIntegralOrderListVO> fsIntegralOrderListVOS = new ArrayList<>();
+        if (CloudHostUtils.hasCloudHostName("金牛明医")){
+            *//*目前只有金牛有状态为6的查询,其他项目避免使用6状态码*//*
+            if (fsIntegralOrder.getStatus() != null && fsIntegralOrder.getStatus().equals("6")) {
+                fsIntegralOrder.setStatus("1");
+                fsIntegralOrder.setIsPush(0);
+            }
+            fsIntegralOrderListVOS = fsIntegralOrderService.selectFsIntegralOrderListByJn(fsIntegralOrder);
+        } else {
+            fsIntegralOrderListVOS = fsIntegralOrderService.selectFsIntegralOrderListVO(fsIntegralOrder);
+        }
+        SysRole sysRole = isCheckPermission();
+        // 处理商品名称:解析item_json并设置goodsName
         for (FsIntegralOrderListVO vo : fsIntegralOrderListVOS) {
             if (StringUtils.isNotEmpty(vo.getItemJson())) {
                 try {
@@ -201,7 +297,7 @@ public class FsIntegralOrderController extends BaseController
         }
         ExcelUtil<FsIntegralOrderListVO> util = new ExcelUtil<>(FsIntegralOrderListVO.class);
         return util.exportExcel(new ArrayList<>(fsIntegralOrderListVOS), "积分商品订单数据");
-    }
+    }*/
     /**
      * 发货
      */

+ 241 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwWorkTaskNewController.java

@@ -0,0 +1,241 @@
+package com.fs.qw.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.service.impl.CompanyDeptServiceImpl;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+//import com.fs.framework.security.LoginUser;
+//import com.fs.framework.service.TokenService;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.qw.vo.QwWorkTaskAllListVO;
+import com.fs.qw.vo.QwWorkTaskListVO;
+import com.fs.qwApi.domain.QwExternalContactRemarkResult;
+import com.fs.qwApi.param.QwExternalContactRemarkParam;
+import com.fs.qwApi.service.QwApiService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 企微任务看板Controller
+ *
+ * @author fs
+ * @date 2025-03-25
+ */
+@RestController
+@RequestMapping("/qw/QwWorkTaskNew")
+public class QwWorkTaskNewController extends BaseController
+{
+    @Autowired
+    private IQwWorkTaskService qwWorkTaskService;
+//    @Autowired
+//    private TokenService tokenService;
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+
+    @Autowired
+    private CompanyDeptServiceImpl companyDeptService;
+
+    /**
+     * 查询企微任务看板列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwWorkTaskListParam qwWorkTask)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwWorkTask.setCompanyId(loginUser.getCompany().getCompanyId());
+//        qwWorkTask.setCompanyUserId(loginUser.getUser().getUserId());
+        List<QwWorkTaskListVO> list = qwWorkTaskService.selectQwWorkTaskListVO(qwWorkTask);
+
+        for (QwWorkTaskListVO qwWorkTaskListVO : list) {
+            qwWorkTaskListVO.setLogs(fsCourseWatchLogMapper.selectFsCourseWatchLog7DayByExtId(qwWorkTaskListVO.getExtId()));
+        }
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询企微任务部门催课看板列表
+     */
+//    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:deptList')")
+//    @GetMapping("/deptList")
+//    public TableDataInfo deptList(QwWorkTaskListParam qwWorkTask)
+//    {
+//
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwWorkTask.setCompanyId(loginUser.getCompany().getCompanyId());
+//
+//        List<Long> combinedList = new ArrayList<>();
+//        //本部门
+//        Long deptId = loginUser.getUser().getDeptId();
+//        if (deptId!=null){
+//            combinedList.add(deptId);
+//        }
+//        //本部门的下级部门
+//        List<Long> deptList = companyDeptService.selectCompanyDeptByParentId(deptId);
+//        if (!deptList.isEmpty()){
+//            combinedList.addAll(deptList);
+//        }
+//
+//        qwWorkTask.setCuDeptIdList(combinedList);
+//        qwWorkTask.setUserType(loginUser.getUser().getUserType());
+//
+//
+//        startPage();
+//        List<QwWorkTaskListVO> list = qwWorkTaskService.selectQwWorkTaskListVO(qwWorkTask);
+//        for (QwWorkTaskListVO qwWorkTaskListVO : list) {
+//            qwWorkTaskListVO.setLogs(fsCourseWatchLogMapper.selectFsCourseWatchLog7DayByExtId(qwWorkTaskListVO.getExtId()));
+//        }
+//        return getDataTable(list);
+//    }
+
+    @GetMapping("/glList")
+    public TableDataInfo glList(QwWorkTaskListParam qwWorkTask)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwWorkTask.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<QwWorkTaskListVO> list = qwWorkTaskService.selectQwWorkTaskListVO(qwWorkTask);
+        for (QwWorkTaskListVO qwWorkTaskListVO : list) {
+            qwWorkTaskListVO.setLogs(fsCourseWatchLogMapper.selectFsCourseWatchLog7DayByExtId(qwWorkTaskListVO.getExtId()));
+        }
+        return getDataTable(list);
+    }
+
+    @GetMapping("/allList")
+    public TableDataInfo allList(QwWorkTaskListParam qwWorkTask)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwWorkTask.setCompanyId(loginUser.getCompany().getCompanyId());
+        if (qwWorkTask.getSTime()==null||qwWorkTask.getETime()==null){
+            return new TableDataInfo();
+        }
+        List<QwWorkTaskAllListVO> list = qwWorkTaskService.selectQwWorkTaskAllListVO(qwWorkTask);
+
+        return getDataTable(list);
+    }
+    /**
+     * 导出企微任务看板列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:export')")
+    @Log(title = "企微任务看板", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwWorkTask qwWorkTask)
+    {
+        List<QwWorkTask> list = qwWorkTaskService.selectQwWorkTaskList(qwWorkTask);
+        ExcelUtil<QwWorkTask> util = new ExcelUtil<QwWorkTask>(QwWorkTask.class);
+        return util.exportExcel(list, "企微任务看板数据");
+    }
+
+    /**
+     * 获取企微任务看板详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwWorkTaskService.selectQwWorkTaskById(id));
+    }
+
+    /**
+     * 新增企微任务看板
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:add')")
+    @Log(title = "企微任务看板", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody QwWorkTask qwWorkTask)
+    {
+        return toAjax(qwWorkTaskService.insertQwWorkTask(qwWorkTask));
+    }
+    @Autowired
+    QwApiService qwApiService;
+    @Autowired
+    IQwExternalContactService qwExternalContactService;
+    @Autowired
+    QwExternalContactMapper qwExternalContactMapper;
+    /**
+     * 修改企微任务看板
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:edit')")
+    @Log(title = "企微任务看板处理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody QwWorkTask qwWorkTask)
+    {
+        QwWorkTask task = new QwWorkTask();
+        task.setId(qwWorkTask.getId());
+        task.setStatus(1);
+        task.setTrackType(qwWorkTask.getTrackType());
+        task.setDescription(qwWorkTask.getDescription());
+        task.setUpdateTime(new Date());
+        if (task.getDescription()!=null&& !task.getDescription().isEmpty()){
+
+            QwExternalContact qwExternalContact = qwExternalContactService.selectQwExternalContactById(qwWorkTask.getExtId());
+            if (qwExternalContact!=null){
+                QwExternalContactRemarkParam param = new QwExternalContactRemarkParam();
+                param.setUserid(qwExternalContact.getUserId());
+                param.setExternal_userid(qwExternalContact.getExternalUserId());
+                param.setDescription(task.getDescription());
+
+                QwExternalContactRemarkResult qwExternalContactRemarkResult = qwApiService.externalcontactRemark(param, qwExternalContact.getCorpId());
+                logger.info("QwExternalContactRemarkResult206:" + qwExternalContactRemarkResult);
+                if (qwExternalContactRemarkResult.getErrcode() == 0) {
+                    QwExternalContact ext = new QwExternalContact();
+                    ext.setId(qwExternalContact.getId());
+                    ext.setDescription(task.getDescription());
+                    qwExternalContactMapper.updateQwExternalContact(ext);
+                }
+            }
+        }
+        return toAjax(qwWorkTaskService.updateQwWorkTask(task));
+    }
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:edit')")
+    @Log(title = "企微任务看板处理", businessType = BusinessType.UPDATE)
+    @PutMapping("/edit2")
+    public AjaxResult edit2(@RequestBody QwWorkTask qwWorkTask)
+    {
+        QwWorkTask task = new QwWorkTask();
+        task.setId(qwWorkTask.getId());
+        task.setStatus(1);
+        task.setUpdateTime(new Date());
+        task.setTrackType(2);
+        return toAjax(qwWorkTaskService.updateQwWorkTask(task));
+    }
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:edit')")
+    @Log(title = "企微任务看板处理", businessType = BusinessType.UPDATE)
+    @PutMapping("/edit3")
+    public AjaxResult edit3(@RequestBody QwWorkTask qwWorkTask)
+    {
+        QwWorkTask task = new QwWorkTask();
+        task.setId(qwWorkTask.getId());
+        task.setStatus(1);
+        task.setUpdateTime(new Date());
+        task.setTrackType(3);
+        return toAjax(qwWorkTaskService.updateQwWorkTask(task));
+    }
+    /**
+     * 删除企微任务看板
+     */
+    @PreAuthorize("@ss.hasPermi('qw:QwWorkTaskNew:remove')")
+    @Log(title = "企微任务看板", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwWorkTaskService.deleteQwWorkTaskByIds(ids));
+    }
+}

+ 3 - 2
fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java

@@ -4,6 +4,7 @@ import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.uuid.IdUtils;
 import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.service.AiHookService;
@@ -322,9 +323,9 @@ public class QwMsgController {
                         ste.setUuid(wxWorkMsgResp.getUuid());
                         WxWorkResponseDTO<WxwSpeechToTextEntityRespDTO> dto = wxWorkService.SpeechToTextEntity(ste, serverId);
                         System.out.println(dto);
-                        if(dto.getErrcode() != 0){
+                        if(dto.getErrcode() != 0 || Objects.isNull(dto.getData()) || StringUtils.isBlank(dto.getData().getText())){
                             try {
-                                TimeUnit.SECONDS.sleep(1); // 阻塞1
+                                TimeUnit.SECONDS.sleep(2); // 阻塞2
                             } catch (InterruptedException e) {
                                 Thread.currentThread().interrupt(); // 处理中断异常
                                 log.info("id:{}, 第一次语音转换失败", id);

+ 50 - 8
fs-qw-task/src/main/java/com/fs/app/task/CourseWatchLogScheduler.java

@@ -9,10 +9,12 @@ import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.system.service.ISysConfigService;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
+import java.util.Calendar;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 @Component
@@ -55,29 +57,69 @@ public class CourseWatchLogScheduler {
 
 
 
-    /**
-     * 检查看课状态
-     */
+//    /**
+//     * 检查看课状态
+//     */
+//    @Scheduled(fixedRate = 60000) // 每分钟执行一次
+//    public void checkWatchStatus() {
+//        // 尝试设置标志为 true,表示任务开始执行
+//        if (!isRunning1.compareAndSet(false, true)) {
+//            log.warn("检查看课中任务执行 - 上一个任务尚未完成,跳过此次执行");
+//            return;
+//        }
+//        try {
+//            log.info("检查看课中任务执行>>>>>>>>>>>>");
+//            courseWatchLogService.scheduleBatchUpdateToDatabase();
+////            courseWatchLogService.scheduleBatchUpdateToDatabaseIsOpen();
+//            courseWatchLogService.checkWatchStatus();
+//            log.info("检查看课中任务执行完成>>>>>>>>>>>>");
+//        }catch (Exception e) {
+//            log.error("检查看课中任务执行完成 - 定时任务执行失败", e);
+//        } finally {
+//            // 重置标志为 false,表示任务已完成
+//            isRunning1.set(false);
+//        }
+//
+//    }
+
+
     @Scheduled(fixedRate = 60000) // 每分钟执行一次
     public void checkWatchStatus() {
         // 尝试设置标志为 true,表示任务开始执行
         if (!isRunning1.compareAndSet(false, true)) {
-            log.warn("检查看课中任务执行 - 上一个任务尚未完成,跳过此次执行");
+            log.info("检查看课中任务执行 - 上一个任务尚未完成,跳过此次执行");
             return;
         }
+
         try {
             log.info("检查看课中任务执行>>>>>>>>>>>>");
             courseWatchLogService.scheduleBatchUpdateToDatabase();
-            courseWatchLogService.scheduleBatchUpdateToDatabaseIsOpen();
             courseWatchLogService.checkWatchStatus();
             log.info("检查看课中任务执行完成>>>>>>>>>>>>");
-        }catch (Exception e) {
-            log.error("检查看课中任务执行完成 - 定时任务执行失败", e);
+
+            // 检查当前时间是否为整五分钟(0, 5, 10, 15, ... 55分钟)
+            Calendar calendar = Calendar.getInstance();
+            int minute = calendar.get(Calendar.MINUTE);
+
+            // 只有当分钟数是5的倍数时才执行创建完课消息
+            if (minute % 5 == 0) {
+                try {
+                    long startTime = System.currentTimeMillis();
+                    log.info("创建完课消息 - 定时任务开始"+System.currentTimeMillis());
+                    sopLogsTaskService.createCourseFinishMsg();
+                    long endTime = System.currentTimeMillis();
+                    long duration = endTime - startTime;
+                    log.info("创建完课消息 - 定时任务成功完成"+duration);
+                } catch (Exception e) {
+                    log.error("创建完课消息 - 定时任务执行失败", ExceptionUtils.getStackTrace(e));
+                }
+            }
+        } catch (Exception e) {
+            log.error("检查看课中任务执行完成 - 定时任务执行失败", ExceptionUtils.getStackTrace(e));
         } finally {
             // 重置标志为 false,表示任务已完成
             isRunning1.set(false);
         }
-
     }
 
 

+ 5 - 4
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java

@@ -14,10 +14,7 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.file.OssException;
-import com.fs.common.utils.DateUtils;
-import com.fs.common.utils.PatternUtils;
-import com.fs.common.utils.SecurityUtils;
-import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.*;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
 import com.fs.company.param.CompanyUserAreaParam;
@@ -1040,6 +1037,10 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     public R getBindInfo(Long companyUserId) {
         //链接
         String url = bindBaseUrl + companyUserId;
+        if (CloudHostUtils.hasCloudHostName("木易华康")) {
+            url = "https://h5api.muyikp.com/bindcompanyuser?companyUserId=" + companyUserId;
+        }
+
         //查询码是否存在
         CompanyUser companyUser = selectCompanyUserById(companyUserId);
         if (companyUser != null) {

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

@@ -105,7 +105,6 @@ public class RedisKeyScanner {
                     .match(pattern)
                     .count(1000) // 每次扫描的批量大小,可根据实际情况调整
                     .build())) {
-
                 while (cursor.hasNext()) {
                     keys.add(new String(cursor.next(), StandardCharsets.UTF_8)); // 显式指定字符集,避免依赖默认值
                 }

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

@@ -260,7 +260,16 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "<if test= 'sendType == 1 '> " +
             " LEFT JOIN company_user cu on cu.user_id=o.company_user_id\n" +
             "</if>\n" +
-            "where o.company_id=#{companyId} " +
+            "<where> \n" +
+            "<if test = 'companyId != null and companyId != 0'>\n" +
+            "and o.company_id=#{companyId} \n" +
+            "</if>\n" +
+            "<if test = 'userIds != null and userIds.size() > 0'>\n" +
+            "and o.company_user_id in \n" +
+            "        <foreach collection='userIds' item='id' open='(' separator=',' close=')'>\n" +
+            "            #{id}\n" +
+            "        </foreach>\n" +
+            "</if>\n" +
             "<if test= 'sendType != null '> " +
             "       and  send_type= #{sendType} " +
             "</if>\n" +
@@ -282,6 +291,7 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "<if test ='videoId !=null'> " +
             "     and o.video_id = #{videoId} " +
             "</if>" +
+            "</where>" +
             "GROUP BY o.video_id," +
             "<if test= 'sendType != 1 '> " +
             " o.qw_user_id," +

+ 25 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogStatisticsListParam.java

@@ -4,8 +4,12 @@ import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.utils.DateUtils;
 import lombok.Data;
 
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.StringJoiner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 @Data
 public class FsCourseWatchLogStatisticsListParam {
@@ -56,4 +60,25 @@ public class FsCourseWatchLogStatisticsListParam {
      * 标识('course','company')
      */
     private  String dimension ;
+    /**
+     * 今正要求把公司筛选条件优化到细分至部门,销售(前端判断,只有今正传)
+     */
+    private List<String> userIds;
+    public List<String> getUserIds() {
+        if (userIds == null || userIds.isEmpty()) {
+            return userIds;
+        }
+        ArrayList<String> longs = new ArrayList<>();
+        userIds.forEach(userId -> {
+            if (userId.contains("user_")){
+                StringJoiner joiner = new StringJoiner("");
+                Matcher matcher = Pattern.compile("\\d+").matcher(userId);
+                while (matcher.find()) {
+                    joiner.add(matcher.group());
+                }
+                longs.add(joiner.toString());
+            }
+        });
+        return longs;
+    }
 }

+ 324 - 113
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -20,6 +20,7 @@ import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.mapper.CompanyMapper;
 import com.fs.course.config.CourseConfig;
+import com.fs.course.config.RedisKeyScanner;
 import com.fs.course.domain.*;
 import com.fs.course.mapper.*;
 import com.fs.course.param.*;
@@ -72,6 +73,9 @@ import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -759,6 +763,7 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
 
     @Override
     public List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO(FsCourseWatchLogStatisticsListParam param) {
+        if (param.getUserIds() != null && !param.getUserIds().isEmpty())param.setUserIds(param.getUserIds());
         return fsCourseWatchLogMapper.selectFsCourseWatchLogStatisticsListVO(param);
     }
 
@@ -808,143 +813,349 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     @Autowired
     private ISysConfigService configService;
 
+    @Autowired
+    private RedisKeyScanner redisKeyScanner;
+
     @Override
     public void scheduleBatchUpdateToDatabase() {
-        log.info("开始更新看课时长,检查完课>>>>>>");
-        //读取所有的key
-        Collection<String> keys = redisCache.keys("h5user:watch:duration:*");
-        //读取看课配置
-        String json = configService.selectConfigByKey("course.config");
-        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        try {
+            log.info("开始更新看课时长,检查完课>>>>>>");
 
-        List<FsCourseWatchLog> logs = new ArrayList<>();
-        List<FsCourseWatchLog> finishedLogs = new ArrayList<>();
-        for (String key : keys) {
-            //取key中数据
-            Long qwUserId = null;
-            Long videoId = null;
-            Long externalId = null;
-            try {
-                String[] parts = key.split(":");
-                qwUserId = Long.parseLong(parts[3]);
-                externalId = Long.parseLong(parts[4]);
-                videoId = Long.parseLong(parts[5]);
-            } catch (Exception e) {
-                log.error("key中id为null:{}", key);
-                continue;
-            }
-            String durationStr = redisCache.getCacheObject(key);
-            if (com.fs.common.utils.StringUtils.isEmpty(durationStr)) {
-                redisCache.deleteObject(key);
-                log.error("key中数据为null:{}", key);
-                continue;  // 如果 Redis 中没有记录,跳过
+            // 读取所有的key
+            Set<String> keys = redisKeyScanner.scanMatchKey("h5user:watch:duration:*");
+            log.info("共扫描到 {} 个待处理键", keys.size());
+
+            // 如果keys为空,直接返回
+            if (CollectionUtils.isEmpty(keys)) {
+                return;
             }
-            Long duration = Long.valueOf(durationStr);
 
-            FsCourseWatchLog watchLog = new FsCourseWatchLog();
-            watchLog.setVideoId(videoId);
-            watchLog.setQwUserId(qwUserId);
-            watchLog.setQwExternalContactId(externalId);
-            watchLog.setDuration(duration);
+            // 读取看课配置
+            String json = configService.selectConfigByKey("course.config");
+            CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
-            //取对应视频的时长
-            Long videoDuration = 0L;
-            try {
-                videoDuration = getVideoDuration(videoId);
-            } catch (Exception e) {
-                log.error("视频时长识别错误:{}", key);
-                continue;
-            }
+            // 创建线程安全的集合
+            List<FsCourseWatchLog> logs = Collections.synchronizedList(new ArrayList<>());
 
-            if (videoDuration != null && videoDuration != 0) {
-                boolean complete = false;
-                // 判断百分比
-                if (config.getCompletionMode() == 1 && config.getAnswerRate() != null) {
-                    long percentage = (duration * 100 / videoDuration);
-                    complete = percentage >= config.getAnswerRate();
-                }
-                // 判断分钟数
-                if (config.getCompletionMode() == 2 && config.getMinutesNum() != null) {
-                    int i = config.getMinutesNum() * 60;
-                    complete = videoDuration > i;
-                }
-                //判断是否完课
-                if (complete) {
-                    watchLog.setLogType(2); // 设置状态为“已完成”
-                    watchLog.setFinishTime(new Date());
-                    String heartbeatKey = "h5user:watch:heartbeat:" + qwUserId + ":" + externalId + ":" + videoId;
-                    // 完课删除心跳记录
-                    redisCache.deleteObject(heartbeatKey);
-                    // 完课删除看课时长记录
-                    redisCache.deleteObject(key);
+            // 创建线程池(根据服务器配置调整线程数)
+            int threadCount = Math.min(Runtime.getRuntime().availableProcessors() * 2, 8);
+            ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
 
-                    finishedLogs.add(watchLog);
-                }
+            // 将keys分成多个批次,每个线程处理一批
+            List<List<String>> keyBatches = partitionKeys(new ArrayList<>(keys), threadCount * 10);
+
+            log.info("开始多线程处理,共 {} 个批次", keyBatches.size());
+
+            int type = 1; //检查完课任务
+
+            // 创建所有任务
+            List<CompletableFuture<Void>> futures = new ArrayList<>();
+            for (List<String> batchKeys : keyBatches) {
+                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                    processKeyBatch(batchKeys, config, logs,type);
+                }, executorService);
+                futures.add(future);
             }
-            //集合中增加
-            logs.add(watchLog);
-        }
 
-        batchUpdateFsCourseWatchLog(logs, 100);
+            // 等待所有任务完成
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+
+            // 关闭线程池
+            executorService.shutdown();
+
+            // 批量更新到数据库
+            batchUpdateFsCourseWatchLog(logs, 100);
 
-        // 完课打标签
-        if (CollectionUtils.isNotEmpty(finishedLogs)) {
-            fsTagUpdateService.onCourseWatchFinishedBatch(finishedLogs);
+        } catch (Exception e) {
+            log.error("定时任务执行失败 scheduleBatchUpdateToDatabase", e);
         }
+//        log.info("开始更新看课时长,检查完课>>>>>>");
+//        //读取所有的key
+//        Collection<String> keys = redisCache.keys("h5user:watch:duration:*");
+//        //读取看课配置
+//        String json = configService.selectConfigByKey("course.config");
+//        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+//
+//        List<FsCourseWatchLog> logs = new ArrayList<>();
+//        List<FsCourseWatchLog> finishedLogs = new ArrayList<>();
+//        for (String key : keys) {
+//            //取key中数据
+//            Long qwUserId = null;
+//            Long videoId = null;
+//            Long externalId = null;
+//            try {
+//                String[] parts = key.split(":");
+//                qwUserId = Long.parseLong(parts[3]);
+//                externalId = Long.parseLong(parts[4]);
+//                videoId = Long.parseLong(parts[5]);
+//            } catch (Exception e) {
+//                log.error("key中id为null:{}", key);
+//                continue;
+//            }
+//            String durationStr = redisCache.getCacheObject(key);
+//            if (com.fs.common.utils.StringUtils.isEmpty(durationStr)) {
+//                redisCache.deleteObject(key);
+//                log.error("key中数据为null:{}", key);
+//                continue;  // 如果 Redis 中没有记录,跳过
+//            }
+//            Long duration = Long.valueOf(durationStr);
+//
+//            FsCourseWatchLog watchLog = new FsCourseWatchLog();
+//            watchLog.setVideoId(videoId);
+//            watchLog.setQwUserId(qwUserId);
+//            watchLog.setQwExternalContactId(externalId);
+//            watchLog.setDuration(duration);
+//
+//            //取对应视频的时长
+//            Long videoDuration = 0L;
+//            try {
+//                videoDuration = getVideoDuration(videoId);
+//            } catch (Exception e) {
+//                log.error("视频时长识别错误:{}", key);
+//                continue;
+//            }
+//
+//            if (videoDuration != null && videoDuration != 0) {
+//                boolean complete = false;
+//                // 判断百分比
+//                if (config.getCompletionMode() == 1 && config.getAnswerRate() != null) {
+//                    long percentage = (duration * 100 / videoDuration);
+//                    complete = percentage >= config.getAnswerRate();
+//                }
+//                // 判断分钟数
+//                if (config.getCompletionMode() == 2 && config.getMinutesNum() != null) {
+//                    int i = config.getMinutesNum() * 60;
+//                    complete = videoDuration > i;
+//                }
+//                //判断是否完课
+//                if (complete) {
+//                    watchLog.setLogType(2); // 设置状态为“已完成”
+//                    watchLog.setFinishTime(new Date());
+//                    String heartbeatKey = "h5user:watch:heartbeat:" + qwUserId + ":" + externalId + ":" + videoId;
+//                    // 完课删除心跳记录
+//                    redisCache.deleteObject(heartbeatKey);
+//                    // 完课删除看课时长记录
+//                    redisCache.deleteObject(key);
+//
+//                    finishedLogs.add(watchLog);
+//                }
+//            }
+//            //集合中增加
+//            logs.add(watchLog);
+//        }
+//
+//        batchUpdateFsCourseWatchLog(logs, 100);
+//
+//        // 完课打标签
+//        if (CollectionUtils.isNotEmpty(finishedLogs)) {
+//            fsTagUpdateService.onCourseWatchFinishedBatch(finishedLogs);
+//        }
     }
 
     @Override
     public void checkWatchStatus() {
-        log.info("开始更新看课中断记录>>>>>");
-        // 从 Redis 中获取所有正在看课的用户记录
-        Collection<String> keys = redisCache.keys("h5user:watch:heartbeat:*");
-        LocalDateTime now = LocalDateTime.now();
-        List<FsCourseWatchLog> logs = new ArrayList<>();
+        try {
+            log.info("开始更新看课中断记录>>>>>");
 
-        List<FsCourseWatchLog> watchingLogs = new ArrayList<>();
-        for (String key : keys) {
-            FsCourseWatchLog watchLog = new FsCourseWatchLog();
-            //取key中数据
-            Long qwUserId = null;
-            Long videoId = null;
-            Long externalId = null;
-            try {
-                String[] parts = key.split(":");
-                qwUserId = Long.parseLong(parts[3]);
-                externalId = Long.parseLong(parts[4]);
-                videoId = Long.parseLong(parts[5]);
-            } catch (Exception e) {
-                log.error("key中id为null:{}", key);
-                continue;
+            // 读取所有的key
+            Set<String> keys = redisKeyScanner.scanMatchKey("h5user:watch:heartbeat:*");
+            log.info("共扫描到 {} 个待处理键", keys.size());
+
+            // 如果keys为空,直接返回
+            if (CollectionUtils.isEmpty(keys)) {
+                return;
             }
-            // 获取最后心跳时间
-            String lastHeartbeatStr = redisCache.getCacheObject(key);
-            if (com.fs.common.utils.StringUtils.isEmpty(lastHeartbeatStr)) {
-                redisCache.deleteObject(key);
-                continue; // 如果 Redis 中没有记录,跳过
+
+            // 读取看课配置
+            String json = configService.selectConfigByKey("course.config");
+            CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+
+            // 创建线程安全的集合
+            List<FsCourseWatchLog> logs = Collections.synchronizedList(new ArrayList<>());
+
+            // 创建线程池(根据服务器配置调整线程数)
+            int threadCount = Math.min(Runtime.getRuntime().availableProcessors() * 2, 8);
+            ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
+
+            // 将keys分成多个批次,每个线程处理一批
+            List<List<String>> keyBatches = partitionKeys(new ArrayList<>(keys), threadCount * 10);
+
+            log.info("开始多线程处理,共 {} 个批次", keyBatches.size());
+
+            int type = 2; //检查看课中断
+
+            // 创建所有任务
+            List<CompletableFuture<Void>> futures = new ArrayList<>();
+            for (List<String> batchKeys : keyBatches) {
+                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                    processKeyBatch(batchKeys, config, logs, type);
+                }, executorService);
+                futures.add(future);
             }
-            LocalDateTime lastHeartbeatTime = LocalDateTime.parse(lastHeartbeatStr);
-            Duration duration = Duration.between(lastHeartbeatTime, now);
 
-            watchLog.setVideoId(videoId);
-            watchLog.setQwUserId(qwUserId);
-            watchLog.setQwExternalContactId(externalId);
-            // 如果超过一分钟没有心跳,标记为“观看中断”
-            if (duration.getSeconds() >= 60) {
-                watchLog.setLogType(4);
-                // 从 Redis 中删除该记录
-                redisCache.deleteObject(key);
-            } else {
-                watchLog.setLogType(1);
-                watchingLogs.add(watchLog);
+            // 等待所有任务完成
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+
+            // 关闭线程池
+            executorService.shutdown();
+
+            // 批量更新到数据库
+            batchUpdateFsCourseWatchLog(logs, 100);
+
+        } catch (Exception e) {
+            log.error("定时任务执行失败checkWatchStatus", e);
+        }
+//        log.info("开始更新看课中断记录>>>>>");
+//        // 从 Redis 中获取所有正在看课的用户记录
+//        Collection<String> keys = redisCache.keys("h5user:watch:heartbeat:*");
+//        LocalDateTime now = LocalDateTime.now();
+//        List<FsCourseWatchLog> logs = new ArrayList<>();
+//
+//        List<FsCourseWatchLog> watchingLogs = new ArrayList<>();
+//        for (String key : keys) {
+//            FsCourseWatchLog watchLog = new FsCourseWatchLog();
+//            //取key中数据
+//            Long qwUserId = null;
+//            Long videoId = null;
+//            Long externalId = null;
+//            try {
+//                String[] parts = key.split(":");
+//                qwUserId = Long.parseLong(parts[3]);
+//                externalId = Long.parseLong(parts[4]);
+//                videoId = Long.parseLong(parts[5]);
+//            } catch (Exception e) {
+//                log.error("key中id为null:{}", key);
+//                continue;
+//            }
+//            // 获取最后心跳时间
+//            String lastHeartbeatStr = redisCache.getCacheObject(key);
+//            if (com.fs.common.utils.StringUtils.isEmpty(lastHeartbeatStr)) {
+//                redisCache.deleteObject(key);
+//                continue; // 如果 Redis 中没有记录,跳过
+//            }
+//            LocalDateTime lastHeartbeatTime = LocalDateTime.parse(lastHeartbeatStr);
+//            Duration duration = Duration.between(lastHeartbeatTime, now);
+//
+//            watchLog.setVideoId(videoId);
+//            watchLog.setQwUserId(qwUserId);
+//            watchLog.setQwExternalContactId(externalId);
+//            // 如果超过一分钟没有心跳,标记为“观看中断”
+//            if (duration.getSeconds() >= 60) {
+//                watchLog.setLogType(4);
+//                // 从 Redis 中删除该记录
+//                redisCache.deleteObject(key);
+//            } else {
+//                watchLog.setLogType(1);
+//                watchingLogs.add(watchLog);
+//            }
+//            logs.add(watchLog);
+//        }
+//        batchUpdateFsCourseWatchLog(logs, 100);
+//
+//        if (CollectionUtils.isNotEmpty(watchingLogs)) {
+//            fsTagUpdateService.onCourseWatchingBatch(watchingLogs);
+//        }
+    }
+
+    /**
+     * 处理一个key批次
+     */
+    private void processKeyBatch(List<String> batchKeys, CourseConfig config, List<FsCourseWatchLog> logs,int type) {
+        LocalDateTime now = LocalDateTime.now();
+        for (String key : batchKeys) {
+            try {
+                // 取key中数据
+                Long qwUserId = null;
+                Long videoId = null;
+                Long externalId = null;
+                try {
+                    String[] parts = key.split(":");
+                    qwUserId = Long.parseLong(parts[3]);
+                    externalId = Long.parseLong(parts[4]);
+                    videoId = Long.parseLong(parts[5]);
+                } catch (Exception e) {
+                    log.error("key中id为null:{}", key);
+                    continue;
+                }
+                FsCourseWatchLog watchLog = new FsCourseWatchLog();
+                //检查完课
+                if (type==1){
+                    String durationStr = redisCache.getCacheObject(key);
+                    if (com.fs.common.utils.StringUtils.isEmpty(durationStr)) {
+                        redisCache.deleteObject(key);
+                        log.error("key中数据为null:{}", key);
+                        continue;  // 如果 Redis 中没有记录,跳过
+                    }
+                    Long duration = Long.valueOf(durationStr);
+
+                    watchLog.setDuration(duration);
+
+                    // 取对应视频的时长
+                    Long videoDuration;
+                    try {
+                        videoDuration = getVideoDuration(videoId);
+                    } catch (Exception e) {
+                        log.error("视频时长识别错误:{}", key);
+                        continue;
+                    }
+
+
+                    if (videoDuration != null && videoDuration != 0) {
+                        // 判断是否完课
+                        long percentage = (duration * 100 / videoDuration);
+                        if (percentage >= config.getAnswerRate()) {
+                            watchLog.setLogType(2); // 设置状态为"已完成"
+                            watchLog.setFinishTime(new Date());
+                            String heartbeatKey = "h5user:watch:heartbeat:" + qwUserId + ":" + externalId + ":" + videoId;
+                            // 完课删除心跳记录
+                            redisCache.deleteObject(heartbeatKey);
+                            // 完课删除看课时长记录
+                            redisCache.deleteObject(key);
+                        }
+                    }
+                }else{
+                    //检查看课中断
+                    // 获取最后心跳时间
+                    String lastHeartbeatStr = redisCache.getCacheObject(key);
+                    if (com.fs.common.utils.StringUtils.isEmpty(lastHeartbeatStr)) {
+                        redisCache.deleteObject(key);
+                        continue; // 如果 Redis 中没有记录,跳过
+                    }
+                    LocalDateTime lastHeartbeatTime = LocalDateTime.parse(lastHeartbeatStr);
+                    Duration duration = Duration.between(lastHeartbeatTime, now);
+                    // 如果超过一分钟没有心跳,标记为“观看中断”
+                    if (duration.getSeconds() >= 60) {
+                        watchLog.setLogType(4);
+                        // 从 Redis 中删除该记录
+                        redisCache.deleteObject(key);
+                    } else {
+                        watchLog.setLogType(1);
+                    }
+                }
+                watchLog.setVideoId(videoId);
+                watchLog.setQwUserId(qwUserId);
+                watchLog.setQwExternalContactId(externalId);
+
+
+                // 线程安全地添加到集合
+                logs.add(watchLog);
+
+            } catch (Exception e) {
+                log.error("处理key {} 时发生异常: {}", key, e.getMessage());
             }
-            logs.add(watchLog);
         }
-        batchUpdateFsCourseWatchLog(logs, 100);
+    }
 
-        if (CollectionUtils.isNotEmpty(watchingLogs)) {
-            fsTagUpdateService.onCourseWatchingBatch(watchingLogs);
+    /**
+     * 将keys分成多个批次
+     */
+    private List<List<String>> partitionKeys(List<String> keys, int batchSize) {
+        List<List<String>> batches = new ArrayList<>();
+        for (int i = 0; i < keys.size(); i += batchSize) {
+            int end = Math.min(i + batchSize, keys.size());
+            batches.add(keys.subList(i, end));
         }
+        return batches;
     }
 
     @Override

+ 3 - 2
fs-service/src/main/java/com/fs/his/vo/FsIntegralOrderListVO.java

@@ -34,9 +34,10 @@ public class FsIntegralOrderListVO {
     private String userAddress;
 
     /** 商品信息 */
-    @Excel(name = "商品信息")
+    //@Excel(name = "商品信息")
     private String itemJson;
-    @Excel(name = "商品名称")
+    //@Excel(name = "商品名称")
+    @Excel(name = "商品信息")//卓美 导出时候把商品名称 数据拼接在一起
     private String goodsName;
 
     /** 商品条码 */

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderVO.java

@@ -277,4 +277,6 @@ public class FsStoreOrderVO implements Serializable
     // 是否审核,1-是,0-否
     private Boolean isAudit;
 
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date auditTime;
 }

+ 26 - 0
fs-service/src/main/java/com/fs/qw/param/QwWatchLogStatisticsListParam.java

@@ -4,8 +4,12 @@ import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.utils.DateUtils;
 import lombok.Data;
 
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.StringJoiner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 @Data
 public class QwWatchLogStatisticsListParam {
@@ -41,4 +45,26 @@ public class QwWatchLogStatisticsListParam {
     private Long pageNum;
     private Long pageSize;
     private List<Long> filterDeptIds;
+    /**
+     * 今正要求把公司筛选条件优化到细分至部门,销售(前端判断,只有今正传)
+     */
+    private List<String> userIds;
+    public List<String> getUserIds() {
+        if (userIds == null || userIds.isEmpty()) {
+            return userIds;
+        }
+        ArrayList<String> longs = new ArrayList<>();
+        userIds.forEach(userId -> {
+            if (userId.contains("user_")){
+                StringJoiner joiner = new StringJoiner("");
+                Matcher matcher = Pattern.compile("\\d+").matcher(userId);
+                while (matcher.find()) {
+                    joiner.add(matcher.group());
+                }
+                longs.add(joiner.toString());
+            }
+
+        });
+        return longs;
+    }
 }

+ 4 - 2
fs-service/src/main/java/com/fs/qw/service/impl/QwWatchLogServiceImpl.java

@@ -403,7 +403,7 @@ public class QwWatchLogServiceImpl extends ServiceImpl<QwWatchLogMapper, QwWatch
     public TableDataInfo selectQwWatchLogAllStatisticsListVONew(QwWatchLogStatisticsListParam param) {
         // 获取当前公司下的所有销售
         List<Long> userIds;
-        if(param.getCompanyUserId()  == null){
+        if(param.getCompanyUserId()  == null && (param.getUserIds() == null || param.getUserIds().isEmpty())){
             List<CompanyUser> companyUsers = companyUserMapper.selectCompanyUserByCompanyId(param.getCompanyId());
             if(CollectionUtils.isEmpty(companyUsers)){
                 throw new CustomException("该公司下面没有任何销售!");
@@ -412,7 +412,9 @@ public class QwWatchLogServiceImpl extends ServiceImpl<QwWatchLogMapper, QwWatch
                     .map(CompanyUser::getUserId)
                     .filter(Objects::nonNull)
                     .collect(Collectors.toList());
-        } else {
+        } else if((param.getUserIds() != null && !param.getUserIds().isEmpty())){
+            userIds = param.getUserIds().stream().map(Long::valueOf).collect(Collectors.toList());
+        }else{
             userIds = new ArrayList<>();
             userIds.add(param.getCompanyUserId());
         }

+ 11 - 2
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -147,7 +147,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 and l.update_time &gt;= #{maps.upSTime}
             </if>
             <if test='maps.upETime != null '>
-                and l.update_time &lt;= #{maps.upETime}
+                and l.update_time &lt; date_add(#{maps.upETime}, interval 1 day)
             </if>
             <if test="maps.sopIds != null and maps.sopIds.size() > 0">
                 and l.sop_id in
@@ -1058,7 +1058,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="sendType == 1">
             LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
         </if>
-        WHERE o.company_id = #{companyId}
+        WHERE 1=1
+        <if test = 'companyId != null and companyId != 0'>
+            and o.company_id=#{companyId}
+            </if>
+        <if test = 'userIds != null and userIds.size() > 0'>
+        and o.company_user_id in
+                <foreach collection='userIds' item='id' open='(' separator=',' close=')'>
+                        #{id}
+                   </foreach>
+        </if>
         <!-- 发送类型筛选 -->
         <if test="sendType != null">
             AND send_type = #{sendType}

+ 2 - 2
fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml

@@ -1721,7 +1721,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectFsStoreOrderListVO" resultType="com.fs.hisStore.vo.FsStoreOrderVO">
         select DISTINCT o.id,o.order_code,o.extend_order_id,o.pay_order_id,o.bank_order_id,o.user_id,o.real_name,o.user_phone,o.user_address,o.cart_id,o.freight_price,o.total_num,o.total_price,o.total_postage,o.pay_price,o.pay_postage,o.pay_delivery,o.pay_money,o.deduction_price,o.coupon_id,o.coupon_price,o.paid,o.pay_time,o.pay_type,o.create_time,o.update_time,o.status,o.refund_status,o.refund_reason_wap_img,o.refund_reason_wap_explain,o.refund_reason_time,o.refund_reason_wap,o.refund_reason,o.refund_price,o.delivery_sn,o.delivery_name,o.delivery_type,o.delivery_id,o.gain_integral,o.use_integral,o.pay_integral,o.back_integral,o.mark,o.is_del,o.remark,o.verify_code,o.store_id,o.shipping_type,o.is_channel,o.is_remind,o.is_sys_del,o.is_prescribe,o.prescribe_id,o.company_id,o.company_user_id,o.is_package,o.package_json,o.order_type,o.package_id,o.finish_time,o.delivery_status,o.delivery_pay_status,o.delivery_time,o.delivery_pay_time,o.delivery_pay_money,o.tui_money,o.tui_money_status,o.delivery_import_time,o.tui_user_id,o.tui_user_money_status,o.order_create_type,o.store_house_code,o.dept_id,o.is_edit_money,o.customer_id,o.is_pay_remain,o.delivery_send_time,o.certificates,o.upload_time,o.item_json,o.schedule_id,o.delivery_pay_type,o.order_visit,o.service_fee,o.cycle,o.prescribe_price,o.follow_doctor_id,o.follow_time,o.user_coupon_id,o.order_medium,o.erp_phone
         ,u.phone,u.register_code,u.register_date,u.source, c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber
-        , csc.name miniProgramName,fsp.cost, fspc.cate_name,spavs.bar_code, sp_latest.bank_transaction_id as bankTransactionId, o.is_audit
+        , csc.name miniProgramName,fsp.cost, fspc.cate_name,spavs.bar_code, sp_latest.bank_transaction_id as bankTransactionId, o.is_audit, o.audit_time
         from fs_store_order_scrm o
         left join fs_user u on o.user_id=u.user_id
         left join company c on c.company_id=o.company_id
@@ -2272,7 +2272,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <!-- 批量更新订单审核状态 -->
     <update id="batchUpdateAuditStatus">
         UPDATE fs_store_order_scrm
-        SET is_audit = #{isAudit}
+        SET is_audit = #{isAudit},audit_time = now()
         WHERE id IN
         <foreach collection="orderIds" item="orderId" open="(" separator="," close=")">
             #{orderId}