Browse Source

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

15376779826 2 tuần trước cách đây
mục cha
commit
ccabd39383
100 tập tin đã thay đổi với 2715 bổ sung242 xóa
  1. 10 1
      fs-admin/src/main/java/com/fs/company/controller/CompanyMoneyLogsController.java
  2. 4 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyUserAllController.java
  3. 35 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseCategoryController.java
  4. 4 1
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  5. 53 0
      fs-admin/src/main/java/com/fs/his/task/Task.java
  6. 1 1
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  7. 3 3
      fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java
  8. 1 1
      fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java
  9. 37 2
      fs-admin/src/main/java/com/fs/live/controller/OrderController.java
  10. 1 1
      fs-admin/src/main/java/com/fs/qw/controller/QwExternalContactController.java
  11. 43 0
      fs-admin/src/main/java/com/fs/qw/controller/QwPushCountController.java
  12. 96 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyAiWorkflowController.java
  13. 9 1
      fs-company/src/main/java/com/fs/company/controller/company/CompanyMoneyLogsController.java
  14. 39 2
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  15. 7 0
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java
  16. 11 3
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseWatchLogController.java
  17. 45 7
      fs-company/src/main/java/com/fs/company/controller/live/OrderController.java
  18. 11 5
      fs-company/src/main/java/com/fs/company/controller/store/FsUserAddressController.java
  19. 21 0
      fs-company/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  20. 12 3
      fs-company/src/main/java/com/fs/user/FsUserAdminController.java
  21. 8 1
      fs-live-app/src/main/resources/application.yml
  22. 5 5
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  23. 43 11
      fs-service/src/main/java/com/fs/company/mapper/CompanyMoneyLogsMapper.java
  24. 9 0
      fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java
  25. 9 1
      fs-service/src/main/java/com/fs/company/service/impl/CompanyMoneyLogsServiceImpl.java
  26. 23 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  27. 1 1
      fs-service/src/main/java/com/fs/company/vo/CompanyMoneyLogsExport1VO.java
  28. 1 1
      fs-service/src/main/java/com/fs/company/vo/CompanyMoneyLogsVO.java
  29. 121 0
      fs-service/src/main/java/com/fs/course/config/RedisKeyScanner.java
  30. 14 0
      fs-service/src/main/java/com/fs/course/dto/FsCourseCategoryImportDTO.java
  31. 61 0
      fs-service/src/main/java/com/fs/course/dto/RedisKeyInfo.java
  32. 26 0
      fs-service/src/main/java/com/fs/course/enums/RedisKeyType.java
  33. 12 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseCategoryMapper.java
  34. 3 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  35. 8 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseCategoryService.java
  36. 89 1
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseCategoryServiceImpl.java
  37. 56 0
      fs-service/src/main/java/com/fs/fastGpt/param/FastGptPushTokenDeptTotalParam.java
  38. 21 0
      fs-service/src/main/java/com/fs/fastGpt/vo/FastGptPushTokenDeptTotalVO.java
  39. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsAiWorkflow.java
  40. 5 0
      fs-service/src/main/java/com/fs/his/mapper/FsAiWorkflowMapper.java
  41. 8 0
      fs-service/src/main/java/com/fs/his/mapper/FsAiWorkflowNodeMapper.java
  42. 2 2
      fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderMapper.java
  43. 1 0
      fs-service/src/main/java/com/fs/his/param/FsCompanyMoneyLogsExportParam.java
  44. 18 0
      fs-service/src/main/java/com/fs/his/service/IFsAiWorkflowService.java
  45. 46 0
      fs-service/src/main/java/com/fs/his/service/impl/FsAiWorkflowServiceImpl.java
  46. 11 10
      fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderServiceImpl.java
  47. 1 0
      fs-service/src/main/java/com/fs/hisStore/config/StoreConfig.java
  48. 10 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
  49. 10 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStorePaymentScrmMapper.java
  50. 6 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductAttrValueScrmMapper.java
  51. 6 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java
  52. 40 0
      fs-service/src/main/java/com/fs/hisStore/mapper/MergedOrderMapper.java
  53. 3 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderParam.java
  54. 1 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStorePaymentScrmService.java
  55. 8 0
      fs-service/src/main/java/com/fs/hisStore/service/IMergedOrderService.java
  56. 3 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  57. 5 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStorePaymentScrmServiceImpl.java
  58. 40 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  59. 36 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java
  60. 0 2
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreProductListVO.java
  61. 3 1
      fs-service/src/main/java/com/fs/live/domain/LiveCouponUser.java
  62. 7 0
      fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java
  63. 7 0
      fs-service/src/main/java/com/fs/live/mapper/LiveOrderPaymentMapper.java
  64. 8 0
      fs-service/src/main/java/com/fs/live/mapper/LiveWatchLogMapper.java
  65. 3 0
      fs-service/src/main/java/com/fs/live/param/MergedOrderQueryParam.java
  66. 2 0
      fs-service/src/main/java/com/fs/live/service/ILiveOrderService.java
  67. 4 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveCouponServiceImpl.java
  68. 41 23
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  69. 123 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java
  70. 5 0
      fs-service/src/main/java/com/fs/live/vo/HandleUserTagVO.java
  71. 1 1
      fs-service/src/main/java/com/fs/live/vo/MergedOrderVO.java
  72. 61 0
      fs-service/src/main/java/com/fs/live/vo/PaymentExportVO.java
  73. 41 0
      fs-service/src/main/java/com/fs/live/vo/PaymentImportVO.java
  74. 1 1
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  75. 1 0
      fs-service/src/main/java/com/fs/qw/mapper/QwFriendWelcomeMapper.java
  76. 3 0
      fs-service/src/main/java/com/fs/qw/mapper/QwRestrictionPushRecordMapper.java
  77. 1 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  78. 4 0
      fs-service/src/main/java/com/fs/qw/service/IQwPushCountService.java
  79. 7 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwPushCountServiceImpl.java
  80. 140 6
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  81. 2 2
      fs-service/src/main/resources/application-config-druid-bjzm-test.yml
  82. 2 2
      fs-service/src/main/resources/application-config-druid-bjzm.yml
  83. 7 1
      fs-service/src/main/resources/application-config-fzbt.yml
  84. 1 0
      fs-service/src/main/resources/mapper/company/CompanyUserMapper.xml
  85. 1 0
      fs-service/src/main/resources/mapper/course/FsCourseFinishTempParentMapper.xml
  86. 11 1
      fs-service/src/main/resources/mapper/his/FsAiWorkflowMapper.xml
  87. 12 0
      fs-service/src/main/resources/mapper/his/FsAiWorkflowNodeMapper.xml
  88. 14 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
  89. 8 0
      fs-service/src/main/resources/mapper/hisStore/FsStorePaymentScrmMapper.xml
  90. 2 1
      fs-service/src/main/resources/mapper/hisStore/FsStoreProductPackageScrmMapper.xml
  91. 909 127
      fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml
  92. 2 2
      fs-service/src/main/resources/mapper/live/LiveAfterSalesMapper.xml
  93. 7 1
      fs-service/src/main/resources/mapper/live/LiveCouponUserMapper.xml
  94. 8 0
      fs-service/src/main/resources/mapper/live/LiveOrderMapper.xml
  95. 8 0
      fs-service/src/main/resources/mapper/live/LiveOrderPaymentMapper.xml
  96. 4 0
      fs-service/src/main/resources/mapper/live/LiveWatchLogMapper.xml
  97. 1 0
      fs-service/src/main/resources/mapper/qw/QwFriendWelcomeMapper.xml
  98. 31 0
      fs-service/src/main/resources/mapper/qw/QwRestrictionPushRecordMapper.xml
  99. 2 2
      fs-service/src/main/resources/mapper/sop/QwSopTempVoiceMapper.xml
  100. 4 4
      fs-user-app/src/main/java/com/fs/app/controller/InquiryOrderController.java

+ 10 - 1
fs-admin/src/main/java/com/fs/company/controller/CompanyMoneyLogsController.java

@@ -73,6 +73,7 @@ public class CompanyMoneyLogsController extends BaseController
         }
         if(companyMoneyLogs.getLogsType()!=null){
             if(companyMoneyLogs.getLogsType()==3 || companyMoneyLogs.getLogsType()==4 || companyMoneyLogs.getLogsType()==5 || companyMoneyLogs.getLogsType()==6 || companyMoneyLogs.getLogsType()==13|| companyMoneyLogs.getLogsType()==14){
+                // 根据type字段判断,支持商城订单(type=0)和直播订单(type=1)
                 List<CompanyMoneyLogsVO> list = companyMoneyLogsService.selectCompanyMoneyLogsMallVOList(companyMoneyLogs);
                 return getDataTable(list);
             }
@@ -156,7 +157,15 @@ public class CompanyMoneyLogsController extends BaseController
             task.setStatus(0);
             task.setStartTime(new Date());
             task.setSysType(1);
-            task.setRemark("导出商城订单明细");
+            String remark = "导出订单明细";
+            if (param.getOrderType() != null) {
+                if (param.getOrderType() == 0) {
+                    remark = "导出商城订单明细";
+                } else if (param.getOrderType() == 1) {
+                    remark = "导出直播订单明细";
+                }
+            }
+            task.setRemark(remark);
             task.setUserId(SecurityUtils.getUserId());
             exportTaskService.insertFsExportTask(task);
             param.setTaskId(task.getTaskId());

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

@@ -415,6 +415,8 @@ public class CompanyUserAllController extends BaseController {
     /**
      * 批量修改 销售的所属区域(临时的)
      */
+    @PreAuthorize("@ss.hasPermi('company:user:updateCompanyUserAreaList')")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
     @PostMapping("/updateCompanyUserAreaList")
     public R updateCompanyUserAreaList(@RequestBody CompanyUserAreaParam param)
     {
@@ -432,6 +434,7 @@ public class CompanyUserAllController extends BaseController {
         return  R.ok().put("data",subDomain);
     }
 
+    @PreAuthorize("@ss.hasPermi('company:user:setRegister')")
     @Log(title = "设置是否需要单独注册会员", businessType = BusinessType.UPDATE)
     @PutMapping("/setRegister")
     public AjaxResult setIsRegisterMember(@RequestParam Boolean status, @RequestBody List<Long> userIds) {
@@ -443,6 +446,7 @@ public class CompanyUserAllController extends BaseController {
         }
     }
 
+    @PreAuthorize("@ss.hasPermi('company:user:allowedAllRegister')")
     @Log(title = "是否允许所有方式注册会员", businessType = BusinessType.UPDATE)
     @PutMapping("/allowedAllRegister")
     public AjaxResult isAllowedAllRegister(@RequestParam Boolean status, @RequestBody List<Long> userIds) {

+ 35 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseCategoryController.java

@@ -5,6 +5,7 @@ import java.util.List;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.utils.ServletUtils;
+import com.fs.course.dto.FsCourseCategoryImportDTO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.domain.FsStoreProductCategory;
 import com.fs.his.vo.FsStoreProductCategoryVO;
@@ -33,6 +34,7 @@ import com.fs.course.domain.FsUserCourseCategory;
 import com.fs.course.service.IFsUserCourseCategoryService;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.core.page.TableDataInfo;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * 课堂分类Controller
@@ -174,4 +176,37 @@ public class FsUserCourseCategoryController extends BaseController
         List<OptionsVO> list = fsUserCourseCategoryService.selectCateListByPid(pid);
         return R.ok().put("data", list);
     }
+
+    // 下载模板
+    @GetMapping("/importTemplate")
+    public AjaxResult importTemplate() {
+        ExcelUtil<FsCourseCategoryImportDTO> util = new ExcelUtil<>(FsCourseCategoryImportDTO.class);
+        return util.importTemplateExcel("课堂分类导入模板");
+    }
+
+    @Log(title = "导入", businessType = BusinessType.IMPORT)
+    @PreAuthorize("@ss.hasPermi('course:userCourseCategory:importData')")
+    @PostMapping("/importData")
+    public AjaxResult importData(MultipartFile file) throws Exception {
+        ExcelUtil<FsCourseCategoryImportDTO> util = new ExcelUtil<>(FsCourseCategoryImportDTO.class);
+        List<FsCourseCategoryImportDTO> list = util.importExcel(file.getInputStream());
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (ObjectUtil.isEmpty(config.getIsBound()) || !config.getIsBound()){
+            userId = null;
+        }
+
+        return AjaxResult.success(fsUserCourseCategoryService.importData(list, userId));
+    }
+
+    @PreAuthorize("@ss.hasPermi('course:userCourseCategory:exportFail')")
+    @Log(title = "课堂分类", businessType = BusinessType.EXPORT)
+    @PostMapping("/exportFail")
+    public AjaxResult exportFail(@RequestBody List<FsCourseCategoryImportDTO> list) {
+        ExcelUtil<FsCourseCategoryImportDTO> util = new ExcelUtil<>(FsCourseCategoryImportDTO.class);
+        return util.exportExcel(list, "课堂分类错误数据");
+    }
 }

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

@@ -42,6 +42,7 @@ import com.fs.his.param.FsStoreOrderSetErpPhoneParam;
 import com.fs.his.service.*;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.*;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.service.ISysRoleService;
@@ -82,6 +83,8 @@ public class FsStoreOrderController extends BaseController
     @Autowired
     private IFsStoreOrderService fsStoreOrderService;
     @Autowired
+    private IFsStoreOrderScrmService fsStoreOrderScrmService;
+    @Autowired
     private IFsExpressService expressService;
     @Autowired
     private ICompanyMoneyLogsService moneyLogsService;
@@ -458,7 +461,7 @@ public class FsStoreOrderController extends BaseController
     @GetMapping("/payment/{orderId}")
     public AjaxResult getPayInfo(@PathVariable("orderId") Long orderId)
     {
-        return AjaxResult.success(fsStoreOrderService.selectFsStorePaymentByOrderId(orderId));
+        return AjaxResult.success(fsStoreOrderScrmService.selectFsStorePaymentByOrderId(orderId));
     }
     /**
      * 获取处方详细信息

+ 53 - 0
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -7,6 +7,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.service.impl.SmsServiceImpl;
 import com.fs.common.utils.DateUtils;
@@ -50,6 +51,8 @@ import com.fs.his.vo.FsSubOrderResultVO;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
 import com.fs.hisStore.domain.FsStorePaymentScrm;
 import com.fs.hisStore.mapper.FsStorePaymentScrmMapper;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
+import com.fs.hisStore.service.IFsStorePaymentScrmService;
 import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
 import com.fs.huifuPay.service.HuiFuService;
@@ -225,7 +228,11 @@ public class Task {
     @Autowired
     private AiHookService aiHookService;
 
+    @Autowired
+    private IFsStorePaymentScrmService paymentScrmService;
 
+    @Autowired
+    private IFsStoreOrderScrmService orderScrmService;
     /**
      * 定时任务,处理ai禁止回复之后的消息
      */
@@ -590,6 +597,52 @@ public class Task {
 
     }
 
+    public void paymentScrm() {
+
+        List<FsStorePaymentScrm> fsStorePaymentScrms = paymentScrmService.selectFsStorePaymentByAll();
+        fsStorePaymentScrms.forEach(payment -> {
+            try {
+                String payMode = payment.getPayMode();
+                logger.info("手动查询"+payment);
+                if (payMode.equals("hf")){
+                    V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
+                    request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
+                    request.setOrgHfSeqId(payment.getTradeNo());
+                    request.setAppId(payment.getAppId());
+                    HuiFuQueryOrderResult o = null;
+                    try {
+                        o = huiFuService.queryOrder(request);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                    logger.info("汇付返回"+o);
+                    if ("00000000".equals(o.getResp_code())) {
+                        if (o.getTrans_stat().equals("S")) {
+                            String[] order=o.getOrg_req_seq_id().split("-");
+                            switch (order[0]) {
+                                case "store":
+                                    orderScrmService.payConfirm(1,null,order[1], o.getOrg_hf_seq_id(),o.getOut_trans_id(),o.getParty_order_id());
+                                    break;
+                                case "store_remain":
+                                    orderScrmService.payRemainConfirm( order[1], o.getOrg_hf_seq_id(),o.getOut_trans_id(),o.getParty_order_id());
+                                    break;
+                                case "payment":
+                                    fsStorePaymentService.payConfirm(order[1],o.getOrg_hf_seq_id(),o.getOut_trans_id(),o.getParty_order_id());
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }catch (Exception e){
+                logger.error("当前订单同步失败-"+payment+":"+e.getMessage());
+            }
+
+        });
+
+
+    }
+
+
     public void addQwUserName() {
         QwCompany qwCompany = new QwCompany();
         List<QwCompany> companyList = qwCompanyService.selectQwCompanyList(qwCompany);

+ 1 - 1
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java

@@ -693,7 +693,7 @@ public class FsStoreOrderScrmController extends BaseController {
         ExpressInfoDTO expressInfoDTO = null;
         if (StringUtils.isNotEmpty(order.getDeliveryId())) {
             String lastFourNumber = "";
-            if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())) {
+            if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue()) || order.getDeliverySn().equals(ShipperCodeEnum.ZTO.getValue())) {
                 lastFourNumber = order.getUserPhone();
                 if (lastFourNumber.length() == 11) {
                     lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);

+ 3 - 3
fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java

@@ -331,10 +331,10 @@ public class LiveTask {
     public void deliveryOp() {
         List<LiveOrder> list = liveOrderService.selectUpdateExpress();
         if(list == null || list.isEmpty()) return;
-
+        Date now = new Date();
         for (LiveOrder order : list) {
-            order.setUpdateTime(new Date());
-            liveOrderService.updateLiveOrder(order);
+            order.setUpdateTime(now);
+            liveOrderService.updateTime(order);
             ErpOrderQueryRequert request = new ErpOrderQueryRequert();
             request.setCode(order.getExtendOrderId());
             IErpOrderService erpOrderService = getErpOrderService();

+ 1 - 1
fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java

@@ -484,7 +484,7 @@ public class MallStoreTask
     //每天执行一次
     public void syncExpress()
     {
-        List<Long> ids =fsStoreOrderMapper.selectSyncExpressIds();
+        List<Long> ids =fsStoreOrderMapper.selectSyncExpressIdsNoDate();
 
         for (Long id : ids) {
             FsStoreOrderExpressEditParam param =new FsStoreOrderExpressEditParam();

+ 37 - 2
fs-admin/src/main/java/com/fs/live/controller/OrderController.java

@@ -7,10 +7,13 @@ import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.PageDomain;
+import com.fs.common.core.page.TableSupport;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.constant.HttpStatus;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.utils.PhoneUtil;
 import com.fs.hisStore.dto.StoreOrderProductDTO;
@@ -64,7 +67,33 @@ public class OrderController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(MergedOrderQueryParam param)
     {
-        startPage();
+        // 从请求参数中获取分页信息
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+        
+        // 设置分页参数到 param
+        if (pageNum == null || pageNum < 1) {
+            pageNum = 1;
+            param.setPageNum(1);
+        } else {
+            param.setPageNum(pageNum);
+        }
+        if (pageSize == null || pageSize < 1) {
+            pageSize = 10;
+            param.setPageSize(10);
+        } else {
+            param.setPageSize(pageSize);
+        }
+        
+        // 在外面计算 offset,然后放在请求参数中
+        Integer offset = (pageNum - 1) * pageSize;
+        param.setOffset(offset);
+        
+        // 先查询总数(不分页)
+        long total = mergedOrderService.countMergedOrderList(param);
+        
+        // 查询分页数据
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         for (MergedOrderVO vo : list) {
             vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
@@ -73,7 +102,13 @@ public class OrderController extends BaseController
             vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
             vo.setCost(BigDecimal.ZERO);
         }
-        return getDataTable(list);
+        
+        TableDataInfo dataTable = new TableDataInfo();
+        dataTable.setCode(HttpStatus.SUCCESS);
+        dataTable.setMsg("查询成功");
+        dataTable.setRows(list);
+        dataTable.setTotal(total);
+        return dataTable;
     }
 
     /**

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

@@ -170,7 +170,7 @@ public class QwExternalContactController extends BaseController
     {
         return toAjax(qwExternalContactService.deleteQwExternalContactByIds(ids));
     }
-
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:getUserInfo')")
     @GetMapping(value = "getUserInfo/{id}")
     public R getUserInfo(@PathVariable("id") Long id)
     {

+ 43 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwPushCountController.java

@@ -10,6 +10,8 @@ import com.fs.common.core.page.TableSupport;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.fastGpt.domain.FastGptPushTokenTotal;
+import com.fs.fastGpt.param.FastGptPushTokenDeptTotalParam;
+import com.fs.fastGpt.vo.FastGptPushTokenDeptTotalVO;
 import com.fs.qw.domain.QwPushCount;
 import com.fs.qw.service.IQwPushCountService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -189,6 +191,47 @@ public class QwPushCountController extends BaseController {
 
 
 
+        list.add(sumTotal); // 将合计行添加到列表末尾
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
+    }
+    @PreAuthorize("@ss.hasPermi('qw:qwPushCount:tokenListDept')")
+    @GetMapping("/tokenListDept")
+    public TableDataInfo tokenListDept(FastGptPushTokenDeptTotalParam pushTokenInfo) {
+        List<FastGptPushTokenDeptTotalVO> list = qwPushCountService.selectFastGptPushTokenTotalDeptList(pushTokenInfo);
+
+
+        // 计算总和
+        FastGptPushTokenDeptTotalVO sumTotal = new FastGptPushTokenDeptTotalVO();
+        sumTotal.setCompanyName("合计"); // 假设有一个字段用于显示“合计”标签,具体字段名根据实际情况替换
+        Long sum = list.stream().mapToLong(FastGptPushTokenDeptTotalVO::getCount).sum(); // 假设有一个数字字段需要求和,具体字段名根据实际情况替换
+        sumTotal.setCount(sum); // 设置合计值,具体字段名根据实际情况替换
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
         list.add(sumTotal); // 将合计行添加到列表末尾
 
         // 构造返回结果

+ 96 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyAiWorkflowController.java

@@ -0,0 +1,96 @@
+package com.fs.company.controller.company;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.utils.ServletUtils;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import com.fs.his.domain.FsAiWorkflow;
+import com.fs.his.domain.FsAiWorkflowNode;
+import com.fs.his.service.IFsAiWorkflowService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 企业AI工作流Controller
+ *
+ * @author fs
+ * @date 2026-01-09
+ */
+@RestController
+@RequestMapping("/company/aiWorkflow")
+public class CompanyAiWorkflowController extends BaseController {
+
+    @Autowired
+    private IFsAiWorkflowService fsAiWorkflowService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    /**
+     * 分页查询AI工作流列表
+     */
+    @GetMapping("/list")
+    public TableDataInfo list(FsAiWorkflow fsAiWorkflow) {
+        startPage();
+        List<FsAiWorkflow> list = fsAiWorkflowService.selectFsAiWorkflowList(fsAiWorkflow);
+        return getDataTable(list);
+    }
+
+    /**
+     * 根据企业用户ID获取工作流的关键节点
+     * 节点类型: start(开始), end(结束), ai_chat(AI对话)
+     */
+    @GetMapping("/nodes/{companyUserId}")
+    public R getWorkflowNodes(@PathVariable("companyUserId") Long companyUserId) {
+        // 定义要查询的节点类型
+        List<String> nodeTypes = Arrays.asList("start", "end", "ai_chat");
+        List<FsAiWorkflowNode> nodes = fsAiWorkflowService.selectWorkflowNodesByCompanyUserId(companyUserId, nodeTypes);
+        return R.ok().put("data", nodes);
+    }
+
+    /**
+     * 获取当前登录企业用户的工作流关键节点
+     * 节点类型: start(开始), end(结束), ai_chat(AI对话)
+     */
+    @GetMapping("/myNodes")
+    public R getMyWorkflowNodes() {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        if (loginUser == null || loginUser.getUser() == null) {
+            return R.error("用户信息错误");
+        }
+        Long companyUserId = loginUser.getUser().getUserId();
+        List<String> nodeTypes = Arrays.asList("start", "end", "ai_chat");
+        List<FsAiWorkflowNode> nodes = fsAiWorkflowService.selectWorkflowNodesByCompanyUserId(companyUserId, nodeTypes);
+        return R.ok().put("data", nodes);
+    }
+
+    /**
+     * 更新节点的语音URL
+     * @param nodeId 节点ID
+     * @param voiceUrl 语音URL
+     */
+    @PutMapping("/node/voiceUrl")
+    public R updateNodeVoiceUrl(@RequestParam("nodeId") Long nodeId,
+                                @RequestParam("voiceUrl") String voiceUrl) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        if (loginUser == null || loginUser.getUser() == null) {
+            return R.error("用户信息错误");
+        }
+        Long companyUserId = loginUser.getUser().getUserId();
+
+        int result = fsAiWorkflowService.updateNodeVoiceUrl(nodeId, voiceUrl, companyUserId);
+        if (result == -1) {
+            return R.error("节点不存在");
+        } else if (result == -2) {
+            return R.error("无权限修改该节点");
+        } else if (result > 0) {
+            return R.ok("更新成功");
+        }
+        return R.error("更新失败");
+    }
+}

+ 9 - 1
fs-company/src/main/java/com/fs/company/controller/company/CompanyMoneyLogsController.java

@@ -126,7 +126,15 @@ public class CompanyMoneyLogsController extends BaseController
             task.setTaskType(7);
             task.setStatus(0);
             task.setStartTime(new Date());
-            task.setRemark("药品订单导出");
+            String remark = "导出订单明细";
+            if (param.getOrderType() != null) {
+                if (param.getOrderType() == 0) {
+                    remark = "导出商城订单明细";
+                } else if (param.getOrderType() == 1) {
+                    remark = "导出直播订单明细";
+                }
+            }
+            task.setRemark(remark);
             task.setSysType(2);
             task.setCompanyUserId(userId);
             exportTaskService.insertFsExportTask(task);

+ 39 - 2
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java

@@ -135,6 +135,7 @@ public class CompanyUserController extends BaseController {
     /**
      * 获取用户列表
      */
+    @PreAuthorize("@ss.hasPermi('company:user:deptList')")
     @GetMapping("/deptList")
     public TableDataInfo deptList(CompanyUser user)
     {
@@ -160,7 +161,6 @@ public class CompanyUserController extends BaseController {
         return getDataTable(list);
     }
 
-
     @GetMapping("/getUserList")
     public R getUserList()
     {
@@ -215,6 +215,43 @@ public class CompanyUserController extends BaseController {
         return getDataTable(list);
     }
 
+    @GetMapping("/myQwList")
+    public TableDataInfo myQwList(CompanyUserQwParam user) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        user.setCompanyId(loginUser.getCompany().getCompanyId());
+        user.setUserId(loginUser.getUser().getUserId());
+        List<CompanyUserQwListVO> list = companyUserService.selectCompanyUserQwListVO(user);
+        if (!list.isEmpty()){
+            String json = configService.selectConfigByKey("course.config");
+            CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+            Long sendDelayTime = config.getSendDelayTime();
+            List<CompletableFuture<Void>> futures = new ArrayList<>();
+            for (CompanyUserQwListVO companyUserQwListVO : list) {
+                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                    CompanyUserDelayTime companyUserDelayTime = companyUserDelayTimeService.selectCompanyUserDelayTimeByCompanyUser(companyUserQwListVO.getCompanyId(), companyUserQwListVO.getUserId());
+                    if (ObjectUtil.isEmpty(companyUserDelayTime)) {
+                        companyUserQwListVO.setSendDelayTime(sendDelayTime);
+                    } else {
+                        companyUserQwListVO.setSendDelayTime(companyUserDelayTime.getSendDelayTime());
+                    }
+                    //是否绑定
+                    if(QwStatusEnum.BOUND.getCode() == companyUserQwListVO.getQwStatus()){
+                        if(!StringUtil.strIsNullOrEmpty(companyUserQwListVO.getQwUserId())){
+                            Long[] ids = Arrays.stream(companyUserQwListVO.getQwUserId().split(","))
+                                    .map(Long::parseLong)
+                                    .toArray(Long[]::new);
+                            List<QwUserVO> qwUserVOS = qwUserService.selectQwUserVOByIds(ids);
+                            companyUserQwListVO.setQwUsers(qwUserVOS);
+                        }
+                    }
+                });
+                futures.add(future);
+            }
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+        }
+        return getDataTable(list);
+    }
+
     @Log(title = "用户管理导出", businessType = BusinessType.EXPORT)
     @PreAuthorize("@ss.hasPermi('company:user:export')")
     @GetMapping("/export")
@@ -414,7 +451,7 @@ public class CompanyUserController extends BaseController {
     /**
      * 重置密码
      */
-    @PreAuthorize("@ss.hasPermi('company:user:edit')")
+    @PreAuthorize("@ss.hasPermi('company:user:resetPwd')")
     @Log(title = "用户管理", businessType = BusinessType.UPDATE)
     @PutMapping("/resetPwd")
     public AjaxResult resetPwd(@RequestBody CompanyUser user)

+ 7 - 0
fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java

@@ -26,6 +26,7 @@ import com.fs.his.utils.PhoneUtil;
 import com.fs.his.vo.OptionsVO;
 import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
@@ -56,6 +57,8 @@ public class FsCourseRedPacketLogController extends BaseController
 
     @Autowired
     private IFsUserCoursePeriodService fsUserCoursePeriodService;
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
     /**
      * 查询短链课程看课记录列表
      */
@@ -76,6 +79,10 @@ public class FsCourseRedPacketLogController extends BaseController
             fsCourseRedPacketLog.setCompanyId( loginUser.getCompany().getCompanyId());
         }
 
+        if("济南联志健康".equals(signProjectName)){
+            fsCourseRedPacketLog.setCompanyId( loginUser.getCompany().getCompanyId());
+        }
+
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
             if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){

+ 11 - 3
fs-company/src/main/java/com/fs/company/controller/course/FsCourseWatchLogController.java

@@ -104,11 +104,19 @@ public class FsCourseWatchLogController extends BaseController
 
 
         List<Long> combinedList = new ArrayList<>();
-        //本部门
-        Long deptId = loginUser.getUser().getDeptId();
-        if (deptId!=null){
+        Long deptId;
+
+        if(param.getDeptId() != null){
+            deptId = param.getDeptId();
             combinedList.add(deptId);
+        }else{
+            //本部门
+            deptId = loginUser.getUser().getDeptId();
+            if (deptId!=null){
+                combinedList.add(deptId);
+            }
         }
+
         //本部门的下级部门
         List<Long> deptList = companyDeptService.selectCompanyDeptByParentId(deptId);
         if (!deptList.isEmpty()){

+ 45 - 7
fs-company/src/main/java/com/fs/company/controller/live/OrderController.java

@@ -3,10 +3,13 @@ package com.fs.company.controller.live;
 import cn.hutool.core.bean.BeanUtil;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.common.annotation.Log;
+import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.core.page.PageDomain;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.TableSupport;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ServletUtils;
@@ -63,15 +66,44 @@ public class OrderController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(MergedOrderQueryParam param)
     {
-        if(param.getOrderTypeFilter() == null || param.getOrderTypeFilter().equals("2")){
-            return getDataTable(new ArrayList<>());
+        // 从请求参数中获取分页信息
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        // 设置分页参数到 param
+        if (pageNum == null || pageNum < 1) {
+            pageNum = 1;
+            param.setPageNum(1);
+        } else {
+            param.setPageNum(pageNum);
+        }
+        if (pageSize == null || pageSize < 1) {
+            pageSize = 10;
+            param.setPageSize(10);
+        } else {
+            param.setPageSize(pageSize);
         }
+        
+        // 在外面计算 offset,然后放在请求参数中
+        Integer offset = (pageNum - 1) * pageSize;
+        param.setOffset(offset);
+        
+        // 设置公司ID和用户ID
         CompanyUser user = SecurityUtils.getLoginUser().getUser();
         param.setCompanyId(user.getCompanyId());
-       if (user.getUserType().equals("01")){
-           param.setUserId(user.getUserId());
-       }
-        startPage();
+        if (user.getUserType().equals("01")){
+            param.setUserId(user.getUserId());
+        }
+        
+        // 如果 OrderTypeFilter 为空,只查询销售和直播订单,不查询商城订单
+        // 通过设置一个特殊值来实现,或者修改查询逻辑
+        // 这里我们通过修改查询逻辑来实现:如果 orderTypeFilter 为 null,在 SQL 中排除商城订单
+        
+        // 先查询总数(不分页)
+        long total = mergedOrderService.countMergedOrderList(param);
+        
+        // 查询分页数据
         List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
         for (MergedOrderVO vo : list) {
             vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
@@ -80,7 +112,13 @@ public class OrderController extends BaseController
             vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
             vo.setCost(BigDecimal.ZERO);
         }
-        return getDataTable(list);
+
+        TableDataInfo dataTable = new TableDataInfo();
+        dataTable.setCode(HttpStatus.SUCCESS);
+        dataTable.setMsg("查询成功");
+        dataTable.setRows(list);
+        dataTable.setTotal(total);
+        return dataTable;
     }
 
     /**

+ 11 - 5
fs-company/src/main/java/com/fs/company/controller/store/FsUserAddressController.java

@@ -11,6 +11,7 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.his.domain.FsUserAddress;
 import com.fs.his.dto.AddressInfoDTO;
 import com.fs.his.service.IFsUserAddressService;
@@ -33,6 +34,8 @@ public class FsUserAddressController extends BaseController
     @Autowired
     private IFsUserAddressService fsUserAddressService;
 
+    @Autowired
+    private CloudHostProper cloudHostProper;
     /**
      * 查询用户地址列表
      */
@@ -43,11 +46,14 @@ public class FsUserAddressController extends BaseController
         startPage();
         List<FsUserAddress> list = fsUserAddressService.selectFsUserAddressList(fsUserAddress);
         for (FsUserAddress userAddress : list) {
-            if (userAddress.getPhone()!=null){
-                userAddress.setPhone(userAddress.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
-            }
-            if (userAddress.getDetail()!=null&&userAddress.getDetail()!=""){
-                userAddress.setDetail(userAddress.getDetail().substring(0, userAddress.getDetail().length() / 2) + "********");
+            //郑多燕不需要加密
+            if(!"广州郑多燕".equals(cloudHostProper.getCompanyName())){
+                if (userAddress.getPhone()!=null){
+                    userAddress.setPhone(userAddress.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                }
+                if (userAddress.getDetail()!=null&&userAddress.getDetail()!=""){
+                    userAddress.setDetail(userAddress.getDetail().substring(0, userAddress.getDetail().length() / 2) + "********");
+                }
             }
         }
         return getDataTable(list);

+ 21 - 0
fs-company/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java

@@ -15,6 +15,9 @@ import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.crm.domain.CrmCustomer;
 import com.fs.crm.service.ICrmCustomerService;
 import com.fs.framework.security.LoginUser;
@@ -46,6 +49,7 @@ import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 订单Controller
@@ -80,6 +84,10 @@ public class FsStoreOrderScrmController extends BaseController
     private IFsStoreOrderAuditLogScrmService orderAuditLogService;
     @Autowired
     private ISysConfigService configService;
+    @Autowired
+    private ICompanyUserService companyUserService;
+    @Autowired
+    private CloudHostProper cloudHostProper;
 
     /**
      * 查询订单列表
@@ -90,6 +98,19 @@ public class FsStoreOrderScrmController extends BaseController
     {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId(loginUser.getCompany().getCompanyId());
+
+        // 郑多燕需求
+        if("广州郑多燕".equals(cloudHostProper.getCompanyName())){
+            // 根据当前销售所属的数据权限过滤订单;
+            List<CompanyUser> companyUsers = companyUserService.getDataScopeCompanyUser(loginUser.getUser().getUserId());
+            if(!companyUsers.isEmpty()){
+                List<Long> companyUserIds = companyUsers.stream().map(CompanyUser::getUserId).collect(Collectors.toList());
+                param.setCompanyUserIds(companyUserIds);
+            } else {
+                // 表示数据权限是本人
+                param.setCompanyUserId(loginUser.getUser().getUserId());
+            }
+        }
         startPage();
         if(!StringUtils.isEmpty(param.getCreateTimeRange())){
             param.setCreateTimeList(param.getCreateTimeRange().split("--"));

+ 12 - 3
fs-company/src/main/java/com/fs/user/FsUserAdminController.java

@@ -36,6 +36,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.Date;
+import java.util.List;
 
 @Api(tags = "会员管理接口")
 @RestController
@@ -47,9 +48,6 @@ public class FsUserAdminController extends BaseController {
     @Autowired
     private IFsUserService fsUserService;
 
-    @Autowired
-    private ICompanyUserCacheService companyUserCacheService;
-
     @Autowired
     private TokenService tokenService;
 
@@ -65,6 +63,7 @@ public class FsUserAdminController extends BaseController {
     @Autowired
     private IFsUserCompanyUserService fsUserCompanyUserService;
 
+
     @PreAuthorize("@ss.hasPermi('user:fsUser:list')")
     @PostMapping("/list")
     @ApiOperation("会员列表(与移动端使用的相同查询)")
@@ -192,4 +191,14 @@ public class FsUserAdminController extends BaseController {
         return openIMService.batchSendCourse(batchSendCourseDTO);
     }
 
+    @PreAuthorize("@ss.hasPermi('his:user:unbind')")
+    @Log(title = "批量解绑会员(删除销售和会员的关系)", businessType = BusinessType.DELETE)
+    @DeleteMapping("/batchUnbind")
+    public AjaxResult batchUnbind(@RequestBody List<Long> userIds){
+        if(!userIds.isEmpty()){
+            return toAjax(fsUserCompanyUserService.batchUnbind(userIds));
+        } else {
+            return error("请先选择会员");
+        }
+    }
 }

+ 8 - 1
fs-live-app/src/main/resources/application.yml

@@ -2,8 +2,15 @@
 server:
   # 服务器的HTTP端口,默认为
   port: 7114
+  tomcat:
+    max-connections: 1000000
+    max-threads: 32
+    connection-timeout: 600000
+    accept-count: 10000
+  compression:
+    enabled: true
 
 # Spring配置
 spring:
   profiles:
-    active: dev-test
+    active: druid-bjzm-test

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

@@ -2286,11 +2286,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 } else {
                     log.info("完课消息-客户信息有误,不生成完课消息: {}", finishLog.getQwExternalContactId());
                 }
-                try {
-                    fsUserCompanyBindService.finish(externalContact.getFsUserId(), externalContact.getQwUserId(), externalContact.getCompanyUserId(), finishLog);
-                }catch (Exception e){
-                    log.error("更新重粉看课状态失败",e);
-                }
+//                try {
+//                    fsUserCompanyBindService.finish(externalContact.getFsUserId(), externalContact.getQwUserId(), externalContact.getCompanyUserId(), finishLog);
+//                }catch (Exception e){
+//                    log.error("更新重粉看课状态失败",e);
+//                }
             } catch (Exception e) {
                 log.error("处理完课记录失败: {}", finishLog.getLogId(), e);
             }

+ 43 - 11
fs-service/src/main/java/com/fs/company/mapper/CompanyMoneyLogsMapper.java

@@ -92,10 +92,15 @@ public interface CompanyMoneyLogsMapper
 
 
     @Select({"<script> " +
-            "select l.*,c.company_name ,o.order_code,o.user_phone,p.bank_transaction_id,p.pay_type_code,p.pay_code " +
+            "select l.*,c.company_name, " +
+            "CASE WHEN l.type = 0 THEN o.order_code WHEN l.type = 1 THEN lo.order_code ELSE NULL END as order_code, " +
+            "CASE WHEN l.type = 0 THEN o.user_name WHEN l.type = 1 THEN lo.user_name ELSE NULL END as real_name, " +
+            "CASE WHEN l.type = 0 THEN o.user_phone WHEN l.type = 1 THEN lo.user_phone ELSE NULL END as user_phone, " +
+            "p.bank_transaction_id,p.pay_type_code,p.pay_code " +
             "from company_money_logs l left join company c on c.company_id=l.company_id   " +
-            "left join fs_store_order o on o.order_id=l.business_id " +
-            "left join fs_store_payment p on (o.order_code=p.business_code and p.`status`=1) "+
+            "left join fs_store_order_scrm o on (o.id=CAST(l.business_id AS UNSIGNED) and l.type=0) " +
+            "left join live_order lo on (lo.order_id=CAST(l.business_id AS UNSIGNED) and l.type=1) " +
+            "left join fs_store_payment_scrm p on (o.order_code=p.business_code and p.`status`=1 and l.type=0) "+
             "where  (l.logs_type=3 || l.logs_type=4 || l.logs_type=5 || l.logs_type=6 || l.logs_type=13 || l.logs_type=14)  " +
             "<if test = 'maps.companyId != null  '> " +
             "and l.company_id = #{maps.companyId}" +
@@ -106,8 +111,11 @@ public interface CompanyMoneyLogsMapper
             "<if test = 'maps.businessId != null  '> " +
             "and l.business_id = #{maps.businessId}" +
             "</if>" +
+            "<if test = 'maps.type != null  '> " +
+            "and l.type = #{maps.type}" +
+            "</if>" +
             "<if test = 'maps.orderCode != null and maps.orderCode != \"\"  '> " +
-            "and o.order_code = #{maps.orderCode}" +
+            "and ((l.type=0 and o.order_code = #{maps.orderCode}) or (l.type=1 and lo.order_code = #{maps.orderCode}))" +
             "</if>" +
             "<if test = 'maps.tradeCode != null and maps.tradeCode != \"\"  '> " +
             "and p.bank_transaction_id = #{maps.tradeCode}" +
@@ -116,7 +124,7 @@ public interface CompanyMoneyLogsMapper
             "and p.pay_code = #{maps.payCode}" +
             "</if>" +
             "<if test = 'maps.userPhone != null and maps.userPhone != \"\"  '> " +
-            "and o.user_phone = #{maps.userPhone}" +
+            "and ((l.type=0 and o.user_phone = #{maps.userPhone}) or (l.type=1 and lo.user_phone = #{maps.userPhone}))" +
             "</if>" +
             "<if test = 'maps.createTimeList != null    '> " +
             " AND date_format(l.create_time,'%y%m%d') &gt;= date_format(#{maps.createTimeList[0]},'%y%m%d') " +
@@ -234,15 +242,36 @@ public interface CompanyMoneyLogsMapper
 //            "order by l.logs_id desc" +
 //            "</script>"})
     @Select({"<script> " +
-            "select l.*,o.*,c.company_name,cd.dept_name,cu.nick_name as company_user_name  FROM company_money_logs l " +
-            " LEFT JOIN fs_store_order_scrm o ON o.id=l.business_id and o.company_id =l.company_id  " +
-            " LEFT JOIN company c ON c.company_id= l.company_id " +
-            " LEFT JOIN company_user cu ON cu.user_id=o.company_user_id " +
-            " LEFT JOIN company_dept cd ON cd.dept_id=cu.dept_id   " +
-            " where (l.logs_type=3||l.logs_type=4||l.logs_type=5||l.logs_type=6 ||  l.logs_type=13 ||  l.logs_type=14)" +
+            "select l.*,c.company_name,cd.dept_name,cu.nick_name as company_user_name, " +
+            "CASE WHEN l.type = 0 THEN o.order_code WHEN l.type = 1 THEN lo.order_code ELSE NULL END as order_code, " +
+            "CASE WHEN l.type = 0 THEN o.real_name WHEN l.type = 1 THEN lo.user_name ELSE NULL END as user_name, " +
+            "CASE WHEN l.type = 0 THEN o.user_phone WHEN l.type = 1 THEN lo.user_phone ELSE NULL END as user_phone, " +
+            "CASE WHEN l.type = 0 THEN o.user_address WHEN l.type = 1 THEN lo.user_address ELSE NULL END as user_address, " +
+            "CASE WHEN l.type = 0 THEN o.total_price WHEN l.type = 1 THEN lo.total_price ELSE NULL END as total_price, " +
+            "CASE WHEN l.type = 0 THEN o.pay_money WHEN l.type = 1 THEN lo.pay_money ELSE NULL END as pay_money, " +
+            "CASE WHEN l.type = 0 THEN o.pay_delivery WHEN l.type = 1 THEN lo.pay_delivery ELSE NULL END as pay_delivery, " +
+            "CASE WHEN l.type = 0 THEN CAST(o.status AS CHAR) WHEN l.type = 1 THEN CAST(lo.status AS CHAR) ELSE NULL END as status, " +
+            "CASE WHEN l.type = 0 THEN o.delivery_sn WHEN l.type = 1 THEN lo.delivery_sn ELSE NULL END as delivery_sn, " +
+            "CASE WHEN l.type = 0 THEN CAST(o.delivery_pay_status AS CHAR) WHEN l.type = 1 THEN CAST(lo.delivery_pay_status AS CHAR) ELSE NULL END as delivery_pay_status, " +
+            "CASE WHEN l.type = 0 THEN o.delivery_time WHEN l.type = 1 THEN lo.delivery_time ELSE NULL END as delivery_time, " +
+            "CASE WHEN l.type = 0 THEN o.delivery_pay_time WHEN l.type = 1 THEN lo.delivery_pay_time ELSE NULL END as delivery_pay_time, " +
+            "CASE WHEN l.type = 0 THEN null WHEN l.type = 1 THEN NULL ELSE NULL END as package_name, " +
+            "CASE WHEN l.type = 0 THEN null WHEN l.type = 1 THEN NULL ELSE NULL END as package_second_name, " +
+            "CASE WHEN l.type = 0 THEN null WHEN l.type = 1 THEN lo.pay_type ELSE NULL END as pay_type " +
+            "FROM company_money_logs l " +
+            "LEFT JOIN fs_store_order_scrm o ON (o.id=CAST(l.business_id AS UNSIGNED) and o.company_id=l.company_id and l.type=0) " +
+            "LEFT JOIN live_order lo ON (lo.order_id=CAST(l.business_id AS UNSIGNED) and lo.company_id=l.company_id and l.type=1) " +
+            "LEFT JOIN company c ON c.company_id= l.company_id " +
+            "LEFT JOIN company_user cu ON (cu.user_id=CASE WHEN l.type=0 THEN o.company_user_id WHEN l.type=1 THEN lo.company_user_id ELSE NULL END) " +
+            "LEFT JOIN company_dept cd ON cd.dept_id=cu.dept_id   " +
+            "LEFT JOIN fs_store_payment_scrm p ON (p.business_code=o.order_code and p.status=1 and l.type=0) " +
+            "where (l.logs_type=3||l.logs_type=4||l.logs_type=5||l.logs_type=6 ||  l.logs_type=13 ||  l.logs_type=14)" +
             "<if test = 'maps.companyId != null  '> " +
             "and l.company_id = #{maps.companyId}" +
             "</if>" +
+            "<if test = 'maps.orderType != null  '> " +
+            "and l.type = #{maps.orderType}" +
+            "</if>" +
             "<if test = 'maps.createTimeList != null    '> " +
             " AND date_format(l.create_time,'%y%m%d') &gt;= date_format(#{maps.createTimeList[0]},'%y%m%d') " +
             " AND date_format(l.create_time,'%y%m%d') &lt;= date_format(#{maps.createTimeList[1]},'%y%m%d') " +
@@ -335,6 +364,9 @@ public interface CompanyMoneyLogsMapper
             "<if test = 'maps.companyId != null  '> " +
             "and l.company_id = #{maps.companyId}" +
             "</if>" +
+            "<if test = 'maps.orderType != null  '> " +
+            "and l.type = #{maps.orderType}" +
+            "</if>" +
             "<if test = 'maps.createTimeList != null    '> " +
             " AND date_format(l.create_time,'%y%m%d') &gt;= date_format(#{maps.createTimeList[0]},'%y%m%d') " +
             " AND date_format(l.create_time,'%y%m%d') &lt;= date_format(#{maps.createTimeList[1]},'%y%m%d') " +

+ 9 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java

@@ -264,4 +264,13 @@ public interface ICompanyUserService {
      * @return 绑定的用户列表
      */
     List<com.fs.hisStore.domain.FsUserScrm> selectBoundFsUsersByCompanyUserId(Long companyUserId);
+
+
+    /**
+     * 根据当前销售所属的数据权限过滤订单;
+     * @auth Caoliqin
+     * @param companyUserId
+     * @return
+     */
+    List<CompanyUser> getDataScopeCompanyUser(Long companyUserId);
 }

+ 9 - 1
fs-service/src/main/java/com/fs/company/service/impl/CompanyMoneyLogsServiceImpl.java

@@ -238,8 +238,16 @@ public class CompanyMoneyLogsServiceImpl implements ICompanyMoneyLogsService
                     }
                 }
             }
+            String fileName = "订单明细";
+            if (param.getOrderType() != null) {
+                if (param.getOrderType() == 0) {
+                    fileName = "商城订单明细";
+                } else if (param.getOrderType() == 1) {
+                    fileName = "直播订单明细";
+                }
+            }
             ExcelUtil<CompanyMoneyLogsExport1VO> util = new ExcelUtil<CompanyMoneyLogsExport1VO>(CompanyMoneyLogsExport1VO.class);
-            AjaxResult result =  util.exportExcel(list, "商城订单");
+            AjaxResult result =  util.exportExcel(list, fileName);
             FsExportTask task=fsExportTaskMapper.selectFsExportTaskByTaskId(param.getTaskId());
             task.setFinishTime(new Date());
             task.setStatus(1);

+ 23 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java

@@ -128,6 +128,9 @@ public class CompanyUserServiceImpl implements ICompanyUserService
 //    @Autowired
 //    private ICompanyUserRoleService userRoleService;
 
+    @Autowired
+    private CompanyRoleMapper companyRoleMapper;
+
 
     /**
      * 查询物业公司管理员信息
@@ -1096,4 +1099,24 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     public List<com.fs.hisStore.domain.FsUserScrm> selectBoundFsUsersByCompanyUserId(Long companyUserId) {
         return companyUserMapper.selectBoundFsUsersByCompanyUserId(companyUserId);
     }
+
+    @Override
+    public List<CompanyUser> getDataScopeCompanyUser(Long companyUserId) {
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(companyUserId);
+        List<CompanyRole> companyRoles = companyRoleMapper.selectRolePermissionByUserId(companyUserId);
+        List<CompanyUser> companyUsers;
+        // 是否是管理员或者包含 1:全部数据权限
+        if (companyUser.isAdmin() || companyRoles.stream().anyMatch(r -> "1".equals(r.getDataScope()))) {
+            companyUsers = companyUserService.getCompanyUserListByCompanyIdAndDeptId(companyUser.getCompanyId(), null);
+        }
+        // 是否包含 3:本部门数据权限 4:本部门及以下数据权限
+        else if (companyRoles.stream().anyMatch(r -> "3".equals(r.getDataScope()) || "4".equals(r.getDataScope()))) {
+            companyUsers = companyUserService.getCompanyUserListByCompanyIdAndDeptId(companyUser.getCompanyId(), companyUser.getDeptId());
+        }
+        // 默认空,判断是否包含 5:仅可查看本人
+        else {
+            companyUsers = new ArrayList<>();
+        }
+        return companyUsers;
+    }
 }

+ 1 - 1
fs-service/src/main/java/com/fs/company/vo/CompanyMoneyLogsExport1VO.java

@@ -71,7 +71,7 @@ public class CompanyMoneyLogsExport1VO implements Serializable
     @Excel(name = "物流代收")
     private BigDecimal payDelivery;
 
-    @Excel(name = "订单状态",dictType = "sys_order_status")
+    @Excel(name = "订单状态", readConverterExp = "-1=申请退款,-2=退款成功,-3=已取消,0=待支付,1=待发货,2=待收货,3=已完成,4=待评价")
     private String status;
 
     @Excel(name = "快递单号")

+ 1 - 1
fs-service/src/main/java/com/fs/company/vo/CompanyMoneyLogsVO.java

@@ -68,5 +68,5 @@ public class CompanyMoneyLogsVO implements Serializable
 
     //支付类型
     private String payTypeCode;
-
+    private int type;
 }

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

@@ -0,0 +1,121 @@
+package com.fs.course.config;
+
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.Cursor;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.ScanOptions;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+@Component
+public class RedisKeyScanner {
+
+    private final StringRedisTemplate redisTemplate;
+
+    public RedisKeyScanner(StringRedisTemplate redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    public Set<String> scan(String pattern) {
+        Set<String> keys = new HashSet<>();
+
+        // 这里指定 match + count
+        ScanOptions options = ScanOptions.scanOptions()
+                .match(pattern)
+                .count(1000)
+                .build();
+
+        // 使用 RedisConnection 提供的 scan
+        redisTemplate.execute((RedisConnection connection) -> {
+            try (Cursor<byte[]> cursor = connection.scan(options)) {
+                RedisSerializer<String> keySerializer = redisTemplate.getStringSerializer();
+                cursor.forEachRemaining(item -> keys.add(keySerializer.deserialize(item)));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+            return null;
+        });
+
+        return keys;
+    }
+
+    public Set<String> scanKeys(String pattern) {
+        Set<String> keys = new HashSet<>();
+        ScanOptions options = ScanOptions.scanOptions().match(pattern).count(1000).build();
+
+        redisTemplate.execute((RedisConnection connection) -> {
+            try (Cursor<byte[]> cursor = connection.scan(options)) {
+                while (cursor.hasNext()) {
+                    byte[] rawKey = cursor.next();
+                    String key = (String) redisTemplate.getKeySerializer().deserialize(rawKey);
+                    keys.add(key);
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Error during scan", e);
+            }
+            return null;
+        });
+
+        return keys;
+    }
+
+    /**
+     * 使用 SCAN 替代 KEYS,避免阻塞 Redis
+     *
+     * @param pattern key 匹配模式,例如 "h5user:watch:duration:*"
+     * @return 匹配到的 key 集合
+     */
+    public Collection<String> scanPatternKeys(final String pattern) {
+        Set<String> keys = new HashSet<>();
+
+        // 每次扫描的数量,可根据实际情况调整(越大速度越快,但对 CPU 影响越大)
+        ScanOptions options = ScanOptions.scanOptions()
+                .match(pattern)
+                .count(1000)
+                .build();
+
+        // 使用 redisTemplate 的 execute 保证连接获取 & 释放正确
+        redisTemplate.execute((RedisConnection connection) -> {
+            try (Cursor<byte[]> cursor = connection.scan(options)) {
+                RedisSerializer<String> keySerializer = redisTemplate.getStringSerializer();
+                while (cursor.hasNext()) {
+                    keys.add(keySerializer.deserialize(cursor.next()));
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Error during redis SCAN", e);
+            }
+            return null;
+        });
+
+        return keys;
+    }
+
+    public Set<String> scanMatchKey(String pattern) {
+        return (Set<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
+            Set<String> keys = new HashSet<>();
+            // 使用 try-with-resources 确保 Cursor 无论是否异常都会被关闭
+            try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions()
+                    .match(pattern)
+                    .count(1000) // 每次扫描的批量大小,可根据实际情况调整
+                    .build())) {
+
+                while (cursor.hasNext()) {
+                    keys.add(new String(cursor.next(), StandardCharsets.UTF_8)); // 显式指定字符集,避免依赖默认值
+                }
+            } catch (IOException e) {
+                // 在实际项目中,应该使用更合适的日志记录和异常处理方式
+                // 例如抛出自定义异常或记录错误日志
+                throw new RuntimeException("Error during SCAN operation", e);
+            }
+            return keys;
+        });
+    }
+
+}

+ 14 - 0
fs-service/src/main/java/com/fs/course/dto/FsCourseCategoryImportDTO.java

@@ -0,0 +1,14 @@
+package com.fs.course.dto;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class FsCourseCategoryImportDTO {
+
+    @Excel(name = "父分类")
+    private String rootCate;
+
+    @Excel(name = "子分类")
+    private String subCate;
+}

+ 61 - 0
fs-service/src/main/java/com/fs/course/dto/RedisKeyInfo.java

@@ -0,0 +1,61 @@
+package com.fs.course.dto;
+
+import com.fs.course.enums.RedisKeyType;
+import lombok.Data;
+
+@Data
+public class RedisKeyInfo {
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 视频ID
+     */
+    private Long videoId;
+
+    /**
+     * 企业用户ID
+     */
+    private Long companyUserId;
+
+    /**
+     * Redis key类型
+     */
+    private RedisKeyType keyType;
+
+    /**
+     * 原始key
+     */
+    private String originalKey;
+
+    /**
+     * 验证信息是否完整
+     */
+    public boolean isValid() {
+        return userId != null && videoId != null && companyUserId != null;
+    }
+
+    /**
+     * 构建时长key
+     */
+    public String buildDurationKey() {
+        return String.format("h5wxuser:watch:duration:%d:%d:%d",
+                userId, videoId, companyUserId);
+    }
+
+    /**
+     * 构建心跳key
+     */
+    public String buildHeartbeatKey() {
+        return String.format("h5wxuser:watch:heartbeat:%d:%d:%d",
+                userId, videoId, companyUserId);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("userId=%d, videoId=%d, companyUserId=%d",
+                userId, videoId, companyUserId);
+    }
+}

+ 26 - 0
fs-service/src/main/java/com/fs/course/enums/RedisKeyType.java

@@ -0,0 +1,26 @@
+package com.fs.course.enums;
+
+/**
+ * Redis key类型枚举
+ */
+
+public enum RedisKeyType {
+    DURATION("duration", "看课时长"),
+    HEARTBEAT("heartbeat", "心跳记录");
+
+    private final String code;
+    private final String desc;
+
+    RedisKeyType(String code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+}

+ 12 - 0
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseCategoryMapper.java

@@ -101,4 +101,16 @@ public interface FsUserCourseCategoryMapper
 
     @Select("select cate_id dict_value, cate_name dict_label  from fs_user_course_category WHERE pid = 0 and is_del=0 ")
     List<OptionsVO> selectAllFsUserCourseCategoryPidList();
+
+    /**
+     * 根据名称和上级ID查询分类
+     */
+    @Select("select * from fs_user_course_category where cate_name = #{name} and pid = #{parentId} and is_del = 0")
+    FsUserCourseCategory selectFsUserCourseCategoryByNameAndParentId(@Param("name") String name, @Param("parentId") Long parentId);
+
+    /**
+     * 获取序号
+     */
+    @Select("select ifnull(max(sort), 0) + 1 from fs_user_course_category where pid = #{parentId}")
+    Long getSortByParentId(@Param("parentId") Long parentId);
 }

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

@@ -220,6 +220,9 @@ public interface FsUserCourseMapper
             "<if test = ' maps.cateId !=null '> " +
             "and (cc.cate_id =#{maps.cateId} or cc.pid=#{maps.cateId} )" +
             "</if>" +
+            "<if test = ' maps.courseId !=null '> " +
+            "and (c.course_id =#{maps.courseId})" +
+            "</if>" +
             "<if test = ' maps.courseName!=null and maps.courseName != \"\" '> " +
             "and c.course_name like concat('%', #{maps.courseName}, '%') " +
             "</if>" +

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

@@ -1,7 +1,10 @@
 package com.fs.course.service;
 
 import java.util.List;
+import java.util.Map;
+
 import com.fs.course.domain.FsUserCourseCategory;
+import com.fs.course.dto.FsCourseCategoryImportDTO;
 import com.fs.his.vo.OptionsVO;
 
 /**
@@ -68,4 +71,9 @@ public interface IFsUserCourseCategoryService
     List<OptionsVO> selectFsUserCourseCategoryPidList(Long userId);
 
     List<OptionsVO> selectCateListByPid(Long pid);
+
+    /**
+     * 课堂分类导入
+     */
+    Map<String, Object> importData(List<FsCourseCategoryImportDTO> list, Long userId);
 }

+ 89 - 1
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseCategoryServiceImpl.java

@@ -1,8 +1,14 @@
 package com.fs.course.service.impl;
 
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.course.dto.FsCourseCategoryImportDTO;
 import com.fs.his.vo.OptionsVO;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.course.mapper.FsUserCourseCategoryMapper;
@@ -15,6 +21,7 @@ import com.fs.course.service.IFsUserCourseCategoryService;
  * @author fs
  * @date 2024-05-15
  */
+@Slf4j
 @Service
 public class FsUserCourseCategoryServiceImpl implements IFsUserCourseCategoryService
 {
@@ -118,4 +125,85 @@ public class FsUserCourseCategoryServiceImpl implements IFsUserCourseCategorySer
     public List<OptionsVO> selectCateListByPid(Long pid) {
         return fsUserCourseCategoryMapper.selectCateListByPid(pid);
     }
+
+    /**
+     * 课堂分类导入
+     */
+    @Override
+    public Map<String, Object> importData(List<FsCourseCategoryImportDTO> list, Long userId) {
+        int suCnt = 0;
+        int filCnt = 0;
+        List<FsCourseCategoryImportDTO> failedList = new ArrayList<>();
+
+        if (list == null || list.isEmpty()) {
+            throw new CustomException("导入数据不能为空");
+        }
+
+        Map<String, List<FsCourseCategoryImportDTO>> rootMap = list.stream()
+                .filter(dto -> StringUtils.isNotBlank(dto.getRootCate()))
+                .collect(Collectors.groupingBy(FsCourseCategoryImportDTO::getRootCate));
+        for (Map.Entry<String, List<FsCourseCategoryImportDTO>> entry : rootMap.entrySet()) {
+            if (StringUtils.isBlank(entry.getKey())) {
+                failedList.addAll(entry.getValue());
+                filCnt += entry.getValue().size();
+                continue;
+            }
+
+            String rootCate = entry.getKey().trim();
+            FsUserCourseCategory root = fsUserCourseCategoryMapper.selectFsUserCourseCategoryByNameAndParentId(rootCate, 0L);
+            if (Objects.isNull(root)) {
+                root = new FsUserCourseCategory();
+                root.setPid(0L);
+                root.setCateName(rootCate);
+                root.setSort(fsUserCourseCategoryMapper.getSortByParentId(0L));
+                root.setIsShow(1);
+                root.setCreateTime(new Date());
+                root.setIsDel(0);
+                root.setUserId(userId);
+                fsUserCourseCategoryMapper.insertFsUserCourseCategory(root);
+            }
+
+            Long parentId = root.getCateId();
+            long curSort = fsUserCourseCategoryMapper.getSortByParentId(root.getCateId());
+            List<FsCourseCategoryImportDTO> subList = entry.getValue();
+            for (FsCourseCategoryImportDTO child : subList) {
+                try {
+                    if (StringUtils.isBlank(child.getSubCate())) {
+                        failedList.add(child);
+                        filCnt++;
+                        continue;
+                    }
+
+                    String cateName = child.getSubCate().trim();
+                    FsUserCourseCategory sub = fsUserCourseCategoryMapper.selectFsUserCourseCategoryByNameAndParentId(cateName, parentId);
+                    if (Objects.nonNull(sub)) {
+                        failedList.add(child);
+                        filCnt++;
+                        continue;
+                    }
+
+                    sub = new FsUserCourseCategory();
+                    sub.setPid(parentId);
+                    sub.setCateName(cateName);
+                    sub.setSort(curSort++);
+                    sub.setIsShow(1);
+                    sub.setCreateTime(new Date());
+                    sub.setIsDel(0);
+                    sub.setUserId(userId);
+                    fsUserCourseCategoryMapper.insertFsUserCourseCategory(sub);
+
+                    suCnt++;
+                } catch (Exception e) {
+                    filCnt++;
+                    log.error("导入子分类失败: {}", child, e);
+                }
+            }
+        }
+
+        String message = "导入完成!成功" + suCnt + "条,失败" + filCnt + "条。";
+        Map<String, Object> resp = new HashMap<>();
+        resp.put("message", message);
+        resp.put("failList", failedList);
+        return resp;
+    }
 }

+ 56 - 0
fs-service/src/main/java/com/fs/fastGpt/param/FastGptPushTokenDeptTotalParam.java

@@ -0,0 +1,56 @@
+package com.fs.fastGpt.param;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.Map;
+
+@Data
+public class FastGptPushTokenDeptTotalParam {
+    private Long id;
+    private Integer type;
+    private Long qwUserId;
+    private Long companyId;
+    private String companyName;
+
+    private Long count;
+    private Integer status;
+
+    private String statTime;
+
+    private Long deptId;
+    /** 搜索值 */
+    private String searchValue;
+
+    /** 创建者 */
+    private String createBy;
+
+    /** 创建时间 */
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 更新者 */
+    private String updateBy;
+
+    /** 更新时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
+    /** 备注 */
+    private String remark;
+
+    /** 开始时间 */
+    @JsonIgnore
+    private String beginTime;
+    /** 结束时间 */
+    @JsonIgnore
+    private String endTime;
+    /** 请求参数 */
+    private Map<String, Object> params;
+}

+ 21 - 0
fs-service/src/main/java/com/fs/fastGpt/vo/FastGptPushTokenDeptTotalVO.java

@@ -0,0 +1,21 @@
+package com.fs.fastGpt.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class FastGptPushTokenDeptTotalVO {
+    private Long id;
+    private Integer type;
+    private Long qwUserId;
+    private Long companyId;
+    @Excel(name = "公司名称")
+    private String companyName;
+    @Excel(name = "token消耗数")
+    private Long count;
+    private Integer status;
+    @Excel(name = "生成时间")
+    private String statTime;
+    private Long deptId;
+    private String deptName;
+}

+ 3 - 0
fs-service/src/main/java/com/fs/his/domain/FsAiWorkflow.java

@@ -41,4 +41,7 @@ public class FsAiWorkflow extends BaseEntity {
 
     /** 删除标志 0正常 1删除 */
     private Integer delFlag;
+
+    /** 企业用户ID */
+    private Long companyUserId;
 }

+ 5 - 0
fs-service/src/main/java/com/fs/his/mapper/FsAiWorkflowMapper.java

@@ -15,6 +15,11 @@ public interface FsAiWorkflowMapper {
 
     List<FsAiWorkflow> selectFsAiWorkflowList(FsAiWorkflow fsAiWorkflow);
 
+    /**
+     * 根据企业用户ID查询工作流列表
+     */
+    List<FsAiWorkflow> selectFsAiWorkflowByCompanyUserId(Long companyUserId);
+
     int insertFsAiWorkflow(FsAiWorkflow fsAiWorkflow);
 
     int updateFsAiWorkflow(FsAiWorkflow fsAiWorkflow);

+ 8 - 0
fs-service/src/main/java/com/fs/his/mapper/FsAiWorkflowNodeMapper.java

@@ -1,6 +1,8 @@
 package com.fs.his.mapper;
 
 import com.fs.his.domain.FsAiWorkflowNode;
+import org.apache.ibatis.annotations.Param;
+
 import java.util.List;
 
 /**
@@ -15,6 +17,12 @@ public interface FsAiWorkflowNodeMapper {
 
     List<FsAiWorkflowNode> selectFsAiWorkflowNodeByWorkflowId(Long workflowId);
 
+    /**
+     * 根据工作流ID和节点类型列表查询节点
+     */
+    List<FsAiWorkflowNode> selectNodesByWorkflowIdAndTypes(@Param("workflowId") Long workflowId,
+                                                           @Param("nodeTypes") List<String> nodeTypes);
+
     int insertFsAiWorkflowNode(FsAiWorkflowNode node);
 
     int batchInsertFsAiWorkflowNode(List<FsAiWorkflowNode> nodes);

+ 2 - 2
fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderMapper.java

@@ -367,7 +367,7 @@ public interface FsStoreOrderMapper
 
     @Select("select so.*,st.store_name,us.nick_name,us.phone,p.patient_age,p.patient_gender,p.patient_descs,p.patient_name," +
             "d.doctor_name,fp.prescribe_code,fd.doctor_name follow_doctor_name,c.company_name,cu.user_name company_user_name,pat.relation  " +
-            "FROM fs_store_order so LEFT JOIN fs_store st ON so.store_id =st.store_id " +
+            "FROM fs_store_order_scrm so LEFT JOIN fs_store_scrm st ON so.store_id =st.store_id " +
             "LEFT JOIN fs_user us ON us.user_id=so.user_id " +
             "LEFT JOIN fs_prescribe p ON p.prescribe_id =so.prescribe_id " +
             "LEFT JOIN fs_doctor d ON p.doctor_id= d.doctor_id " +
@@ -375,7 +375,7 @@ public interface FsStoreOrderMapper
             "LEFT JOIN company c on c.company_id =so.company_id " +
             "LEFT JOIN company_user cu on cu.user_id=so.company_user_id " +
             "LEFT JOIN fs_doctor fd on so.follow_doctor_id =fd.doctor_id " +
-            "LEFT JOIN fs_patient pat ON pat.patient_id=p.patient_id  WHERE so.is_del=0  and so.order_id= #{orderId}")
+            "LEFT JOIN fs_patient pat ON pat.patient_id=p.patient_id  WHERE so.is_del=0  and so.id= #{orderId}")
     FsStoreOrderVO selectFsStoreOrderByOrderIdVO(@Param("orderId") Long orderId);
 
     @Update("update fs_store_order set status=-3 where order_id=#{orderId}")

+ 1 - 0
fs-service/src/main/java/com/fs/his/param/FsCompanyMoneyLogsExportParam.java

@@ -10,6 +10,7 @@ public class FsCompanyMoneyLogsExportParam  implements Serializable {
     private Long companyId;
     private Integer logsType;
     private Integer type;//0 全部 1明细
+    private Integer orderType;//订单类型:0=商城订单 1=直播订单 null=全部
     private Long taskId;//任务ID
     private String createTimeRange;
 

+ 18 - 0
fs-service/src/main/java/com/fs/his/service/IFsAiWorkflowService.java

@@ -1,6 +1,7 @@
 package com.fs.his.service;
 
 import com.fs.his.domain.FsAiWorkflow;
+import com.fs.his.domain.FsAiWorkflowNode;
 import com.fs.his.domain.FsAiWorkflowNodeType;
 import com.fs.his.param.FsAiWorkflowSaveParam;
 import com.fs.his.vo.FsAiWorkflowExportVO;
@@ -54,4 +55,21 @@ public interface IFsAiWorkflowService {
      * 导出工作流JSON(包含节点、连接顺序、节点类型)
      */
     FsAiWorkflowExportVO exportWorkflowJson(Long workflowId);
+
+    /**
+     * 根据企业用户ID查询工作流的指定类型节点
+     * @param companyUserId 企业用户ID
+     * @param nodeTypes 节点类型列表(如: start, end, ai_chat)
+     * @return 节点列表
+     */
+    List<FsAiWorkflowNode> selectWorkflowNodesByCompanyUserId(Long companyUserId, List<String> nodeTypes);
+
+    /**
+     * 更新节点的voiceUrl
+     * @param nodeId 节点ID
+     * @param voiceUrl 语音URL
+     * @param companyUserId 企业用户ID(用于校验归属)
+     * @return 1成功 0失败 -1节点不存在 -2无权限
+     */
+    int updateNodeVoiceUrl(Long nodeId, String voiceUrl, Long companyUserId);
 }

+ 46 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsAiWorkflowServiceImpl.java

@@ -253,4 +253,50 @@ public class FsAiWorkflowServiceImpl implements IFsAiWorkflowService {
             default: return "";
         }
     }
+
+    @Override
+    public List<FsAiWorkflowNode> selectWorkflowNodesByCompanyUserId(Long companyUserId, List<String> nodeTypes) {
+        // 根据企业用户ID查询工作流
+        List<FsAiWorkflow> workflows = fsAiWorkflowMapper.selectFsAiWorkflowByCompanyUserId(companyUserId);
+        if (workflows == null || workflows.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // 收集所有工作流的指定类型节点
+        List<FsAiWorkflowNode> result = new ArrayList<>();
+        for (FsAiWorkflow workflow : workflows) {
+            List<FsAiWorkflowNode> nodes = fsAiWorkflowNodeMapper.selectNodesByWorkflowIdAndTypes(
+                    workflow.getWorkflowId(), nodeTypes);
+            if (nodes != null) {
+                result.addAll(nodes);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public int updateNodeVoiceUrl(Long nodeId, String voiceUrl, Long companyUserId) {
+        // 1. 查询节点是否存在
+        FsAiWorkflowNode node = fsAiWorkflowNodeMapper.selectFsAiWorkflowNodeById(nodeId);
+        if (node == null) {
+            return -1; // 节点不存在
+        }
+
+        // 2. 查询节点所属的工作流
+        FsAiWorkflow workflow = fsAiWorkflowMapper.selectFsAiWorkflowById(node.getWorkflowId());
+        if (workflow == null) {
+            return -1; // 工作流不存在
+        }
+
+        // 3. 校验工作流是否属于该企业用户
+        if (workflow.getCompanyUserId() == null || !workflow.getCompanyUserId().equals(companyUserId)) {
+            return -2; // 无权限
+        }
+
+        // 4. 更新voiceUrl
+        FsAiWorkflowNode updateNode = new FsAiWorkflowNode();
+        updateNode.setNodeId(nodeId);
+        updateNode.setVoiceUrl(voiceUrl);
+        return fsAiWorkflowNodeMapper.updateFsAiWorkflowNode(updateNode);
+    }
 }

+ 11 - 10
fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderServiceImpl.java

@@ -397,7 +397,7 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
                 if (remainingIntegral < 0) {
                     throw new CustomException("积分不足,无法创建订单");
                 }
-                
+
                 //写入日志
                 FsUser userMap=new FsUser();
                 userMap.setUserId(user.getUserId());
@@ -749,7 +749,7 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
             }
 
             List<Long> goodsIds = new ArrayList<>();
-            
+
             // 解析订单商品信息,获取商品ID列表
             if (order.getItemJson().startsWith("[") && order.getItemJson().endsWith("]")) {
                 // 多个商品
@@ -862,10 +862,11 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
             fsUserIntegralLogs.setBalance(fsUser.getIntegral());
             fsUserIntegralLogs.setBusinessId(fsIntegralOrder.getOrderId().toString());
             fsUserIntegralLogs.setUserId(fsIntegralOrder.getUserId());
-            fsUserIntegralLogs.setLogType(20);
+            fsUserIntegralLogs.setLogType(FsUserIntegralLogTypeEnum.TYPE_27.getValue());//20
             fsUserIntegralLogs.setIntegral(Long.parseLong(fsIntegralOrder.getIntegral()));
             fsUserIntegralLogs.setBusinessType(2);
             fsUserIntegralLogs.setStatus(0);
+            fsUserIntegralLogs.setCreateTime(new Date());
             i = fsUserIntegralLogsMapper.insertFsUserIntegralLogs(fsUserIntegralLogs);
         }
         return i;
@@ -1105,11 +1106,11 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
             throw new ServiceException("导入数据不能为空!");
         }
         FsIntegralOrderImportResultVO result = new FsIntegralOrderImportResultVO();
-        
+
         for (int i = 0; i < list.size(); i++) {
             FsIntegralOrderExcelVO vo = list.get(i);
             int rowNum = i + 2; // Excel行号从2开始(第1行是表头)
-            
+
             try {
                 //1.必填参数
                 ExcelUtils.validateRequiredFields(vo, rowNum);
@@ -1124,11 +1125,11 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
                     result.setFailureNum(result.getFailureNum() + 1);
                     continue;
                 }
-                
+
                 FsIntegralOrder param = new FsIntegralOrder(); //修改订单的参数
                 param.setOrderCode(vo.getOrderCode());
                 param.setOrderId(order.getOrderId());
-                
+
                 if ("6".equals(vo.getStatus())) {
                     FsIntegralOrderImportResultVO.ImportResultItem item = new FsIntegralOrderImportResultVO.ImportResultItem();
                     item.setRowNum(rowNum);
@@ -1183,7 +1184,7 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
                         continue;
                     }
                 }
-                
+
                 // 处理物流字段(非必须,填了就更新)
                 if (StringUtils.isNotBlank(vo.getDeliveryCode())) {
                     param.setDeliveryCode(vo.getDeliveryCode());
@@ -1194,7 +1195,7 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
                 if (StringUtils.isNotBlank(vo.getDeliverySn())) {
                     param.setDeliverySn(vo.getDeliverySn());
                 }
-                
+
                 param.setDeliveryStatus((vo.getDeliveryStatus()==null|| vo.getDeliveryStatus().isEmpty())?null:Integer.valueOf(vo.getDeliveryStatus()));
                 param.setDeliveryType(vo.getDeliveryType().isEmpty()?null:vo.getDeliveryType());
                 param.setUpdateTime(DateUtils.getNowDate());
@@ -1216,7 +1217,7 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
                 result.setFailureNum(result.getFailureNum() + 1);
             }
         }
-        
+
         return result;
     }
 

+ 1 - 0
fs-service/src/main/java/com/fs/hisStore/config/StoreConfig.java

@@ -28,4 +28,5 @@ public class StoreConfig implements Serializable {
     private Boolean isWeChatShipping;//是否开启微信发货
     private Boolean scanCodeDiscountEnabled;//是否开启扫码立减金
     private BigDecimal scanCodeDiscountAmount;//扫码立减金额
+    private Boolean checkStock;//是否检查库存,默认关闭
 }

+ 10 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java

@@ -39,6 +39,13 @@ public interface FsStoreOrderScrmMapper
      */
     public FsStoreOrderScrm selectFsStoreOrderById(Long id);
 
+    /**
+     * 根据订单ID列表批量查询订单
+     * @param orderIds 订单ID列表
+     * @return 订单列表
+     */
+    List<FsStoreOrderScrm> selectFsStoreOrderByIds(@Param("orderIds") List<Long> orderIds);
+
     /**
      * 查询订单列表
      *
@@ -992,6 +999,9 @@ public interface FsStoreOrderScrmMapper
     @Select("select id from fs_store_order_scrm where status = 2  and DATE(pay_time)>='2023-11-1' and DATE(create_time)<='2024-2-7' and company_id is null ")
     List<Long> selectSyncExpressIds();
 
+    @Select("select id from fs_store_order_scrm where status = 2  and DATE(pay_time)>='2025-11-1' and delivery_id is not null and delivery_sn is not null")
+    List<Long> selectSyncExpressIdsNoDate();
+
 
     List<Long> selectAddTuiMoney();
 

+ 10 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStorePaymentScrmMapper.java

@@ -367,6 +367,9 @@ public interface FsStorePaymentScrmMapper
     @Select("select * from fs_store_payment_scrm where trade_no=#{tradeNo}")
     FsStorePaymentScrm selectFsStorePaymentByTradeNo(String tradeNo);
 
+    @Select("select * from fs_store_payment_scrm where status = 0 and create_time < NOW() - INTERVAL 5 MINUTE AND create_time > NOW() - INTERVAL 60 MINUTE ")
+    List<FsStorePaymentScrm> selectFsStorePaymentByAll();
+
     @Select("select * from fs_store_payment_scrm where business_type=#{type} and  business_order_id=#{business_order_id} and status=1")
     List<FsStorePaymentScrm> selectFsStorePaymentByPay(@Param("type")int type, @Param("business_order_id") Long business_order_id);
 
@@ -388,4 +391,11 @@ public interface FsStorePaymentScrmMapper
 
     @Select("select * from fs_store_payment_scrm where order_id=#{orderId} and status=0   ")
     List<FsStorePaymentScrm> selectNoPayByOrderId(Long orderId);
+
+    /**
+     * 根据交易单号列表批量查询支付记录
+     * @param bankTransactionIds 交易单号列表
+     * @return 支付记录列表
+     */
+    List<FsStorePaymentScrm> selectByBankTransactionIds(@Param("bankTransactionIds") List<String> bankTransactionIds);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductAttrValueScrmMapper.java

@@ -76,6 +76,12 @@ public interface FsStoreProductAttrValueScrmMapper
     List<FsStoreProductAttrValueScrm> selectFsStoreProductAttrValueByProductId(Long productId);
     @Select("select ifnull(stock,0) from fs_store_product_attr_value_scrm where  id=#{productAttrValueId}")
     int selectFsStoreProductStockById(Long productAttrValueId);
+    
+    /**
+     * 使用行锁查询规格库存
+     */
+    @Select("select ifnull(stock,0) from fs_store_product_attr_value_scrm where id=#{productAttrValueId} for update")
+    Integer selectProductAttrStockForUpdate(@Param("productAttrValueId") Long productAttrValueId);
 
     @Update("update fs_store_product_attr_value_scrm set stock=stock-#{num},sales=sales+#{num}" +
             " where id=#{productAttrValueId} and stock >= #{num} ")

+ 6 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java

@@ -255,6 +255,12 @@ public interface FsStoreProductScrmMapper
     @Update("update fs_store_product_scrm set stock=stock-#{num}, sales=sales+#{num}" +
             " where product_id=#{productId} and stock >= #{num}")
     int decProductAttrStock(@Param("productId")Long productId, @Param("num")Integer cartNum);
+
+    /**
+     * 使用行锁查询商品库存
+     */
+    @Select("select stock from fs_store_product_scrm where product_id=#{productId} for update")
+    Integer selectProductStockForUpdate(@Param("productId") Long productId);
     @Update("update fs_store_product_scrm set stock=stock+#{num}, sales=sales-#{num}" +
             " where product_id=#{productId}")
     int incStockDecSales( @Param("num")Long num, @Param("productId")Long productId);

+ 40 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/MergedOrderMapper.java

@@ -27,6 +27,46 @@ public interface MergedOrderMapper
      */
     List<MergedOrderVO> selectMergedOrderList(@Param("maps") MergedOrderQueryParam param);
 
+    /**
+     * 查询销售订单列表(只查询销售订单,order_type = 1)
+     *
+     * @param param 查询参数
+     * @return 销售订单列表
+     */
+    List<MergedOrderVO> selectSalesOrderList(@Param("maps") MergedOrderQueryParam param);
+
+    /**
+     * 查询商城订单列表(只查询商城订单,order_type = 2)
+     *
+     * @param param 查询参数
+     * @return 商城订单列表
+     */
+    List<MergedOrderVO> selectStoreOrderList(@Param("maps") MergedOrderQueryParam param);
+
+    /**
+     * 查询直播订单列表(只查询直播订单,order_type = 3)
+     *
+     * @param param 查询参数
+     * @return 直播订单列表
+     */
+    List<MergedOrderVO> selectLiveOrderList(@Param("maps") MergedOrderQueryParam param);
+
+    /**
+     * 从视图查询合并订单列表(全部订单,使用视图优化)
+     *
+     * @param param 查询参数
+     * @return 合并后的订单列表
+     */
+    List<MergedOrderVO> selectMergedOrderListFromView(@Param("maps") MergedOrderQueryParam param);
+
+    /**
+     * 统计合并订单总数
+     *
+     * @param param 查询参数
+     * @return 订单总数
+     */
+    long countMergedOrderList(@Param("maps") MergedOrderQueryParam param);
+    
     /**
      * 查询合并的售后列表(商城售后+直播售后)
      *

+ 3 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderParam.java

@@ -121,4 +121,7 @@ public class FsStoreOrderParam extends BaseEntity implements Serializable
 
     // 是否审核,1-是,0-否
     private Integer isAudit;
+
+    // 多个销售id
+    private List<Long> companyUserIds;
 }

+ 1 - 0
fs-service/src/main/java/com/fs/hisStore/service/IFsStorePaymentScrmService.java

@@ -97,6 +97,7 @@ public interface IFsStorePaymentScrmService
     List<FsStorePaymentScrm> selectFsStorePaymentByOrder(Long orderId);
 
     FsStorePaymentScrm selectFsStorePaymentByTradeNo(String tradeNo);
+    List<FsStorePaymentScrm> selectFsStorePaymentByAll();
 
     R sendRedPacket(WxSendRedPacketParam param);
 

+ 8 - 0
fs-service/src/main/java/com/fs/hisStore/service/IMergedOrderService.java

@@ -27,6 +27,14 @@ public interface IMergedOrderService
     List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param);
 
     List<MergedOrderVO> selectMergedOrderList(MergedOrderQueryParam param);
+    
+    /**
+     * 统计合并订单总数
+     *
+     * @param param 查询参数
+     * @return 订单总数
+     */
+    long countMergedOrderList(MergedOrderQueryParam param);
 
     /**
      * 查询合并的售后列表(商城售后+直播售后)

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

@@ -2747,6 +2747,9 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             }
         }
         ExpressInfoDTO dto = expressService.getExpressInfo(order.getOrderCode(), order.getDeliverySn(), order.getDeliveryId(), lastFourNumber);
+        if (dto == null || dto.getState() == null || dto.getStateEx() == null) {
+            return null;
+        }
         FsStoreOrderScrm map = new FsStoreOrderScrm();
         map.setDeliveryStatus(Integer.parseInt(dto.getState()));
         map.setId(order.getId());

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStorePaymentScrmServiceImpl.java

@@ -395,6 +395,11 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
         return fsStorePaymentMapper.selectFsStorePaymentByTradeNo(tradeNo);
     }
 
+    @Override
+    public List<FsStorePaymentScrm> selectFsStorePaymentByAll() {
+        return fsStorePaymentMapper.selectFsStorePaymentByAll();
+    }
+
     @Override
     @Transactional
     public R sendRedPacket(WxSendRedPacketParam param) {

+ 40 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -99,6 +99,9 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
 
     @Autowired
     private ConfigUtil configUtil;
+    
+    @Autowired
+    private com.fs.system.service.ISysConfigService configService;
 
     @Autowired
     @Qualifier("hzOMSErpGoodsServiceImpl")
@@ -1046,8 +1049,45 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         return fsStoreProductMapper.selectFsStoreProductByIdQuery(productId,storeId,medicalMallConfig);
     }
 
+    /**
+     * 检查库存(使用行锁)
+     */
+    private void checkStockWithLock(Long productId, Long productAttrValueId, Integer cartNum) {
+        // 检查商品库存
+        Integer productStock = fsStoreProductMapper.selectProductStockForUpdate(productId);
+        if (productStock == null || productStock < cartNum) {
+            FsStoreProductScrm product = fsStoreProductMapper.selectFsStoreProductById(productId);
+            String productName = product != null ? product.getProductName() : "商品";
+            throw new CustomException(productName + "库存不足,当前库存:" + (productStock != null ? productStock : 0) + ",需要数量:" + cartNum);
+        }
+        
+        // 如果有规格,检查规格库存
+        if (productAttrValueId != null) {
+            Integer attrStock = fsStoreProductAttrValueMapper.selectProductAttrStockForUpdate(productAttrValueId);
+            if (attrStock == null || attrStock < cartNum) {
+                FsStoreProductAttrValueScrm attrValue = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueById(productAttrValueId);
+                String sku = attrValue != null ? attrValue.getSku() : "规格";
+                throw new CustomException("商品规格" + sku + "库存不足,当前库存:" + (attrStock != null ? attrStock : 0) + ",需要数量:" + cartNum);
+            }
+        }
+    }
+    
     @Override
     public void decProductStock(Long productId, Long productAttrValueId, Integer cartNum) {
+        // 检查配置是否开启库存检查
+        try {
+            String configJson = configService.selectConfigByKey("his.store");
+            if (StringUtils.isNotEmpty(configJson)) {
+                com.fs.hisStore.config.StoreConfig config = com.alibaba.fastjson.JSON.parseObject(configJson, com.fs.hisStore.config.StoreConfig.class);
+                if (config != null && Boolean.TRUE.equals(config.getCheckStock())) {
+                    // 使用行锁检查库存
+                    checkStockWithLock(productId, productAttrValueId, cartNum);
+                }
+            }
+        } catch (Exception e) {
+            log.error("检查库存配置失败", e);
+        }
+        
         //处理属性sku
         fsStoreProductAttrValueMapper.decProductAttrStock(productAttrValueId,cartNum);
         fsStoreProductMapper.decProductAttrStock(productId,cartNum);

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

@@ -49,11 +49,37 @@ public class MergedOrderServiceImpl implements IMergedOrderService
 
     /*
      * 后端合并
+     * 根据订单类型筛选,选择不同的查询方式:
+     * - orderTypeFilter = 1: 只查询销售订单
+     * - orderTypeFilter = 2: 只查询商城订单
+     * - orderTypeFilter = 3: 只查询直播订单
+     * - orderTypeFilter = null: 使用视图查询全部订单
      * */
     @Override
     public List<MergedOrderVO> selectMergedOrderList(MergedOrderQueryParam param)
     {
-        List<MergedOrderVO> list = mergedOrderMapper.selectMergedOrderList(param);
+        List<MergedOrderVO> list;
+
+        // 根据订单类型筛选选择不同的查询方式
+        if (param.getOrderTypeFilter() != null) {
+            // 选择单个订单类型,只查询对应的表
+            if (param.getOrderTypeFilter() == 1) {
+                // 销售订单
+                list = mergedOrderMapper.selectSalesOrderList(param);
+            } else if (param.getOrderTypeFilter() == 2) {
+                // 商城订单
+                list = mergedOrderMapper.selectStoreOrderList(param);
+            } else if (param.getOrderTypeFilter() == 3) {
+                // 直播订单
+                list = mergedOrderMapper.selectLiveOrderList(param);
+            } else {
+                // 未知类型,使用视图查询
+                list = mergedOrderMapper.selectMergedOrderList(param);
+            }
+        } else {
+            // 全部订单,使用视图查询优化性能
+            list = mergedOrderMapper.selectMergedOrderList(param);
+        }
 
         // 处理商品JSON
         for (MergedOrderVO vo : list)
@@ -77,6 +103,15 @@ public class MergedOrderServiceImpl implements IMergedOrderService
 
         return list;
     }
+    
+    /**
+     * 统计合并订单总数
+     */
+    @Override
+    public long countMergedOrderList(MergedOrderQueryParam param)
+    {
+        return mergedOrderMapper.countMergedOrderList(param);
+    }
 
 
     @Autowired

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

@@ -35,7 +35,6 @@ public class FsStoreProductListVO  implements Serializable
 
     /** 产品条码(一维码) */
     private String barCode;
-
     /** 分类id */
     private Long cateId;
 
@@ -116,5 +115,4 @@ public class FsStoreProductListVO  implements Serializable
 
     private String storeId;
     private String storeName;
-
 }

+ 3 - 1
fs-service/src/main/java/com/fs/live/domain/LiveCouponUser.java

@@ -73,5 +73,7 @@ public class LiveCouponUser extends BaseEntity
     @Excel(name = "商品ID")
     private Long goodsId;
 
-
+    //非数据库实体 只是返回
+    private String nickname;
+    private String phone;
 }

+ 7 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java

@@ -159,6 +159,13 @@ public interface LiveOrderMapper {
     String selectLiveOrderProductStatistics(@Param("maps")LiveOrderParam param);
 
     List<LiveOrder> selectLiveOrderInId(@Param("ids") Long[] ids);
+
+    /**
+     * 根据订单ID列表批量查询订单
+     * @param orderIds 订单ID列表
+     * @return 订单列表
+     */
+    List<LiveOrder> selectLiveOrderByOrderIds(@Param("orderIds") List<Long> orderIds);
     @Select({"<script> " +
             "select o.*,cts.name as scheduleName,u.nickname,u.phone,cc.push_code,cc.create_time as customer_create_time," +
             "cc.source,cc.customer_code, c.company_name ,cu.nick_name as company_user_nick_name ," +

+ 7 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveOrderPaymentMapper.java

@@ -82,4 +82,11 @@ public interface LiveOrderPaymentMapper {
 
     @Select("select * from live_order_payment where business_id= #{orderId} and status=1 order by create_time desc limit 1")
     LiveOrderPayment selectLiveOrderLatestPayByOrderId(@Param("orderId") Long orderId);
+
+    /**
+     * 根据交易单号列表批量查询支付记录
+     * @param bankTransactionIds 交易单号列表
+     * @return 支付记录列表
+     */
+    List<LiveOrderPayment> selectByBankTransactionIds(@Param("bankTransactionIds") List<String> bankTransactionIds);
 }

+ 8 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveWatchLogMapper.java

@@ -72,6 +72,14 @@ public interface LiveWatchLogMapper extends BaseMapper<LiveWatchLog> {
 
     List<LiveWatchLog> selectLiveWatchLogByLiveId(@Param("liveId")Long liveId);
 
+    /**
+     * 根据直播间ID和外部联系人ID查询看课记录
+     * @param liveId 直播间ID
+     * @param externalContactId 外部联系人ID
+     * @return 看课记录
+     */
+    LiveWatchLog selectLiveWatchLogByLiveIdAndExternalId(@Param("liveId")Long liveId, @Param("externalContactId")Long externalContactId);
+
     List<LiveWatchLogListVO> selectLiveWatchLogListInfo(LiveWatchLog liveWatchLog);
 
     /**

+ 3 - 0
fs-service/src/main/java/com/fs/live/param/MergedOrderQueryParam.java

@@ -120,5 +120,8 @@ public class MergedOrderQueryParam extends BaseQueryParam implements Serializabl
     private String erpPhoneNumber;
     /** 汇付商户订单号 */
     private String hfshh;
+    
+    /** 分页偏移量(在外部计算后传入,不在SQL中计算) */
+    private Integer offset;
 }
 

+ 2 - 0
fs-service/src/main/java/com/fs/live/service/ILiveOrderService.java

@@ -270,4 +270,6 @@ public interface ILiveOrderService {
     Long isExistPayedRecord(Long orderId);
 
     void payConfirmPayment(Long existPayedRecordId);
+
+    void updateTime(LiveOrder order);
 }

+ 4 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveCouponServiceImpl.java

@@ -350,6 +350,10 @@ public class LiveCouponServiceImpl implements ILiveCouponService
         userRecord.setIsDel(0);
         userRecord.setGoodsId(coupon.getGoodsId());
         userRecord.setType("live-"+coupon.getLiveId());
+        //库存 remain_count
+        issue.setRemainCount(issue.getRemainCount()-1);
+        liveCouponIssueService.updateLiveCouponIssue(issue);
+
         liveCouponUserService.insertLiveCouponUser(userRecord);
         liveCouponIssueUserService.insertLiveCouponIssueUser(record);
 

+ 41 - 23
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -711,7 +711,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         } else {
             lockKey = "livePayConfirm:lock:" + System.currentTimeMillis();
         }
-        
+
         boolean lockAcquired = false;
         Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
         try {
@@ -1654,6 +1654,8 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 userService.subLiveTuiMoney(liveOrder);
             }
         }
+        // 删除限购记录
+        deletePurchaseLimitRecordsForLiveOrder(order);
 
         return R.ok();
     }
@@ -3661,6 +3663,20 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         this.payConfirm(1, null, payment.getPayCode(), payment.getTradeNo(), payment.getBankSerialNo(), payment.getBankSerialNo());
     }
 
+    @Override
+    public void updateTime(LiveOrder order) {
+        LiveOrder liveOrder = new LiveOrder();
+        liveOrder.setOrderId(order.getOrderId());
+        liveOrder.setUpdateTime(order.getUpdateTime());
+        liveOrderMapper.updateLiveOrder(liveOrder);
+    }
+
+    public void deStockIncSale(List<FsStoreCartQueryVO> cartInfo) {
+        for (FsStoreCartQueryVO storeCartVO : cartInfo) {
+            fsStoreProductService.decProductStock(storeCartVO.getProductId(),
+                    storeCartVO.getProductAttrValueId(), storeCartVO.getCartNum());
+        }
+    }
 
     @Override
     @Transactional(rollbackFor = Throwable.class,propagation = Propagation.REQUIRED)
@@ -3690,27 +3706,19 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         if(!"1".equals(fsStoreProduct.getIsAudit()) ) return R.error("商品已下架,购买失败");
         if(liveOrder.getTotalNum() == null || StringUtils.isEmpty(liveOrder.getTotalNum())) liveOrder.setTotalNum("1");
         if(goods.getStock() == null) return R.error("直播间商品库存不足");
-        if(fsStoreProduct.getStock() < Integer.parseInt(liveOrder.getTotalNum()) || goods.getStock() < Integer.parseInt(liveOrder.getTotalNum())) return R.error("抱歉,这款商品已被抢光,暂时无库存~");
+        FsStoreProductAttrValueScrm attrValue = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueById(liveOrder.getAttrValueId());
+        if(fsStoreProduct.getStock() < Integer.parseInt(liveOrder.getTotalNum()) || goods.getStock() < Integer.parseInt(liveOrder.getTotalNum()) || attrValue.getStock() < Integer.parseInt(liveOrder.getTotalNum())){
+            return R.error("抱歉,这款商品已被抢光,暂时无库存~");
+        }
+
 
         // 检查限购
         Long userId = Long.parseLong(liveOrder.getUserId());
         Integer purchaseNum = Integer.parseInt(liveOrder.getTotalNum());
         checkPurchaseLimitForLiveOrder(userId, liveOrder.getProductId(), purchaseNum);
 
-        FsStoreProductAttrValueScrm attrValue = null;
-        if (!Objects.isNull(liveOrder.getAttrValueId())) {
-            attrValue = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueById(liveOrder.getAttrValueId());
-        }
-        if (attrValue != null) {
-            attrValue.setStock(attrValue.getStock() - Integer.parseInt(liveOrder.getTotalNum()));
-            attrValue.setSales(attrValue.getSales() + Integer.parseInt(liveOrder.getTotalNum()));
-            fsStoreProductAttrValueMapper.updateFsStoreProductAttrValue(attrValue);
-        }
-        // 更改店铺库存
-        fsStoreProduct.setStock(fsStoreProduct.getStock()-Integer.parseInt(liveOrder.getTotalNum()));
-        fsStoreProduct.setSales(fsStoreProduct.getSales()+Integer.parseInt(liveOrder.getTotalNum()));
-        fsStoreProductScrmMapper.incStockDecSales(Long.valueOf("-" + liveOrder.getTotalNum()),fsStoreProduct.getProductId());
-
+        // 使用 deStockIncSale 方法处理库存和销量
+        deStockIncSale(liveOrder.getProductId(), liveOrder.getAttrValueId(), purchaseNum);
         // 更新直播间库存
         goods.setStock(goods.getStock()-Integer.parseInt(liveOrder.getTotalNum()));
         goods.setSales(goods.getSales()+Integer.parseInt(liveOrder.getTotalNum()));
@@ -3738,6 +3746,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         BigDecimal payPrice = fsStoreProduct.getPrice().multiply(new BigDecimal(liveOrder.getTotalNum()));
         if (attrValue != null) {
             payPrice = attrValue.getPrice().multiply(new BigDecimal(liveOrder.getTotalNum()));
+            fsStoreProduct.setPrice(payPrice);
         }
         // 直播不需要服务费 0915 1735 左
 //        String config=configService.selectConfigByKey("store.config");
@@ -3796,11 +3805,13 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                     dto.setBarCode(fsStoreProductAttrValue.getBarCode());
                     dto.setGroupBarCode(fsStoreProductAttrValue.getGroupBarCode());
                 }
+                dto.setPrice(fsStoreProduct.getPrice());
                 if (attrValue != null) {
                     dto.setBarCode(attrValue.getBarCode());
                     dto.setGroupBarCode(attrValue.getGroupBarCode());
+                    dto.setPrice(attrValue.getPrice());
                 }
-                dto.setPrice(fsStoreProduct.getPrice());
+                //dto.setPrice(fsStoreProduct.getPrice());
                 dto.setProductName(fsStoreProduct.getProductName());
                 dto.setNum(Long.valueOf(liveOrder.getTotalNum()));
 
@@ -3812,7 +3823,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 liveOrderItem.setJsonInfo(JSON.toJSONString(dto));
                 liveOrderItemMapper.insertLiveOrderItem(liveOrderItem);
 
-                
+
                 redisCache.deleteObject("orderKey:" + liveOrder.getOrderKey());
                 //添加支付到期时间
                 Calendar calendar = Calendar.getInstance();
@@ -3983,8 +3994,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
             goods.setStock(goods.getStock()+Long.parseLong(liveOrder.getTotalNum()));
             // 更新商品库存
             liveGoodsMapper.updateLiveGoods(goods);
-            // 删除限购记录
-            deletePurchaseLimitRecordsForLiveOrder(liveOrder);
+
             // 退券
             this.refundCoupon(order);
             TemplateBean templateBean = TemplateBean.builder()
@@ -4008,25 +4018,33 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
      * @param productId 商品ID
      * @param num 购买数量
      */
+    /**
+     * 减库存加销量(直播订单)
+     */
+    private void deStockIncSale(Long productId, Long attrValueId, Integer num) {
+        // 使用 productService.decProductStock 方法,该方法已包含库存检查逻辑
+        fsStoreProductService.decProductStock(productId, attrValueId, num);
+    }
+    
     private void checkPurchaseLimitForLiveOrder(Long userId, Long productId, Integer num) {
         // 查询商品信息
         FsStoreProductScrm product = fsStoreProductService.selectFsStoreProductById(productId);
         if (product == null) {
             return;
         }
-        
+
         // 如果商品没有设置限购,直接返回
         if (product.getPurchaseLimit() == null || product.getPurchaseLimit() <= 0) {
             return;
         }
-        
+
         // 查询用户已购买的数量
         FsStoreProductPurchaseLimitScrm purchaseLimit = purchaseLimitService.selectByProductIdAndUserId(productId, userId);
         int purchasedNum = 0;
         if (purchaseLimit != null) {
             purchasedNum = purchaseLimit.getNum();
         }
-        
+
         // 检查是否超过限购数量
         if (purchasedNum + num > product.getPurchaseLimit()) {
             int productTotalNum = purchasedNum + num;

+ 123 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java

@@ -30,6 +30,8 @@ import com.fs.live.vo.*;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qwApi.domain.QwResult;
 import com.fs.qwApi.param.QwEditUserTagParam;
+import com.fs.qwApi.param.QwExternalContactRemarkParam;
+import com.fs.qwApi.domain.QwExternalContactRemarkResult;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.qw.domain.QwGroupChat;
 import com.fs.qw.domain.QwGroupChatUser;
@@ -46,9 +48,12 @@ import org.springframework.context.annotation.Lazy;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
+import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 /**
@@ -934,6 +939,8 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
                     if (null != liveTagItemVO) {
                         tags.add(liveTagItemVO.getQwTagRealId());
                     }
+                    // 设置完课标志
+                    addItem.setIsCompleted(true);
                     break;
                 //3待看课
                 case 3:
@@ -1053,6 +1060,10 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
                     updateContact.setTagIds(JSON.toJSONString(new ArrayList<>(uniqueTagIds)));
                     qwExternalContactMapper.updateQwExternalContact(updateContact);
 
+                    // 处理完课备注更新逻辑
+                    Boolean isCompleted = userTagVO.getIsCompleted() != null && userTagVO.getIsCompleted();
+                    updateRemarkForCompletedCourse(userTagVO.getLiveId(), userTagVO.getExternalId(), qwExternalContact, isCompleted);
+
                     successCount++;
                     log.info("成功为用户打标签: externalId={}, userId={}, externalUserId={}, tags={}",
                             userTagVO.getExternalId(),
@@ -1080,6 +1091,118 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
                 userTagVOS.size(), successCount, failCount);
     }
 
+    /**
+     * 更新完课备注
+     * 如果用户完课,在备注中添加或更新完课日期
+     * 格式:MMdd完课-原有备注
+     * 
+     * @param liveId 直播间ID
+     * @param externalId 外部联系人ID
+     * @param qwExternalContact 企微外部联系人信息
+     * @param isCompleted 完课标志(如果为true,不再查询数据库)
+     */
+    private void updateRemarkForCompletedCourse(Long liveId, Long externalId, QwExternalContact qwExternalContact, Boolean isCompleted) {
+        try {
+            // 如果liveId为空,跳过
+            if (liveId == null || externalId == null) {
+                return;
+            }
+
+            //
+            boolean completed = false;
+            if (isCompleted != null) {
+                // 使用传入的完课标志
+                completed = isCompleted;
+            }
+            
+            if (!completed) {
+                // 未完课,不改动备注
+                log.debug("用户未完课,不更新备注: liveId={}, externalId={}", 
+                        liveId, externalId);
+                return;
+            }
+
+            // 完课,需要更新备注
+            String currentRemark = qwExternalContact.getRemark();
+            String newRemark = buildCompletedCourseRemark(currentRemark);
+            
+            // 如果备注没有变化,跳过更新
+            if (newRemark.equals(currentRemark)) {
+                log.debug("备注无需更新: liveId={}, externalId={}, remark={}", 
+                        liveId, externalId, currentRemark);
+                return;
+            }
+
+            // 调用企微API更新备注
+            QwExternalContactRemarkParam remarkParam = new QwExternalContactRemarkParam();
+            remarkParam.setUserid(qwExternalContact.getUserId());
+            remarkParam.setExternal_userid(qwExternalContact.getExternalUserId());
+            remarkParam.setRemark(newRemark);
+
+            QwExternalContactRemarkResult remarkResult = qwApiService.externalcontactRemark(remarkParam, qwExternalContact.getCorpId());
+            
+            if (remarkResult != null && remarkResult.getErrcode() == 0) {
+                // 更新成功,同步更新数据库
+                QwExternalContact updateContact = new QwExternalContact();
+                updateContact.setId(qwExternalContact.getId());
+                updateContact.setRemark(newRemark);
+                qwExternalContactMapper.updateQwExternalContact(updateContact);
+                
+                log.info("成功更新完课备注: liveId={}, externalId={}, oldRemark={}, newRemark={}",
+                        liveId, externalId, currentRemark, newRemark);
+            } else {
+                String errorMsg = remarkResult != null ? remarkResult.getErrmsg() : "未知错误";
+                log.error("更新完课备注失败: liveId={}, externalId={}, error={}",
+                        liveId, externalId, errorMsg);
+            }
+        } catch (Exception e) {
+            log.error("更新完课备注异常: liveId={}, externalId={}, error={}",
+                    liveId, externalId, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 构建完课备注
+     * 格式:MMdd完课-原有备注
+     * 如果已有完课标记,则更新日期部分
+     * 
+     * @param currentRemark 当前备注
+     * @return 新的备注
+     */
+    private String buildCompletedCourseRemark(String currentRemark) {
+        // 获取当前日期(MMdd格式)
+        SimpleDateFormat dateFormat = new SimpleDateFormat("MMdd");
+        String todayDate = dateFormat.format(new Date());
+        String completedPrefix = todayDate + "完课";
+
+        // 如果备注为空,直接返回完课标记
+        if (StringUtils.isEmpty(currentRemark)) {
+            return completedPrefix;
+        }
+
+        // 使用正则表达式匹配已有的完课标记格式:MMdd完课-xxx
+        // 匹配模式:4位数字(MMdd)+ "完课" + "-" + 后续内容
+        Pattern pattern = Pattern.compile("^(\\d{4})完课-(.+)$");
+        Matcher matcher = pattern.matcher(currentRemark);
+
+        if (matcher.matches()) {
+            // 已有完课标记,更新日期部分
+            String existingDate = matcher.group(1);
+            String remainingRemark = matcher.group(2);
+            
+            // 如果日期相同,不需要更新
+            if (todayDate.equals(existingDate)) {
+                return currentRemark;
+            }
+            
+            // 更新日期
+            return completedPrefix + "-" + remainingRemark;
+        } else {
+            // 没有完课标记,添加完课标记到前面
+            return completedPrefix + "-" + currentRemark;
+        }
+    }
+
     /**
      * 更新用户观看时长(心跳时调用)- 异步执行
      * @param liveId 直播间ID

+ 5 - 0
fs-service/src/main/java/com/fs/live/vo/HandleUserTagVO.java

@@ -26,4 +26,9 @@ public class HandleUserTagVO {
      * 打标签列表
      */
     private List<String> tags;
+
+    /**
+     * 是否完课标志(logType == 2 表示完课)
+     */
+    private Boolean isCompleted;
 }

+ 1 - 1
fs-service/src/main/java/com/fs/live/vo/MergedOrderVO.java

@@ -96,9 +96,9 @@ public class MergedOrderVO implements Serializable
     /** 支付时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date payTime;
-    @Excel(name = "发货时间")
     /** 发货时间 */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "发货时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     private Date deliverySendTime;
 
     /** 总数量(直播订单使用) */

+ 61 - 0
fs-service/src/main/java/com/fs/live/vo/PaymentExportVO.java

@@ -0,0 +1,61 @@
+package com.fs.live.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 支付导出VO
+ *
+ * @author fs
+ * @date 2026-01-08
+ */
+@Data
+public class PaymentExportVO {
+
+    /** 支付时间 */
+    @Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date payTime;
+
+    /** 交易单号 */
+    @Excel(name = "交易单号")
+    private String bankTransactionId;
+
+    /** 商户单号 */
+    @Excel(name = "商户单号")
+    private String tradeNo;
+
+    /** 商户号 */
+    @Excel(name = "商户号")
+    private String merchantNo;
+
+    /** 支付金额 */
+    @Excel(name = "支付金额")
+    private BigDecimal payMoney;
+
+    /** 状态 */
+    @Excel(name = "状态")
+    private String status;
+
+    /** 订单状态 */
+    @Excel(name = "订单状态")
+    private String orderStatus;
+
+    /** 物流单号 */
+    @Excel(name = "物流单号")
+    private String deliverySn;
+
+    /** 物流公司 */
+    @Excel(name = "物流公司")
+    private String deliveryName;
+
+    /** 商品名称 */
+    @Excel(name = "商品名称")
+    private String productName;
+
+    /** 支付订单号(带前缀) */
+    @Excel(name = "支付订单号")
+    private String payCodeWithPrefix;
+}

+ 41 - 0
fs-service/src/main/java/com/fs/live/vo/PaymentImportVO.java

@@ -0,0 +1,41 @@
+package com.fs.live.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 支付导入VO
+ *
+ * @author fs
+ * @date 2026-01-08
+ */
+@Data
+public class PaymentImportVO {
+
+    /** 支付时间 */
+    @Excel(name = "支付时间")
+    private Date payTime;
+
+    /** 交易单号 */
+    @Excel(name = "交易单号", required = true)
+    private String bankTransactionId;
+
+    /** 商户单号 */
+    @Excel(name = "商户单号")
+    private String tradeNo;
+
+    /** 商户号 */
+    @Excel(name = "商户号")
+    private String merchantNo;
+
+    /** 支付金额 */
+    @Excel(name = "支付金额")
+    private BigDecimal payMoney;
+
+    /** 状态 */
+    @Excel(name = "状态")
+    private String status;
+}

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

@@ -237,6 +237,7 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
             "left join qw_contact_way cw on cw.id = ec.way_id " +
             "left join qw_contact_way_group wg on wg.id=cw.group_id" +
             "<where>  \n" +
+            "            <if test=\"id != null  and id != ''\"> and ec.id   like concat( #{id}, '%') </if>\n" +
             "            <if test=\"userId != null  and userId != ''\"> and ec.user_id   like concat( #{userId}, '%') </if>\n" +
             "            <if test=\"qwUserName != null  and qwUserName != ''\"> and qu.qw_user_name   like concat( #{qwUserName}, '%') </if>\n" +
             "            <if test=\"externalUserId != null  and externalUserId != ''\"> and ec.external_user_id = #{externalUserId}</if>\n" +
@@ -270,7 +271,6 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
             "            <if test=\"companyUserId != null \"> and ec.company_user_id = #{companyUserId}</if>\n" +
             "            <if test=\"customerId != null \"> and ec.customer_id = #{customerId}</if>\n" +
             "            <if test=\"status != null \"> and ec.status = #{status}</if>\n" +
-            "            <if test=\"status != null \"> and ec.status = #{status}</if>\n" +
             "<if test = \"statusCondition != null and  statusCondition.length > 0 \" > " +
             "   AND ec.status in " +
             "   <foreach collection='statusCondition' item='item' open='(' separator=',' close=')'> " +

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

@@ -88,6 +88,7 @@ public interface QwFriendWelcomeMapper
             "           AND qu.qw_user_id = #{qwUserIds} " +
             "       ) AND " +
             "   </if>" +
+            "   <if test=\"id != null  and id != ''\"> qfw.id like concat( #{id}, '%') AND </if>\n" +
             "   <if test=\"isSendMsg != null\"> qfw.is_send_msg = #{isSendMsg} AND </if>" +
             "  <if test=\"createTime != null \"> DATE(qfw.create_time) = DATE(#{createTime}) AND</if>\n" +
             "  <if test=\"updateTime != null \"> DATE(qfw.update_time) = DATE(#{updateTime}) AND</if>\n" +

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

@@ -2,6 +2,8 @@ package com.fs.qw.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.fastGpt.domain.FastGptPushTokenTotal;
+import com.fs.fastGpt.param.FastGptPushTokenDeptTotalParam;
+import com.fs.fastGpt.vo.FastGptPushTokenDeptTotalVO;
 import com.fs.qw.domain.QwRestrictionPushRecord;
 import org.apache.ibatis.annotations.Delete;
 import org.apache.ibatis.annotations.Param;
@@ -57,5 +59,6 @@ public interface QwRestrictionPushRecordMapper extends BaseMapper<QwRestrictionP
 
     List<FastGptPushTokenTotal> selectFastGptPushTokenTotalList(FastGptPushTokenTotal pushTokenInfo);
 
+    List<FastGptPushTokenDeptTotalVO>  selectFastGptPushTokenTotalDeptList(FastGptPushTokenDeptTotalParam pushTokenInfo);
 }
 

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

@@ -151,6 +151,7 @@ public interface QwUserMapper extends BaseMapper<QwUser>
             "RIGHT JOIN qw_dept qd on qu.department=qd.dept_id and qd.corp_id=qu.corp_id " +
             "left join fastgpt_role fr on fr.role_id=qu.fastGpt_role_id " +
             "where qu.company_user_id is not null "+
+            "            <if test=\"id != null  and id != ''\"> and qu.id = #{id}</if>\n" +
             "            <if test=\"qwUserId != null  and qwUserId != ''\"> and qu.qw_user_id = #{qwUserId}</if>\n" +
             "            <if test=\"loginStatus != null \"> and qu.ipad_status = #{loginStatus}</if>\n" +
             "            <if test=\"appKey != null  and appKey != ''\"> and qu.app_key = #{appKey}</if>\n" +

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

@@ -2,6 +2,8 @@ package com.fs.qw.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.fastGpt.domain.FastGptPushTokenTotal;
+import com.fs.fastGpt.param.FastGptPushTokenDeptTotalParam;
+import com.fs.fastGpt.vo.FastGptPushTokenDeptTotalVO;
 import com.fs.qw.domain.QwPushCount;
 
 import java.util.List;
@@ -68,4 +70,6 @@ public interface IQwPushCountService extends IService<QwPushCount>{
     int deleteQwPushCountById(Long id);
 
     List<FastGptPushTokenTotal> selectFastGptPushTokenTotalList(FastGptPushTokenTotal pushTokenInfo);
+
+    List<FastGptPushTokenDeptTotalVO> selectFastGptPushTokenTotalDeptList(FastGptPushTokenDeptTotalParam pushTokenInfo);
 }

+ 7 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwPushCountServiceImpl.java

@@ -2,6 +2,8 @@ package com.fs.qw.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.fastGpt.domain.FastGptPushTokenTotal;
+import com.fs.fastGpt.param.FastGptPushTokenDeptTotalParam;
+import com.fs.fastGpt.vo.FastGptPushTokenDeptTotalVO;
 import com.fs.qw.domain.QwPushCount;
 import com.fs.qw.mapper.QwPushCountMapper;
 import com.fs.qw.mapper.QwRestrictionPushRecordMapper;
@@ -114,4 +116,9 @@ public class QwPushCountServiceImpl extends ServiceImpl<QwPushCountMapper, QwPus
     public List<FastGptPushTokenTotal> selectFastGptPushTokenTotalList(FastGptPushTokenTotal pushTokenInfo) {
         return pushRecordMapper.selectFastGptPushTokenTotalList(pushTokenInfo);
     }
+
+    @Override
+    public   List<FastGptPushTokenDeptTotalVO> selectFastGptPushTokenTotalDeptList(FastGptPushTokenDeptTotalParam pushTokenInfo){
+        return pushRecordMapper.selectFastGptPushTokenTotalDeptList(pushTokenInfo);
+    }
 }

+ 140 - 6
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -581,6 +581,143 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
         fsUserCourseVideoRedPackageService.batchSaveFsUserCourseVideoRedPackage(redPackage);
     }
 
+
+    private void createSopTempRulesTwo(QwSopTemp temp, List<FsUserCourseVideo> videoList, FsUserCourse fsUserCourse) {
+        AtomicInteger i = new AtomicInteger(1);
+        Integer gap = temp.getGap();
+        List<QwSopTempDay> collect = videoList.stream().map(e -> {
+            QwSopTempDay day = new QwSopTempDay();
+            day.setTempId(temp.getId());
+            day.setDayNum(i.getAndIncrement() * gap);
+            day.setName("第" + day.getDayNum() + "天");
+            day.setSorts(day.getDayNum());
+            day.setList(new ArrayList<>());
+            List<String> timeList = new ArrayList<>();
+            if (temp.getTimeList() != null) {
+                timeList = JSON.parseArray(JSON.toJSONString(temp.getTimeList()), String.class);
+            }
+            AtomicInteger sorts = new AtomicInteger(0);
+            List<QwSopTempRules> rulesList = timeList.stream().map(time -> {
+                QwSopTempRules rules = new QwSopTempRules();
+                rules.setTempId(temp.getId());
+                rules.setName(day.getName());
+
+                if (!"河山医院".equals(cloudHostProper.getCompanyName())){
+                    if (temp.getOpenOfficial().equals("1")) {
+                        rules.setIsOfficial(sorts.get() == 0 ? "1" : "0");
+                    } else {
+                        rules.setIsOfficial("0");
+                    }
+
+                    if (day.getDayNum() == 1 && sorts.get() == 0 && temp.getOpenOfficial().equals("1")) {
+                        rules.setTime("01:05");
+                    } else {
+                        rules.setTime(time);
+                    }
+                }else {
+                    rules.setIsOfficial("0");
+                    rules.setTime(time);
+                }
+
+                if (temp.getOpenIsAtAll() != null && temp.getOpenIsAtAll().equals("1")){
+                    rules.setIsAtAll(1);
+                }else {
+                    rules.setIsAtAll(0);
+                }
+
+                rules.setContentType(2);
+                rules.setType(2);
+                rules.setCourseType(0);
+                rules.setCourseId(e.getCourseId());
+                rules.setVideoId(e.getVideoId());
+                // 设置消息类型
+
+                QwSopTempContent content = new QwSopTempContent();
+                content.setTempId(temp.getId());
+                content.setContentType(3);
+                QwSopTempSetting2.Content.Setting setting = new QwSopTempSetting2.Content.Setting();
+                setting.setLinkTitle(e.getTitle());
+                setting.setMiniprogramTitle(e.getTitle());
+
+                //用课节图片做封面
+                if ("今正科技".equals(cloudHostProper.getCompanyName())) {
+                    setting.setMiniprogramPicUrl(!StringUtil.isNullOrEmpty(e.getThumbnail()) ? e.getThumbnail() : fsUserCourse.getImgUrl());
+                    setting.setLinkImageUrl(!StringUtil.isNullOrEmpty(e.getThumbnail()) ? e.getThumbnail() : fsUserCourse.getImgUrl());
+                } else {
+                    setting.setMiniprogramPicUrl(fsUserCourse.getImgUrl());
+                    setting.setLinkImageUrl(fsUserCourse.getImgUrl());
+
+                }
+
+                setting.setIsBindUrl(1);
+                setting.setLinkDescribe(e.getTitle());
+                setting.setContentType("4");
+                content.setContent(JSON.toJSONString(setting));
+                content.setIsBindUrl(1);
+                List<QwSopTempContent> qwSopTempContents = new ArrayList<>();
+                qwSopTempContents.add(content);
+
+                QwSopTempContent content3 = new QwSopTempContent();
+                content3.setTempId(temp.getId());
+                content3.setContentType(3);
+
+                if (!temp.getTimeDesc().isEmpty()){
+
+                    QwSopTempSetting2.Content.Setting setting3 = new QwSopTempSetting2.Content.Setting();
+                    setting3.setValue(temp.getTimeDesc().get(sorts.get()));
+                    setting3.setContentType("1");
+                    content3.setContent(JSON.toJSONString(setting3));
+
+                    qwSopTempContents.add(content3);
+
+                }
+
+
+                rules.setSorts(sorts.getAndIncrement());
+                if (rules.getSorts() == 0) {
+                    rules.setCourseType(0);
+                } else if (rules.getSorts() == 1) {
+                    rules.setCourseType(1);
+                } else {
+                    rules.setCourseType(4);
+                }
+                rules.setList(qwSopTempContents);
+                return rules;
+            }).collect(Collectors.toList());
+            day.getList().addAll(rulesList);
+            return day;
+        }).collect(Collectors.toList());
+        qwSopTempDayService.addOrUpdateBatch(collect);
+        List<QwSopTempRules> ruleList = collect.stream().flatMap(e -> e.getList().stream().peek(r -> r.setDayId(e.getId()))).collect(Collectors.toList());
+        qwSopTempRulesService.saveList(ruleList);
+        List<QwSopTempContent> contentList = ruleList.stream().flatMap(e -> e.getList().stream().peek(r -> {
+            r.setDayId(e.getDayId());
+            r.setRulesId(e.getId());
+        })).collect(Collectors.toList());
+        qwSopTempContentService.saveList(contentList);
+        SysConfig sysConfig = sysConfigService.selectConfigByConfigKey("course.config");
+        CourseConfig courseConfig = JSON.parseObject(sysConfig.getConfigValue(), CourseConfig.class);
+        List<Long> videoIdList = PubFun.listToNewList(ruleList, QwSopTempRules::getVideoId);
+        Map<Long, Optional<BigDecimal>> redMap;
+        if (!videoIdList.isEmpty()) {
+            List<FsUserCourseVideoRedPackage> redPackageList = fsUserCourseVideoRedPackageService.listByCompanyIdAndVideoIds(temp.getCompanyId(), videoIdList);
+            redMap = redPackageList.stream().collect(Collectors.groupingBy(FsUserCourseVideoRedPackage::getVideoId, Collectors.mapping(FsUserCourseVideoRedPackage::getRedPacketMoney, Collectors.reducing((e1, e2) -> e1))));
+        } else {
+            redMap = new HashMap<>();
+        }
+
+        List<FsUserCourseVideoRedPackage> redPackage = ruleList.stream().filter(e -> e.getVideoId() != null && e.getSorts() == 0).map(e -> {
+            FsUserCourseVideoRedPackage red = new FsUserCourseVideoRedPackage();
+            red.setCompanyId(temp.getCompanyId());
+            red.setVideoId(e.getVideoId());
+            red.setRedPacketMoney(redMap.getOrDefault(e.getVideoId(), Optional.ofNullable(courseConfig.getRedPackageMoney())).orElse(BigDecimal.ZERO));
+            red.setRuleId(e.getId());
+            red.setDataType(3);
+            return red;
+        }).collect(Collectors.toList());
+        fsUserCourseVideoRedPackageService.batchSaveFsUserCourseVideoRedPackage(redPackage);
+    }
+
     @Override
     public List<QwSopTempRedPackageVo> redList(String id) {
         List<QwSopTempDay> dayList = qwSopTempRulesService.listByTempIdAll(id);
@@ -691,17 +828,14 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
                 }
                 QwSopTempDay qwSopTempDay = first.get();
 
-                // 构造timeList timeDesc time
-                sopTemp.setTime(LocalTime.now());
-
                 sopTemp.setTimeList(tempRulesList.stream()
-                        .filter(e -> e.getTempId().equals(sopTemp.getId()) && Objects.equals(e.getIsOfficial(), "0")
+                        .filter(e -> e.getTempId().equals(sopTemp.getId())
                                 && Objects.equals(e.getDayId(), qwSopTempDay.getId())
                         ).map(QwSopTempRules::getTime)
                         .collect(Collectors.toList()));
 
                 sopTemp.setTimeDesc(contentList.stream().filter(e -> e.getTempId().equals(sopTemp.getId())
-                                && Objects.isNull(e.getIsBindUrl())
+                                && Objects.equals(1,e.getContentType())
                                 && Objects.equals(qwSopTempDay.getId(), e.getDayId())
                         )
                         .map(m -> JSONObject.parseObject(m.getContent()).getString("value")).collect(Collectors.toList()));
@@ -711,7 +845,7 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
 
                 qwSopTempMapper.updateQwSopTemp(sopTemp);
 
-                createSopTempRules(sopTemp, addVideoList, fsUserCourse);
+                createSopTempRulesTwo(sopTemp, addVideoList, fsUserCourse);
 
                 // 更新排序
                 List<QwSopTempRules> newTempRulesList = qwSopTempRulesService.listByCourseIdAndTempId(courseId, teId);

+ 2 - 2
fs-service/src/main/resources/application-config-druid-bjzm-test.yml

@@ -15,8 +15,8 @@ logging:
 wx:
   miniapp:
     configs:
-      - appid: wxe67df00c8a1e6bed   #北京卓美
-        secret: 7ded976d7aa1901cf5e73e8da70fb37d #北京卓美
+      - appid: wx30d1ed5dbbdc5cc5   #乐享优品百域臻品
+        secret: 63624481b94437ecbd3ce34c19543746 #北京卓美
         token: cbnd7lJvkripVOpyTFAna6NAWCxCrvC
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
         msgDataFormat: JSON

+ 2 - 2
fs-service/src/main/resources/application-config-druid-bjzm.yml

@@ -10,8 +10,8 @@ logging:
 wx:
   miniapp:
     configs:
-      - appid: wxe67df00c8a1e6bed   #北京卓美
-        secret: 7ded976d7aa1901cf5e73e8da70fb37d #北京卓美
+      - appid: wx30d1ed5dbbdc5cc5   #乐享优品百域臻品
+        secret: 63624481b94437ecbd3ce34c19543746 #北京卓美
         token: cbnd7lJvkripVOpyTFAna6NAWCxCrvC
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
         msgDataFormat: JSON

+ 7 - 1
fs-service/src/main/resources/application-config-fzbt.yml

@@ -10,6 +10,11 @@ logging:
 wx:
   miniapp:
     configs:
+      - appid: wx4115995705bb0ea0   #中康智慧
+        secret: 58910ae743005c396012b029c7def579
+        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
   cp:
     corpId: wwb2a1055fb6c9a7c2
     appConfigs:
@@ -74,12 +79,13 @@ cloud_host:
   company_name: 福州白兔
   projectCode: FZBT
   spaceName: myhk-2114522511
+  volcengineUrl: https://myhkvolcengine.ylrztop.com
 #看课授权时显示的头像
 headerImg:
   imgUrl: https://fs-1346741853.cos.ap-chengdu.myqcloud.com/fs/20250323/6189704f2e134b84ad9c9e7c9999f103.jpg
 ipad:
   ipadUrl: http://qwipad.fjbaitu.com
-  aiApi: http://152.136.202.157:3000/api
+  aiApi: http://49.232.181.28:3000/api
   voiceApi:
   commonApi:
 wx_miniapp_temp:

+ 1 - 0
fs-service/src/main/resources/mapper/company/CompanyUserMapper.xml

@@ -97,6 +97,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         u.is_need_register_member,u.is_allowed_all_register,
         u.avatar,u.address_id,
         u.qw_user_id,
+        u.doctor_id,
         d.dept_name,
         d.leader
         from

+ 1 - 0
fs-service/src/main/resources/mapper/course/FsCourseFinishTempParentMapper.xml

@@ -23,6 +23,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectFsCourseFinishTempParentList" parameterType="FsCourseFinishTempParent" resultMap="FsCourseFinishTempParentResult">
         <include refid="selectFsCourseFinishTempParentVo"/>
         <where>
+            <if test="id != null "> and id = #{id}</if>
             <if test="name != null  and name != ''"> and name like concat('%', #{name}, '%')</if>
             <if test="courseId != null "> and course_id = #{courseId}</if>
             <if test="companyId != null "> and company_id = #{companyId}</if>

+ 11 - 1
fs-service/src/main/resources/mapper/his/FsAiWorkflowMapper.xml

@@ -16,11 +16,12 @@
         <result property="updateTime" column="update_time"/>
         <result property="remark" column="remark"/>
         <result property="delFlag" column="del_flag"/>
+        <result property="companyUserId" column="company_user_id"/>
     </resultMap>
 
     <sql id="selectFsAiWorkflowVo">
         select workflow_id, workflow_name, workflow_desc, workflow_type, status, version,
-               canvas_data, create_by, create_time, update_by, update_time, remark, del_flag
+               canvas_data, create_by, create_time, update_by, update_time, remark, del_flag, company_user_id
         from fs_ai_workflow
     </sql>
 
@@ -33,6 +34,7 @@
             </if>
             <if test="workflowType != null">and workflow_type = #{workflowType}</if>
             <if test="status != null">and status = #{status}</if>
+            <if test="companyUserId != null">and company_user_id = #{companyUserId}</if>
         </where>
         order by create_time desc
     </select>
@@ -42,6 +44,12 @@
         where workflow_id = #{workflowId} and del_flag = 0
     </select>
 
+    <select id="selectFsAiWorkflowByCompanyUserId" parameterType="Long" resultMap="FsAiWorkflowResult">
+        <include refid="selectFsAiWorkflowVo"/>
+        where company_user_id = #{companyUserId} and del_flag = 0 and status = 1
+        order by create_time desc
+    </select>
+
     <insert id="insertFsAiWorkflow" parameterType="FsAiWorkflow" useGeneratedKeys="true" keyProperty="workflowId">
         insert into fs_ai_workflow
         <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -54,6 +62,7 @@
             <if test="createBy != null">create_by,</if>
             <if test="createTime != null">create_time,</if>
             <if test="remark != null">remark,</if>
+            <if test="companyUserId != null">company_user_id,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="workflowName != null and workflowName != ''">#{workflowName},</if>
@@ -65,6 +74,7 @@
             <if test="createBy != null">#{createBy},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="remark != null">#{remark},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
         </trim>
     </insert>
 

+ 12 - 0
fs-service/src/main/resources/mapper/his/FsAiWorkflowNodeMapper.xml

@@ -18,6 +18,7 @@
         <result property="sortOrder" column="sort_order"/>
         <result property="createTime" column="create_time"/>
         <result property="updateTime" column="update_time"/>
+        <result property="voiceUrl" column="voice_url"/>
     </resultMap>
 
     <select id="selectFsAiWorkflowNodeById" parameterType="Long" resultMap="FsAiWorkflowNodeResult">
@@ -28,6 +29,16 @@
         select * from fs_ai_workflow_node where workflow_id = #{workflowId} order by sort_order
     </select>
 
+    <select id="selectNodesByWorkflowIdAndTypes" resultMap="FsAiWorkflowNodeResult">
+        select * from fs_ai_workflow_node
+        where workflow_id = #{workflowId}
+        and node_type in
+        <foreach collection="nodeTypes" item="nodeType" open="(" separator="," close=")">
+            #{nodeType}
+        </foreach>
+        order by sort_order
+    </select>
+
     <insert id="insertFsAiWorkflowNode" parameterType="FsAiWorkflowNode" useGeneratedKeys="true" keyProperty="nodeId">
         insert into fs_ai_workflow_node (workflow_id, node_key, node_name, node_type, node_icon, node_color,
             pos_x, pos_y, width, height, node_config, sort_order, create_time)
@@ -58,6 +69,7 @@
             <if test="height != null">height = #{height},</if>
             <if test="nodeConfig != null">node_config = #{nodeConfig},</if>
             <if test="sortOrder != null">sort_order = #{sortOrder},</if>
+            <if test="voiceUrl != null">voice_url = #{voiceUrl},</if>
             update_time = now(),
         </trim>
         where node_id = #{nodeId}

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

@@ -160,6 +160,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where id = #{id}
     </select>
 
+    <select id="selectFsStoreOrderByIds" resultMap="FsStoreOrderResult">
+        <include refid="selectFsStoreOrderVo"/>
+        where id IN
+        <foreach collection="orderIds" item="orderId" open="(" separator="," close=")">
+            #{orderId}
+        </foreach>
+    </select>
+
     <insert id="insertFsStoreOrder" parameterType="FsStoreOrderScrm" useGeneratedKeys="true" keyProperty="id">
         insert into fs_store_order_scrm
         <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -1735,6 +1743,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="maps.isAudit != null   ">
                 and o.is_audit = #{maps.isAudit}
             </if>
+            <if test="maps.companyUserIds != null  and maps.companyUserIds.size > 0">
+                and o.company_user_id in
+                <foreach collection="maps.companyUserIds" item="companyUserId" open="(" close=")" separator=",">
+                    #{companyUserId}
+                </foreach>
+            </if>
         </where>
         ${maps.params.dataScope}
 <!--        <if test="maps.productName != null and  maps.productName !=  ''   ">-->

+ 8 - 0
fs-service/src/main/resources/mapper/hisStore/FsStorePaymentScrmMapper.xml

@@ -234,4 +234,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{bankTransactionId}
         </foreach>
     </update>
+
+    <select id="selectByBankTransactionIds" resultMap="FsStorePaymentResult">
+        <include refid="selectFsStorePaymentVo"/>
+        where bank_transaction_id in
+        <foreach item="bankTransactionId" collection="bankTransactionIds" open="(" separator="," close=")">
+            #{bankTransactionId}
+        </foreach>
+    </select>
 </mapper>

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

@@ -136,8 +136,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                                             </if>
                                              <if test="companyId !=null">
                                                  company_id = #{companyId},
-                                                 products = null
+                                                 products = null,
                                              </if>
+                                                 store_id = null
                                          where package_id in
         <foreach item="packageId" collection="packageIds" open="(" separator="," close=")">
             #{packageId}

+ 909 - 127
fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml

@@ -64,87 +64,92 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       sp_latest.bank_transaction_id,
         CONCAT('store-',sp_latest.pay_code) as hfshh
       FROM
-      fs_store_order_scrm o
-      left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.order_id ORDER BY fsois.item_id ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
-      LEFT JOIN fs_user u ON o.user_id = u.user_id
-
-      LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
-      LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
-      left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
-
-
-      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
-        LEFT JOIN company c ON c.company_id = cu.company_id
-      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_order_id ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_order_id = o.id
-      AND sp_latest.rn = 1
-      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
-          WHERE  o.company_id IS NOT NULL
+      (
+        SELECT * FROM fs_store_order_scrm 
+        WHERE company_id IS NOT NULL AND is_del = 0 AND is_sys_del = 0
           <if test="maps.status != null and maps.status != ''">
-            AND o.status = #{maps.status}
+            AND status = #{maps.status}
           </if>
         <if test="maps.userId != null and maps.userId != ''">
-            AND o.company_user_id = #{maps.userId}
-        </if>
-
-        <if test="maps.productId != null and maps.productId != ''">
-            AND fspc.product_id = #{maps.productId}
+            AND company_user_id = #{maps.userId}
         </if>
           <if test="maps.orderCode != null and maps.orderCode != ''">
-            AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+            AND order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
           </if>
           <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
-            AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+            AND FIND_IN_SET(order_code, #{maps.orderCodeList})
           </if>
           <if test="maps.deliveryId != null and maps.deliveryId != ''">
-            AND o.delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
-          </if>
-        <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
-            AND sp_latest.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+            AND delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
         </if>
           <if test="maps.userPhone != null and maps.userPhone != ''">
-            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+            AND user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
           </if>
         <if test="maps.userAddress != null and maps.userAddress != ''">
-            AND o.user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+            AND user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
         </if>
           <if test="maps.realName != null and maps.realName != ''">
-            AND o.real_name LIKE CONCAT('%', #{maps.realName}, '%')
-          </if>
-          <if test="maps.productName != null and maps.productName != ''">
-            AND fspc.product_name LIKE CONCAT('%', #{maps.productName}, '%')
+            AND real_name LIKE CONCAT('%', #{maps.realName}, '%')
           </if>
           <if test="maps.deliveryStatus != null">
-            AND o.delivery_status = #{maps.deliveryStatus}
+            AND delivery_status = #{maps.deliveryStatus}
           </if>
           <if test="maps.deliveryPayStatus != null">
-            AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+            AND delivery_pay_status = #{maps.deliveryPayStatus}
           </if>
           <if test="maps.payType != null and maps.payType != ''">
-            AND o.pay_type = #{maps.payType}
+            AND pay_type = #{maps.payType}
           </if>
           <if test="maps.companyId != null">
-            AND o.company_id = #{maps.companyId}
+            AND company_id = #{maps.companyId}
           </if>
           <if test="maps.deptId != null">
-            AND o.dept_id = #{maps.deptId}
-          </if>
-          <if test="maps.salesName != null and maps.salesName != ''">
-            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
-          </if>
-          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
-            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+            AND dept_id = #{maps.deptId}
           </if>
           <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
-            AND o.create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+            AND create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
           </if>
           <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
-            AND o.pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+            AND pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
           </if>
           <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
-            AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+            AND DATE(delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
           </if>
           <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
-            AND DATE(o.delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+            AND DATE(delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+          </if>
+        ORDER BY create_time DESC
+        limit 1000
+      ) o
+      left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.order_id ORDER BY fsois.item_id ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
+      LEFT JOIN fs_user u ON o.user_id = u.user_id
+
+      LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
+      LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+      left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+
+
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+        LEFT JOIN company c ON c.company_id = cu.company_id
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_order_id ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_order_id = o.id
+      AND sp_latest.rn = 1
+      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+          WHERE 1=1
+        <!-- 需要 JOIN 后才能过滤的条件 -->
+        <if test="maps.productId != null and maps.productId != ''">
+            AND fspc.product_id = #{maps.productId}
+        </if>
+        <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
+            AND sp_latest.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+        </if>
+          <if test="maps.productName != null and maps.productName != ''">
+            AND fspc.product_name LIKE CONCAT('%', #{maps.productName}, '%')
+        </if>
+          <if test="maps.salesName != null and maps.salesName != ''">
+            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+          </if>
+          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
           </if>
           <if test="maps.appId != null and maps.appId != ''">
             AND csc.appid = #{maps.appId}
@@ -154,7 +159,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </if>
           group by o.id
           UNION ALL
-          -- 商城订单(没有company_user_id的商城订单)
+
       SELECT
       o.id,
       '商城订单' AS order_type_name,
@@ -213,86 +218,92 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       sp_latest.bank_transaction_id,
         CONCAT('store-',sp_latest.pay_code) as hfshh
         FROM
-      fs_store_order_scrm o
-        left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.order_id ORDER BY fsois.item_id ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
-      LEFT JOIN fs_user u ON o.user_id = u.user_id
-
-        LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
-        LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
-        left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
-
-
-      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
-        LEFT JOIN company c ON c.company_id = cu.company_id
-      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_order_id ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_order_id = o.id
-      AND sp_latest.rn = 1
-      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
-          WHERE  o.company_id is null
+      (
+        SELECT * FROM fs_store_order_scrm 
+        WHERE company_id IS NULL AND is_del = 0 AND is_sys_del = 0
           <if test="maps.status != null and maps.status != ''">
-            AND o.status = #{maps.status}
+            AND status = #{maps.status}
           </if>
         <if test="maps.userId != null and maps.userId != ''">
-            AND o.company_user_id = #{maps.userId}
-        </if>
-        <if test="maps.productId != null and maps.productId != ''">
-            AND fspc.product_id = #{maps.productId}
+            AND company_user_id = #{maps.userId}
         </if>
           <if test="maps.orderCode != null and maps.orderCode != ''">
-            AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+            AND order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
           </if>
           <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
-            AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+            AND FIND_IN_SET(order_code, #{maps.orderCodeList})
           </if>
           <if test="maps.deliveryId != null and maps.deliveryId != ''">
-            AND o.delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
-          </if>
-          <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
-            AND sp_latest.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+            AND delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
           </if>
           <if test="maps.userPhone != null and maps.userPhone != ''">
-            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+            AND user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
           </if>
         <if test="maps.userAddress != null and maps.userAddress != ''">
-            AND o.user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+            AND user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
         </if>
           <if test="maps.realName != null and maps.realName != ''">
-            AND o.real_name LIKE CONCAT('%', #{maps.realName}, '%')
-          </if>
-          <if test="maps.productName != null and maps.productName != ''">
-            AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
+            AND real_name LIKE CONCAT('%', #{maps.realName}, '%')
           </if>
           <if test="maps.deliveryStatus != null">
-            AND o.delivery_status = #{maps.deliveryStatus}
+            AND delivery_status = #{maps.deliveryStatus}
           </if>
           <if test="maps.deliveryPayStatus != null">
-            AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+            AND delivery_pay_status = #{maps.deliveryPayStatus}
           </if>
           <if test="maps.payType != null and maps.payType != ''">
-            AND o.pay_type = #{maps.payType}
+            AND pay_type = #{maps.payType}
           </if>
           <if test="maps.companyId != null">
-            AND o.company_id = #{maps.companyId}
+            AND company_id = #{maps.companyId}
           </if>
           <if test="maps.deptId != null">
-            AND o.dept_id = #{maps.deptId}
-          </if>
-          <if test="maps.salesName != null and maps.salesName != ''">
-            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
-          </if>
-          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
-            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+            AND dept_id = #{maps.deptId}
           </if>
           <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
-              AND o.create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+            AND create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
           </if>
         <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
-            AND o.pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+            AND pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
           </if>
           <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
-            AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+            AND DATE(delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
           </if>
           <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
-            AND DATE(o.delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+            AND DATE(delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+          </if>
+        ORDER BY create_time DESC
+      limit 1000
+      ) o
+        left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.order_id ORDER BY fsois.item_id ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
+      LEFT JOIN fs_user u ON o.user_id = u.user_id
+
+        LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
+        LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+        left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+
+
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+        LEFT JOIN company c ON c.company_id = cu.company_id
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_order_id ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_order_id = o.id
+      AND sp_latest.rn = 1
+      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+          WHERE 1=1
+        <!-- 需要 JOIN 后才能过滤的条件 -->
+        <if test="maps.productId != null and maps.productId != ''">
+            AND fspc.product_id = #{maps.productId}
+        </if>
+          <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
+            AND sp_latest.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+          </if>
+          <if test="maps.productName != null and maps.productName != ''">
+            AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
+        </if>
+          <if test="maps.salesName != null and maps.salesName != ''">
+            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+          </if>
+          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
           </if>
           <if test="maps.appId != null and maps.appId != ''">
             AND csc.appid = #{maps.appId}
@@ -361,7 +372,60 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       sp_latest.bank_transaction_id,
         CONCAT('live-',sp_latest.pay_code) as hfshh
       FROM
-      live_order o
+      (
+        SELECT * FROM live_order 
+        WHERE is_del = 0
+          <if test="maps.status != null and maps.status != ''">
+            AND status = #{maps.status}
+          </if>
+          <if test="maps.userId != null and maps.userId != ''">
+            AND company_user_id = #{maps.userId}
+          </if>
+          <if test="maps.orderCode != null and maps.orderCode != ''">
+            AND order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+          </if>
+          <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+            AND FIND_IN_SET(order_code, #{maps.orderCodeList})
+          </if>
+          <if test="maps.deliveryId != null and maps.deliveryId != ''">
+            AND delivery_sn LIKE CONCAT('%', #{maps.deliveryId}, '%')
+          </if>
+          <if test="maps.userPhone != null and maps.userPhone != ''">
+            AND user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+          </if>
+          <if test="maps.userAddress != null and maps.userAddress != ''">
+            AND user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+          </if>
+          <if test="maps.realName != null and maps.realName != ''">
+            AND user_name LIKE CONCAT('%', #{maps.realName}, '%')
+          </if>
+          <if test="maps.deliveryStatus != null">
+            AND delivery_status = #{maps.deliveryStatus}
+          </if>
+          <if test="maps.deliveryPayStatus != null">
+            AND delivery_pay_status = #{maps.deliveryPayStatus}
+          </if>
+          <if test="maps.payType != null and maps.payType != ''">
+            AND pay_type = #{maps.payType}
+          </if>
+          <if test="maps.companyId != null">
+            AND company_id = #{maps.companyId}
+          </if>
+          <if test="maps.deptId != null">
+            AND dept_id = #{maps.deptId}
+          </if>
+          <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+            AND create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+          </if>
+          <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+            AND pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+            AND DATE(delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+          </if>
+        ORDER BY create_time DESC
+      limit 1000
+      ) o
       left join live_order_item loi on loi.order_id = o.order_id
       LEFT JOIN fs_user u ON o.user_id = u.user_id
 
@@ -378,80 +442,509 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_id ORDER BY sp.create_time DESC ) AS rn FROM live_order_payment sp ) sp_latest ON sp_latest.business_id = o.order_id
       AND sp_latest.rn = 1
       LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
-          WHERE o.is_del = 0
+          WHERE 1=1
+        <!-- 需要 JOIN 后才能过滤的条件 -->
+        <if test="maps.productId != null and maps.productId != ''">
+            AND fspc.product_id = #{maps.productId}
+        </if>
+        <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
+            AND sp_latest.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+        </if>
+          <if test="maps.productName != null and maps.productName != ''">
+            AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
+          </if>
+          <if test="maps.salesName != null and maps.salesName != ''">
+            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+          </if>
+          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+          </if>
+          <if test="maps.appId != null and maps.appId != ''">
+            AND csc.appid = #{maps.appId}
+          </if>
+        <if test="maps.hfshh != null and maps.hfshh != ''">
+            AND CONCAT('live-',sp_latest.pay_code) = #{maps.hfshh}
+        </if>
+        group by o.order_id
+        ) AS merged_orders
+        WHERE 1=1
+        <if test="maps.orderTypeFilter != null">
+          AND order_type = #{maps.orderTypeFilter}
+        </if>
+        <if test="maps.orderTypeFilter == null">
+          <!-- 如果 OrderTypeFilter 为空,只查询销售和直播订单,不查询商城订单 -->
+          AND order_type != 2
+        </if>
+        ORDER BY create_time DESC
+        <if test="maps.pageSize != null and maps.pageSize > 0">
+          LIMIT #{maps.pageSize}
+          <if test="maps.offset != null and maps.offset >= 0">
+            OFFSET #{maps.offset}
+          </if>
+        </if>
+    </select>
+
+  <!-- 查询销售订单列表(只查询销售订单,order_type = 1) -->
+  <select id="selectSalesOrderList" parameterType="com.fs.live.param.MergedOrderQueryParam" resultType="com.fs.live.vo.MergedOrderVO">
+    SELECT
+    o.id,
+    '销售订单' AS order_type_name,
+    NULL AS order_id,
+    NULL AS live_id,
+    NULL AS after_sales_id,
+    o.order_code,
+    o.pay_price,
+    o.pay_money,
+    o.STATUS,
+    o.is_package,
+    o.package_json,
+    item_latest.json_info as item_json,
+    o.delivery_id,
+    o.delivery_sn as deliveryCode,
+    o.delivery_name as deliveryName,
+    o.finish_time,
+    o.create_time,
+    o.pay_time,
+    o.delivery_send_time,
+    o.total_num AS total_num,
+    o.deduction_price AS discount_money,
+    1 AS order_type,
+    cu.phonenumber as salesPhone,
+    cu.create_time as salesCreateTime,
+    o.user_id as userId,
+    u.order_count as userOrderCount,
+    u.total_amount as userTotalAmount,
+    u.level as userLevel,
+    fspc.product_id as productId,
+    fspc.product_name as productName,
+    fspc.prescribe_spec as productSpec,
+    COALESCE(fspc.cost, 0) as cost,
+    o.pay_postage as payDelivery,
+    o.coupon_price as discountMoney,
+    fss.store_id as storeId,
+    fss.store_name as storeName,
+    fspcs.cate_name as cateName,
+    GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(item_latest.json_info, '$.barCode')) SEPARATOR ',') AS barCode,
+    c.company_name,
+    cu.user_name AS sales_name,
+    cu.nick_name AS company_user_nick_name,
+    ifnull(u.nickname,u.nick_name) as nickname,
+    u.phone,
+    o.real_name,
+    o.user_phone,
+    o.user_address,
+    o.pay_type,
+    o.delivery_status,
+    o.delivery_pay_status,
+    o.total_price,
+    csc.NAME AS mini_program_name,
+    sp_latest.bank_transaction_id,
+    CONCAT('store-',sp_latest.pay_code) as hfshh
+    FROM
+    (
+      SELECT * FROM fs_store_order_scrm 
+      WHERE company_id IS NOT NULL AND is_del = 0 AND is_sys_del = 0
           <if test="maps.status != null and maps.status != ''">
-            AND o.status = #{maps.status}
+        AND status = #{maps.status}
           </if>
         <if test="maps.userId != null and maps.userId != ''">
-            AND o.company_user_id = #{maps.userId}
-        </if>
-        <if test="maps.productId != null and maps.productId != ''">
-            AND fspc.product_id = #{maps.productId}
+        AND company_user_id = #{maps.userId}
         </if>
           <if test="maps.orderCode != null and maps.orderCode != ''">
-            AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+        AND order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
           </if>
           <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
-            AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+        AND FIND_IN_SET(order_code, #{maps.orderCodeList})
           </if>
           <if test="maps.deliveryId != null and maps.deliveryId != ''">
-            AND o.delivery_sn LIKE CONCAT('%', #{maps.deliveryId}, '%')
+        AND delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
+      </if>
+      <if test="maps.userPhone != null and maps.userPhone != ''">
+        AND user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+      </if>
+      <if test="maps.userAddress != null and maps.userAddress != ''">
+        AND user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+      </if>
+      <if test="maps.realName != null and maps.realName != ''">
+        AND real_name LIKE CONCAT('%', #{maps.realName}, '%')
+      </if>
+      <if test="maps.deliveryStatus != null">
+        AND delivery_status = #{maps.deliveryStatus}
+      </if>
+      <if test="maps.deliveryPayStatus != null">
+        AND delivery_pay_status = #{maps.deliveryPayStatus}
+      </if>
+      <if test="maps.payType != null and maps.payType != ''">
+        AND pay_type = #{maps.payType}
+      </if>
+      <if test="maps.companyId != null">
+        AND company_id = #{maps.companyId}
+      </if>
+      <if test="maps.deptId != null">
+        AND dept_id = #{maps.deptId}
+      </if>
+      <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+        AND create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+      </if>
+      <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+        AND pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+      </if>
+      <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+        AND DATE(delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+      </if>
+      <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
+        AND DATE(delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+      </if>
+      ORDER BY create_time DESC
+      <if test="maps.pageSize != null and maps.pageSize > 0">
+        LIMIT #{maps.pageSize}
+        <if test="maps.pageNum != null and maps.pageNum > 1">
+          OFFSET #{maps.offset}
+        </if>
+      </if>
+    ) o
+    LEFT JOIN ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.order_id ORDER BY fsois.item_id ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
+    LEFT JOIN fs_user u ON o.user_id = u.user_id
+    LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
+    LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+    LEFT JOIN fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+    LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+    LEFT JOIN company c ON c.company_id = cu.company_id
+    LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_order_id ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_order_id = o.id AND sp_latest.rn = 1
+    LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+    WHERE 1=1
+    <!-- 需要 JOIN 后才能过滤的条件 -->
+    <if test="maps.productId != null and maps.productId != ''">
+      AND fspc.product_id = #{maps.productId}
           </if>
         <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
             AND sp_latest.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+    </if>
+    <if test="maps.productName != null and maps.productName != ''">
+      AND fspc.product_name LIKE CONCAT('%', #{maps.productName}, '%')
+    </if>
+    <if test="maps.salesName != null and maps.salesName != ''">
+      AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+    </if>
+    <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+      AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+    </if>
+    <if test="maps.appId != null and maps.appId != ''">
+      AND csc.appid = #{maps.appId}
+    </if>
+    <if test="maps.hfshh != null and maps.hfshh != ''">
+      AND CONCAT('store-',sp_latest.pay_code) = #{maps.hfshh}
+    </if>
+    GROUP BY o.id
+    ORDER BY o.create_time DESC
+  </select>
+
+  <!-- 查询商城订单列表(只查询商城订单,order_type = 2) -->
+  <select id="selectStoreOrderList" parameterType="com.fs.live.param.MergedOrderQueryParam" resultType="com.fs.live.vo.MergedOrderVO">
+    SELECT
+    o.id,
+    '商城订单' AS order_type_name,
+    NULL AS order_id,
+    NULL AS live_id,
+    NULL AS after_sales_id,
+    o.order_code,
+    o.pay_price,
+    o.pay_money,
+    o.STATUS,
+    o.is_package,
+    o.package_json,
+    item_latest.json_info as item_json,
+    o.delivery_id,
+    o.delivery_sn as deliveryCode,
+    o.delivery_name as deliveryName,
+    o.finish_time,
+    o.create_time,
+    o.pay_time,
+    o.delivery_send_time,
+    o.total_num AS total_num,
+    o.deduction_price AS discount_money,
+    2 AS order_type,
+    cu.phonenumber as salesPhone,
+    cu.create_time as salesCreateTime,
+    o.user_id as userId,
+    u.order_count as userOrderCount,
+    u.total_amount as userTotalAmount,
+    u.level as userLevel,
+    fspc.product_id as productId,
+    fspc.product_name as productName,
+    fspc.prescribe_spec as productSpec,
+    COALESCE(fspc.cost, 0) as cost,
+    o.pay_postage as payDelivery,
+    o.coupon_price as discountMoney,
+    fss.store_id as storeId,
+    fss.store_name as storeName,
+    fspcs.cate_name as cateName,
+    GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(item_latest.json_info, '$.barCode')) SEPARATOR ',') AS barCode,
+    c.company_name,
+    cu.user_name AS sales_name,
+    cu.nick_name AS company_user_nick_name,
+    ifnull(u.nickname,u.nick_name) as nickname,
+    u.phone,
+    o.real_name,
+    o.user_phone,
+    o.user_address,
+    o.pay_type,
+    o.delivery_status,
+    o.delivery_pay_status,
+    o.total_price,
+    csc.NAME AS mini_program_name,
+    sp_latest.bank_transaction_id,
+    CONCAT('store-',sp_latest.pay_code) as hfshh
+    FROM
+    (
+      SELECT * FROM fs_store_order_scrm 
+      WHERE company_id IS NULL AND is_del = 0 AND is_sys_del = 0
+      <if test="maps.status != null and maps.status != ''">
+        AND status = #{maps.status}
+      </if>
+      <if test="maps.userId != null and maps.userId != ''">
+        AND company_user_id = #{maps.userId}
+      </if>
+      <if test="maps.orderCode != null and maps.orderCode != ''">
+        AND order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+      </if>
+      <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+        AND FIND_IN_SET(order_code, #{maps.orderCodeList})
+      </if>
+      <if test="maps.deliveryId != null and maps.deliveryId != ''">
+        AND delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
         </if>
           <if test="maps.userPhone != null and maps.userPhone != ''">
-            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+        AND user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
           </if>
         <if test="maps.userAddress != null and maps.userAddress != ''">
-            AND o.user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+        AND user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
           </if>
           <if test="maps.realName != null and maps.realName != ''">
-            AND o.user_name LIKE CONCAT('%', #{maps.realName}, '%')
-          </if>
-          <if test="maps.productName != null and maps.productName != ''">
-            AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
+        AND real_name LIKE CONCAT('%', #{maps.realName}, '%')
           </if>
           <if test="maps.deliveryStatus != null">
-            AND o.delivery_status = #{maps.deliveryStatus}
+        AND delivery_status = #{maps.deliveryStatus}
           </if>
           <if test="maps.deliveryPayStatus != null">
-            AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+        AND delivery_pay_status = #{maps.deliveryPayStatus}
           </if>
           <if test="maps.payType != null and maps.payType != ''">
-            AND o.pay_type = #{maps.payType}
+        AND pay_type = #{maps.payType}
           </if>
           <if test="maps.companyId != null">
-            AND o.company_id = #{maps.companyId}
+        AND company_id = #{maps.companyId}
           </if>
           <if test="maps.deptId != null">
-            AND o.dept_id = #{maps.deptId}
+        AND dept_id = #{maps.deptId}
+      </if>
+      <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+        AND create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+      </if>
+      <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+        AND pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+      </if>
+      <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+        AND DATE(delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+      </if>
+      <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
+        AND DATE(delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+      </if>
+      ORDER BY create_time DESC
+      <if test="maps.pageSize != null and maps.pageSize > 0">
+        LIMIT #{maps.pageSize}
+        <if test="maps.pageNum != null and maps.pageNum > 1">
+          OFFSET #{maps.offset}
+        </if>
+      </if>
+    ) o
+    LEFT JOIN ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.order_id ORDER BY fsois.item_id ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
+    LEFT JOIN fs_user u ON o.user_id = u.user_id
+    LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
+    LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+    LEFT JOIN fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+    LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+    LEFT JOIN company c ON c.company_id = cu.company_id
+    LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_order_id ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_order_id = o.id AND sp_latest.rn = 1
+    LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+    WHERE 1=1
+    <!-- 需要 JOIN 后才能过滤的条件 -->
+    <if test="maps.productId != null and maps.productId != ''">
+      AND fspc.product_id = #{maps.productId}
+    </if>
+    <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
+      AND sp_latest.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+    </if>
+    <if test="maps.productName != null and maps.productName != ''">
+      AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
           </if>
           <if test="maps.salesName != null and maps.salesName != ''">
             AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
           </if>
           <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
             AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+    </if>
+    <if test="maps.appId != null and maps.appId != ''">
+      AND csc.appid = #{maps.appId}
+    </if>
+    <if test="maps.hfshh != null and maps.hfshh != ''">
+      AND CONCAT('store-',sp_latest.pay_code) = #{maps.hfshh}
+    </if>
+    GROUP BY o.id
+    ORDER BY o.create_time DESC
+  </select>
+
+  <!-- 查询直播订单列表(只查询直播订单,order_type = 3) -->
+  <select id="selectLiveOrderList" parameterType="com.fs.live.param.MergedOrderQueryParam" resultType="com.fs.live.vo.MergedOrderVO">
+    SELECT
+    NULL AS id,
+    '直播订单' AS order_type_name,
+    o.order_id,
+    o.live_id,
+    a.id AS after_sales_id,
+    o.order_code,
+    o.pay_price,
+    o.pay_money,
+    o.STATUS,
+    NULL AS is_package,
+    NULL AS package_json,
+    loi.json_info as item_json,
+    o.delivery_sn AS delivery_id,
+    o.delivery_code as deliveryCode,
+    o.delivery_name as deliveryName,
+    o.finish_time,
+    o.create_time,
+    o.pay_time,
+    o.delivery_send_time,
+    o.total_num,
+    o.discount_money,
+    3 AS order_type,
+    cu.phonenumber as salesPhone,
+    cu.create_time as salesCreateTime,
+    o.user_id as userId,
+    u.order_count as userOrderCount,
+    u.total_amount as userTotalAmount,
+    u.level as userLevel,
+    fspc.product_id as productId,
+    fspc.product_name as productName,
+    fspc.prescribe_spec as productSpec,
+    COALESCE(fspc.cost, 0) as cost,
+    o.pay_postage as payDelivery,
+    o.discount_money as discountMoney,
+    fss.store_id as storeId,
+    fss.store_name as storeName,
+    fspcs.cate_name as cateName,
+    GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(loi.json_info, '$.barCode')) SEPARATOR ',') AS barCode,
+    c.company_name,
+    cu.user_name AS sales_name,
+    cu.nick_name AS company_user_nick_name,
+    ifnull(u.nickname,u.nick_name) as nickname,
+    u.phone,
+    o.user_name AS real_name,
+    o.user_phone,
+    o.user_address,
+    o.pay_type,
+    o.delivery_status,
+    o.delivery_pay_status,
+    o.total_price,
+    csc.NAME AS mini_program_name,
+    sp_latest.bank_transaction_id,
+    CONCAT('live-',sp_latest.pay_code) as hfshh
+    FROM
+    (
+      SELECT * FROM live_order 
+      WHERE is_del = 0
+      <if test="maps.status != null and maps.status != ''">
+        AND status = #{maps.status}
+      </if>
+      <if test="maps.userId != null and maps.userId != ''">
+        AND company_user_id = #{maps.userId}
+      </if>
+      <if test="maps.orderCode != null and maps.orderCode != ''">
+        AND order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+      </if>
+      <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+        AND FIND_IN_SET(order_code, #{maps.orderCodeList})
+      </if>
+      <if test="maps.deliveryId != null and maps.deliveryId != ''">
+        AND delivery_sn LIKE CONCAT('%', #{maps.deliveryId}, '%')
+      </if>
+      <if test="maps.userPhone != null and maps.userPhone != ''">
+        AND user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+      </if>
+      <if test="maps.userAddress != null and maps.userAddress != ''">
+        AND user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+      </if>
+      <if test="maps.realName != null and maps.realName != ''">
+        AND user_name LIKE CONCAT('%', #{maps.realName}, '%')
+      </if>
+      <if test="maps.deliveryStatus != null">
+        AND delivery_status = #{maps.deliveryStatus}
+      </if>
+      <if test="maps.deliveryPayStatus != null">
+        AND delivery_pay_status = #{maps.deliveryPayStatus}
+      </if>
+      <if test="maps.payType != null and maps.payType != ''">
+        AND pay_type = #{maps.payType}
+      </if>
+      <if test="maps.companyId != null">
+        AND company_id = #{maps.companyId}
+      </if>
+      <if test="maps.deptId != null">
+        AND dept_id = #{maps.deptId}
           </if>
           <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
-              AND o.create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+        AND create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
           </if>
         <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
-            AND o.pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+        AND pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
           </if>
           <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
-            AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+        AND DATE(delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
           </if>
-        <if test="maps.hfshh != null and maps.hfshh != ''">
-            AND CONCAT('store-',sp_latest.pay_code) = #{maps.hfshh}
+      ORDER BY create_time DESC
+      <if test="maps.pageSize != null and maps.pageSize > 0">
+        LIMIT #{maps.pageSize}
+        <if test="maps.pageNum != null and maps.pageNum > 1">
+          OFFSET #{maps.offset}
         </if>
-        group by o.order_id
-        ) AS merged_orders
+      </if>
+    ) o
+    LEFT JOIN live_order_item loi on loi.order_id = o.order_id
+    LEFT JOIN fs_user u ON o.user_id = u.user_id
+    LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = o.product_id
+    LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+    LEFT JOIN fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+    LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+    LEFT JOIN company c ON c.company_id = cu.company_id
+    LEFT JOIN ( SELECT t.*, ROW_NUMBER() OVER ( PARTITION BY t.order_id ORDER BY t.create_time DESC ) AS rn FROM live_after_sales t ) a ON o.order_id = a.order_id AND a.rn = 1
+    LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_id ORDER BY sp.create_time DESC ) AS rn FROM live_order_payment sp ) sp_latest ON sp_latest.business_id = CAST(o.order_id AS CHAR) AND sp_latest.rn = 1
+    LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
         WHERE 1=1
-        <if test="maps.orderTypeFilter != null">
-          AND order_type = #{maps.orderTypeFilter}
+    <!-- 需要 JOIN 后才能过滤的条件 -->
+    <if test="maps.productId != null and maps.productId != ''">
+      AND fspc.product_id = #{maps.productId}
         </if>
-        ORDER BY create_time DESC
+    <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
+      AND sp_latest.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+    </if>
+    <if test="maps.productName != null and maps.productName != ''">
+      AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
+    </if>
+    <if test="maps.salesName != null and maps.salesName != ''">
+      AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+    </if>
+    <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+      AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+    </if>
+    <if test="maps.appId != null and maps.appId != ''">
+      AND csc.appid = #{maps.appId}
+    </if>
+    <if test="maps.hfshh != null and maps.hfshh != ''">
+      AND CONCAT('live-',sp_latest.pay_code) = #{maps.hfshh}
+    </if>
+    GROUP BY o.order_id
+    ORDER BY o.create_time DESC
     </select>
 
     <!-- 查询合并的售后列表(商城售后+直播售后) -->
@@ -535,5 +1028,294 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         ORDER BY createTime DESC
     </select>
 
+  <!-- 统计合并订单总数 -->
+  <select id="countMergedOrderList" parameterType="com.fs.live.param.MergedOrderQueryParam" resultType="long">
+    SELECT 
+      <choose>
+        <when test="maps.orderTypeFilter != null and maps.orderTypeFilter == 1">
+          <!-- 只统计销售订单 -->
+          (SELECT COUNT(DISTINCT o.id)
+           FROM fs_store_order_scrm o
+           WHERE o.company_id IS NOT NULL AND o.is_del = 0 AND o.is_sys_del = 0
+             <if test="maps.status != null and maps.status != ''">
+               AND o.status = #{maps.status}
+             </if>
+             <if test="maps.userId != null and maps.userId != ''">
+               AND o.company_user_id = #{maps.userId}
+             </if>
+             <if test="maps.orderCode != null and maps.orderCode != ''">
+               AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+             </if>
+             <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+               AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+             </if>
+             <if test="maps.deliveryId != null and maps.deliveryId != ''">
+               AND o.delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
+             </if>
+             <if test="maps.userPhone != null and maps.userPhone != ''">
+               AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+             </if>
+             <if test="maps.userAddress != null and maps.userAddress != ''">
+               AND o.user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+             </if>
+             <if test="maps.realName != null and maps.realName != ''">
+               AND o.real_name LIKE CONCAT('%', #{maps.realName}, '%')
+             </if>
+             <if test="maps.deliveryStatus != null">
+               AND o.delivery_status = #{maps.deliveryStatus}
+             </if>
+             <if test="maps.deliveryPayStatus != null">
+               AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+             </if>
+             <if test="maps.payType != null and maps.payType != ''">
+               AND o.pay_type = #{maps.payType}
+             </if>
+             <if test="maps.companyId != null">
+               AND o.company_id = #{maps.companyId}
+             </if>
+             <if test="maps.deptId != null">
+               AND o.dept_id = #{maps.deptId}
+             </if>
+             <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+               AND o.create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+             </if>
+             <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+               AND o.pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+             </if>
+             <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+               AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+             </if>
+             <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
+               AND DATE(o.delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+             </if>
+          )
+        </when>
+        <when test="maps.orderTypeFilter != null and maps.orderTypeFilter == 2">
+          <!-- 只统计商城订单 -->
+          (SELECT COUNT(DISTINCT o.id)
+           FROM fs_store_order_scrm o
+           WHERE o.company_id IS NULL AND o.is_del = 0 AND o.is_sys_del = 0
+             <if test="maps.status != null and maps.status != ''">
+               AND o.status = #{maps.status}
+             </if>
+             <if test="maps.userId != null and maps.userId != ''">
+               AND o.company_user_id = #{maps.userId}
+             </if>
+             <if test="maps.orderCode != null and maps.orderCode != ''">
+               AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+             </if>
+             <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+               AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+             </if>
+             <if test="maps.deliveryId != null and maps.deliveryId != ''">
+               AND o.delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
+             </if>
+             <if test="maps.userPhone != null and maps.userPhone != ''">
+               AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+             </if>
+             <if test="maps.userAddress != null and maps.userAddress != ''">
+               AND o.user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+             </if>
+             <if test="maps.realName != null and maps.realName != ''">
+               AND o.real_name LIKE CONCAT('%', #{maps.realName}, '%')
+             </if>
+             <if test="maps.deliveryStatus != null">
+               AND o.delivery_status = #{maps.deliveryStatus}
+             </if>
+             <if test="maps.deliveryPayStatus != null">
+               AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+             </if>
+             <if test="maps.payType != null and maps.payType != ''">
+               AND o.pay_type = #{maps.payType}
+             </if>
+             <if test="maps.companyId != null">
+               AND o.company_id = #{maps.companyId}
+             </if>
+             <if test="maps.deptId != null">
+               AND o.dept_id = #{maps.deptId}
+             </if>
+             <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+               AND o.create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+             </if>
+             <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+               AND o.pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+             </if>
+             <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+               AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+             </if>
+             <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
+               AND DATE(o.delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+             </if>
+          )
+        </when>
+        <when test="maps.orderTypeFilter != null and maps.orderTypeFilter == 3">
+          <!-- 只统计直播订单 -->
+          (SELECT COUNT(DISTINCT o.order_id)
+           FROM live_order o
+           WHERE o.is_del = 0
+             <if test="maps.status != null and maps.status != ''">
+               AND o.status = #{maps.status}
+             </if>
+             <if test="maps.userId != null and maps.userId != ''">
+               AND o.company_user_id = #{maps.userId}
+             </if>
+             <if test="maps.orderCode != null and maps.orderCode != ''">
+               AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+             </if>
+             <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+               AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+             </if>
+             <if test="maps.deliveryId != null and maps.deliveryId != ''">
+               AND o.delivery_sn LIKE CONCAT('%', #{maps.deliveryId}, '%')
+             </if>
+             <if test="maps.userPhone != null and maps.userPhone != ''">
+               AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+             </if>
+             <if test="maps.userAddress != null and maps.userAddress != ''">
+               AND o.user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+             </if>
+             <if test="maps.realName != null and maps.realName != ''">
+               AND o.user_name LIKE CONCAT('%', #{maps.realName}, '%')
+             </if>
+             <if test="maps.deliveryStatus != null">
+               AND o.delivery_status = #{maps.deliveryStatus}
+             </if>
+             <if test="maps.deliveryPayStatus != null">
+               AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+             </if>
+             <if test="maps.payType != null and maps.payType != ''">
+               AND o.pay_type = #{maps.payType}
+             </if>
+             <if test="maps.companyId != null">
+               AND o.company_id = #{maps.companyId}
+             </if>
+             <if test="maps.deptId != null">
+               AND o.dept_id = #{maps.deptId}
+             </if>
+             <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+               AND o.create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+             </if>
+             <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+               AND o.pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+             </if>
+             <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+               AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+             </if>
+          )
+        </when>
+        <otherwise>
+          <!-- 如果 OrderTypeFilter 为空,只统计销售和直播订单,不统计商城订单 -->
+          (SELECT COUNT(DISTINCT o.id)
+           FROM fs_store_order_scrm o
+           WHERE o.company_id IS NOT NULL AND o.is_del = 0 AND o.is_sys_del = 0
+             <if test="maps.status != null and maps.status != ''">
+               AND o.status = #{maps.status}
+             </if>
+             <if test="maps.userId != null and maps.userId != ''">
+               AND o.company_user_id = #{maps.userId}
+             </if>
+             <if test="maps.orderCode != null and maps.orderCode != ''">
+               AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+             </if>
+             <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+               AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+             </if>
+             <if test="maps.deliveryId != null and maps.deliveryId != ''">
+               AND o.delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
+             </if>
+             <if test="maps.userPhone != null and maps.userPhone != ''">
+               AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+             </if>
+             <if test="maps.userAddress != null and maps.userAddress != ''">
+               AND o.user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+             </if>
+             <if test="maps.realName != null and maps.realName != ''">
+               AND o.real_name LIKE CONCAT('%', #{maps.realName}, '%')
+             </if>
+             <if test="maps.deliveryStatus != null">
+               AND o.delivery_status = #{maps.deliveryStatus}
+             </if>
+             <if test="maps.deliveryPayStatus != null">
+               AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+             </if>
+             <if test="maps.payType != null and maps.payType != ''">
+               AND o.pay_type = #{maps.payType}
+             </if>
+             <if test="maps.companyId != null">
+               AND o.company_id = #{maps.companyId}
+             </if>
+             <if test="maps.deptId != null">
+               AND o.dept_id = #{maps.deptId}
+             </if>
+             <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+               AND o.create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+             </if>
+             <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+               AND o.pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+             </if>
+             <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+               AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+             </if>
+             <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
+               AND DATE(o.delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+             </if>
+          ) +
+          (SELECT COUNT(DISTINCT o.order_id)
+           FROM live_order o
+           WHERE o.is_del = 0
+             <if test="maps.status != null and maps.status != ''">
+               AND o.status = #{maps.status}
+             </if>
+             <if test="maps.userId != null and maps.userId != ''">
+               AND o.company_user_id = #{maps.userId}
+             </if>
+             <if test="maps.orderCode != null and maps.orderCode != ''">
+               AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+             </if>
+             <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+               AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+             </if>
+             <if test="maps.deliveryId != null and maps.deliveryId != ''">
+               AND o.delivery_sn LIKE CONCAT('%', #{maps.deliveryId}, '%')
+             </if>
+             <if test="maps.userPhone != null and maps.userPhone != ''">
+               AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+             </if>
+             <if test="maps.userAddress != null and maps.userAddress != ''">
+               AND o.user_address LIKE CONCAT('%', #{maps.userAddress}, '%')
+             </if>
+             <if test="maps.realName != null and maps.realName != ''">
+               AND o.user_name LIKE CONCAT('%', #{maps.realName}, '%')
+             </if>
+             <if test="maps.deliveryStatus != null">
+               AND o.delivery_status = #{maps.deliveryStatus}
+             </if>
+             <if test="maps.deliveryPayStatus != null">
+               AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+             </if>
+             <if test="maps.payType != null and maps.payType != ''">
+               AND o.pay_type = #{maps.payType}
+             </if>
+             <if test="maps.companyId != null">
+               AND o.company_id = #{maps.companyId}
+             </if>
+             <if test="maps.deptId != null">
+               AND o.dept_id = #{maps.deptId}
+             </if>
+             <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+               AND o.create_time BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+             </if>
+             <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+               AND o.pay_time BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+             </if>
+             <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+               AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+             </if>
+          )
+        </otherwise>
+      </choose>
+      AS total_count
+  </select>
+
 </mapper>
 

+ 2 - 2
fs-service/src/main/resources/mapper/live/LiveAfterSalesMapper.xml

@@ -90,7 +90,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="refundType != null "> and las.refund_type = #{refundType}</if>
             <if test="status != null "> and las.status = #{status}</if>
             <if test="salesStatus != null "> and las.sales_status = #{salesStatus}</if>
-            <if test="orderStatus != null "> and las.order_status = #{orderStatus}</if>
+            <if test="orderStatus != null "> and lo.status = #{orderStatus}</if>
             <if test="reasons != null  and reasons != ''"> and las.reasons = #{reasons}</if>
             <if test="explains != null  and explains != ''"> and las.explains = #{explains}</if>
             <if test="explainImg != null  and explainImg != ''"> and las.explain_img = #{explainImg}</if>
@@ -99,7 +99,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="deliveryName != null  and deliveryName != ''"> and las.delivery_name like concat('%', #{deliveryName}, '%')</if>
             <if test="status != null "> and las.status = #{status}</if>
             <if test="salesStatus != null "> and las.sales_status = #{salesStatus}</if>
-            <if test="orderStatus != null "> and las.order_status = #{orderStatus}</if>
+            <!--<if test="orderStatus != null "> and las.order_status = #{orderStatus}</if>-->
             <if test="deliveryStatus != null and deliveryStatus!= ''"> and las.order_status = #{deliveryStatus}</if>
             <if test="isDel != null  and isDel != ''"> and las.is_del = #{isDel}</if>
             <if test="userId != null "> and las.user_id = #{userId}</if>

+ 7 - 1
fs-service/src/main/resources/mapper/live/LiveCouponUserMapper.xml

@@ -27,7 +27,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </sql>
 
     <select id="selectLiveCouponUserList" parameterType="LiveCouponUser" resultMap="LiveCouponUserResult">
-        <include refid="selectLiveCouponUserVo"/>
+
+        select cou.id, cou.coupon_id, cou.user_id, cou.coupon_title, cou.coupon_price, cou.use_min_price, cou.create_time, cou.update_time, cou.limit_time, cou.use_time, cou.type, cou.status, cou.is_fail, cou.is_del,cou.goods_id,
+        u.nick_name,u.phone
+
+        from live_coupon_user as  cou
+         left join fs_user u on cou.user_id=u.user_id
+
         <where>
             <if test="couponId != null "> and coupon_id = #{couponId}</if>
             <if test="userId != null "> and user_id = #{userId}</if>

+ 8 - 0
fs-service/src/main/resources/mapper/live/LiveOrderMapper.xml

@@ -483,6 +483,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </foreach>
     </select>
 
+    <select id="selectLiveOrderByOrderIds" resultMap="LiveOrderResult">
+        <include refid="selectLiveOrderVo"/>
+        where order_id IN
+        <foreach collection="orderIds" item="orderId" open="(" separator="," close=")">
+            #{orderId}
+        </foreach>
+    </select>
+
     <select id="selectLiveOrderListVO" resultType="com.fs.live.vo.LiveOrderVO">
 
     select o.*,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

+ 8 - 0
fs-service/src/main/resources/mapper/live/LiveOrderPaymentMapper.xml

@@ -223,4 +223,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{paymentId}
         </foreach>
     </delete>
+
+    <select id="selectByBankTransactionIds" resultMap="LiveOrderPaymentResult">
+        <include refid="selectLiveOrderPaymentVo"/>
+        where bank_transaction_id in
+        <foreach item="bankTransactionId" collection="bankTransactionIds" open="(" separator="," close=")">
+            #{bankTransactionId}
+        </foreach>
+    </select>
 </mapper>

+ 4 - 0
fs-service/src/main/resources/mapper/live/LiveWatchLogMapper.xml

@@ -211,6 +211,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         select * from live_watch_log where live_id = #{liveId}
     </select>
 
+    <select id="selectLiveWatchLogByLiveIdAndExternalId" resultType="com.fs.live.domain.LiveWatchLog">
+        select * from live_watch_log where live_id = #{liveId} and external_contact_id = #{externalContactId} limit 1
+    </select>
+
     <select id="selectLiveWatchLogListInfo"   parameterType="LiveWatchLog" resultType="com.fs.live.vo.LiveWatchLogListVO">
         select
         t2.nick_name as userName,

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

@@ -48,6 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             w.*
         from qw_friend_welcome w
         <where>
+            <if test="id != null  and id != ''"> and w.id like concat( #{id}, '%') </if>
             <if test="qwUserIds != null  and qwUserIds != ''">
                 and FIND_IN_SET(#{qwUserIds},REPLACE(REPLACE(REPLACE(qw_user_ids, '[', ''), ']', ''), ' ', '')) > 0
             </if>

+ 31 - 0
fs-service/src/main/resources/mapper/qw/QwRestrictionPushRecordMapper.xml

@@ -41,4 +41,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </where>
         group by ft.company_id,ft.stat_time
     </select>
+    <select id="selectFastGptPushTokenTotalDeptList" resultType="com.fs.fastGpt.vo.FastGptPushTokenDeptTotalVO">
+        SELECT
+            ft.id,
+            ft.type,
+            ft.qw_user_id AS qwUserId,
+            ft.company_id AS companyId,
+            t2.company_name AS companyName,
+            t4.dept_id as deptId,
+            t4.dept_name as deptName,
+            ft.STATUS,
+            ft.stat_time statTime,
+            sum( ft.count ) count
+        FROM
+            fastgpt_push_token_total ft
+            left join qw_user t1 on t1.id = ft.qw_user_id
+            LEFT JOIN company_user t3 ON t3.user_id = t1.company_user_id
+            LEFT JOIN company t2 ON t2.company_id = ft.company_id
+            left join company_dept t4 on t4.dept_id = t3.dept_id
+        <where>
+            <if test="type != null" >and `ft.type` = #{type} </if>
+            <if test="qwUserId != null" >and ft.qw_user_id= #{qwUserId} </if>
+            <if test="companyId != null" >and ft.company_id= #{companyId} </if>
+            <if test="deptId != null" >and t4.dept_id= #{deptId} </if>
+            <if test="status != null" >and ft.status= #{status} </if>
+            <if test="beginTime != null and endTime != null" >
+                AND date_format(ft.stat_time,'%Y-%m-%d') &gt;= #{beginTime}
+                AND date_format(ft.stat_time,'%Y-%m-%d') &lt;= #{endTime}
+            </if>
+        </where>
+        group by t4.dept_id,ft.stat_time
+    </select>
 </mapper>

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

@@ -64,8 +64,8 @@
         select id,company_user_id companyUserId,voice_txt voiceTxt from qw_sop_temp_voice where id = #{id}
                                                                                             and voice_txt = #{voiceTxt} and record_type in (0,1) limit 1
     </select>
-    <select id="selectQwSopTempVoiceByIdAndUserVoiceUrl" resultType="com.fs.sop.domain.QwSopTempVoice">
-        select id,company_user_id companyUserId from qw_sop_temp_voice where id = #{id} and record_type in (0,1,2) limit 1
+    <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>
     <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,

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

@@ -364,7 +364,7 @@ public class InquiryOrderController extends  AppBaseController {
             storePayment.setUserId(user.getUserId());
             storePayment.setBusinessId(order.getOrderId().toString());
             if(storePaymentService.insertFsStorePayment(storePayment)>0){
-                if(merchantAppConfig.getMerchantId().equals("wx")){
+                if(merchantAppConfig.getMerchantType().equals("wx")){
                     //创建微信订单
                     WxPayConfig payConfig = new WxPayConfig();
                     payConfig.setAppId(appId);
@@ -392,7 +392,7 @@ public class InquiryOrderController extends  AppBaseController {
                         throw new CustomException("支付失败"+e.getMessage());
                     }
                 }
-                else if(merchantAppConfig.getMerchantId().equals("yb")){
+                else if(merchantAppConfig.getMerchantType().equals("yb")){
                     WxJspayDTO p = new WxJspayDTO();
                     // 使用setter方法为对象赋值
                     p.setPayMoney(storePayment.getPayMoney().toString());
@@ -414,7 +414,7 @@ public class InquiryOrderController extends  AppBaseController {
                         throw new CustomException("支付失败");
                     }
                 }
-                else if(merchantAppConfig.getMerchantId().equals("tz")){
+                else if(merchantAppConfig.getMerchantType().equals("tz")){
                     PayCreateOrder o = new PayCreateOrder();
                     o.setOrderNo("inquiry"+storePayment.getPayCode()); // 业务系统订单号
                     o.setTrxAmt(storePayment.getPayMoney().doubleValue()); // 交易金额
@@ -439,7 +439,7 @@ public class InquiryOrderController extends  AppBaseController {
                     mt.setTradeNo(result.getBody().getOrderFlowNo());
                     storePaymentService.updateFsStorePayment(mt);
                     return R.ok().put("isPay",0).put("data",result).put("type","tz");
-                }else if(merchantAppConfig.getMerchantId().equals("hf")){
+                }else if(merchantAppConfig.getMerchantType().equals("hf")){
                     HuiFuCreateOrder o = new HuiFuCreateOrder();
                     o.setTradeType("T_MINIAPP");
                     o.setOpenid(openId);

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác