Browse Source

Merge remote-tracking branch 'origin/master'

zyp 2 days ago
parent
commit
a75efc2e5b
95 changed files with 3263 additions and 581 deletions
  1. 24 0
      fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java
  2. 42 13
      fs-admin/src/main/java/com/fs/course/controller/FsCourseRedPacketLogController.java
  3. 50 10
      fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchLogController.java
  4. 27 0
      fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  5. 6 6
      fs-admin/src/main/java/com/fs/his/controller/FsAdvController.java
  6. 107 0
      fs-admin/src/main/java/com/fs/his/controller/MerchantAppConfigController.java
  7. 5 0
      fs-admin/src/main/java/com/fs/his/task/Task.java
  8. 9 9
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreScrmController.java
  9. 55 0
      fs-admin/src/main/java/com/fs/live/controller/OrderController.java
  10. 25 1
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java
  11. 10 2
      fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java
  12. 31 4
      fs-company/src/main/java/com/fs/framework/service/CompanyLoginService.java
  13. 15 4
      fs-framework/src/main/java/com/fs/framework/web/service/SysLoginService.java
  14. 1 1
      fs-live-app/src/main/java/com/fs/live/controller/LiveController.java
  15. 18 2
      fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java
  16. 5 4
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  17. 5 0
      fs-service/src/main/java/com/fs/course/domain/FsCoursePlaySourceConfig.java
  18. 2 0
      fs-service/src/main/java/com/fs/course/mapper/FsCoursePlaySourceConfigMapper.java
  19. 7 7
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java
  20. 6 2
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  21. 3 0
      fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigEditParam.java
  22. 29 24
      fs-service/src/main/java/com/fs/course/param/FsCourseRedPacketLogParam.java
  23. 18 20
      fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java
  24. 5 0
      fs-service/src/main/java/com/fs/course/param/PeriodStatisticCountParam.java
  25. 7 0
      fs-service/src/main/java/com/fs/course/service/IFsCoursePlaySourceConfigService.java
  26. 5 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCoursePlaySourceConfigServiceImpl.java
  27. 19 5
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java
  28. 19 6
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  29. 13 6
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java
  30. 12 4
      fs-service/src/main/java/com/fs/course/vo/FsCourseRedPacketLogListPVO.java
  31. 5 2
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogListVO.java
  32. 8 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListVO.java
  33. 5 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoDetailsVO.java
  34. 1 1
      fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java
  35. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsPayConfig.java
  36. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsUser.java
  37. 66 0
      fs-service/src/main/java/com/fs/his/domain/MerchantAppConfig.java
  38. 6 0
      fs-service/src/main/java/com/fs/his/mapper/FsIntegralOrderMapper.java
  39. 2 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  40. 74 0
      fs-service/src/main/java/com/fs/his/mapper/MerchantAppConfigMapper.java
  41. 2 0
      fs-service/src/main/java/com/fs/his/service/IFsUserService.java
  42. 61 0
      fs-service/src/main/java/com/fs/his/service/IMerchantAppConfigService.java
  43. 8 2
      fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderServiceImpl.java
  44. 6 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  45. 200 0
      fs-service/src/main/java/com/fs/his/service/impl/MerchantAppConfigServiceImpl.java
  46. 2 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsPayConfigScrm.java
  47. 17 17
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java
  48. 108 0
      fs-service/src/main/java/com/fs/hisStore/mapper/MergedOrderMapper.java
  49. 28 0
      fs-service/src/main/java/com/fs/hisStore/service/IMergedOrderService.java
  50. 4 4
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  51. 26 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  52. 6 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserAddressScrmServiceImpl.java
  53. 134 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java
  54. 78 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsMergedOrderListQueryVO.java
  55. 37 0
      fs-service/src/main/java/com/fs/live/enums/LiveGoodsAddErrorEnum.java
  56. 85 0
      fs-service/src/main/java/com/fs/live/param/MergedOrderQueryParam.java
  57. 96 2
      fs-service/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java
  58. 34 3
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  59. 1 1
      fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java
  60. 2 2
      fs-service/src/main/java/com/fs/live/service/impl/LiveWatchConfigServiceImpl.java
  61. 195 0
      fs-service/src/main/java/com/fs/live/vo/MergedOrderVO.java
  62. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  63. 3 2
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  64. 2 1
      fs-service/src/main/java/com/fs/sop/domain/QwSopTemp.java
  65. 3 0
      fs-service/src/main/java/com/fs/sop/domain/QwSopTempRules.java
  66. 19 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopTempRulesMapper.java
  67. 2 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempContentService.java
  68. 1 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempDayService.java
  69. 2 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopTempRulesService.java
  70. 6 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempContentServiceImpl.java
  71. 7 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempDayServiceImpl.java
  72. 7 2
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempRulesServiceImpl.java
  73. 111 35
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  74. 0 9
      fs-service/src/main/resources/application-common.yml
  75. 2 2
      fs-service/src/main/resources/application-config-druid-cfryt.yml
  76. 3 3
      fs-service/src/main/resources/application-druid-bjzm.yml
  77. 10 0
      fs-service/src/main/resources/application-druid-jnmy-test.yml
  78. 11 0
      fs-service/src/main/resources/application-druid-jnmy.yml
  79. 125 0
      fs-service/src/main/resources/mapper/MerchantAppConfigMapper.xml
  80. 8 0
      fs-service/src/main/resources/mapper/course/FsCoursePlaySourceConfigMapper.xml
  81. 438 345
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  82. 6 1
      fs-service/src/main/resources/mapper/course/FsUserCoursePeriodDaysMapper.xml
  83. 1 0
      fs-service/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml
  84. 2 2
      fs-service/src/main/resources/mapper/his/FsStoreOrderMapper.xml
  85. 3 0
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  86. 1 1
      fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
  87. 1 0
      fs-service/src/main/resources/mapper/hisStore/FsUserAddressScrmMapper.xml
  88. 403 0
      fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml
  89. 25 9
      fs-service/src/main/resources/mapper/live/LiveDataMapper.xml
  90. 2 2
      fs-service/src/main/resources/mapper/qw/CustomerTransferApprovalMapper.xml
  91. 9 1
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  92. 92 0
      fs-user-app/src/main/java/com/fs/app/controller/AppLoginController.java
  93. 15 0
      fs-user-app/src/main/java/com/fs/app/controller/IntegralController.java
  94. 57 0
      fs-user-app/src/main/java/com/fs/app/controller/live/OrderController.java
  95. 1 1
      fs-user-app/src/main/java/com/fs/app/controller/store/AddressScrmController.java

+ 24 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
@@ -134,6 +135,29 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         return AjaxResult.success();
     }
 
+    @PreAuthorize("@ss.hasPermi('course:playSourceConfig:bind')")
+    @Log(title = "绑定支付配置", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateBindConfig")
+    public AjaxResult updateBindConfig(@RequestBody FsCoursePlaySourceConfigEditParam param) {
+
+        FsCoursePlaySourceConfig update =new FsCoursePlaySourceConfig();
+        update.setId(param.getId());
+        update.setMerchantConfigId(param.getMerchantConfigId());
+        fsCoursePlaySourceConfigService.updateById(update);
+        return AjaxResult.success();
+    }
+
+    @PreAuthorize("@ss.hasPermi('course:playSourceConfig:unbind')")
+    @Log(title = "解绑支付配置", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateUnbindConfig")
+    public AjaxResult updateUnbindConfig(@RequestBody FsCoursePlaySourceConfigEditParam param) {
+        LambdaUpdateWrapper<FsCoursePlaySourceConfig> updateWrapper = Wrappers.lambdaUpdate();
+        updateWrapper.eq(FsCoursePlaySourceConfig::getId, param.getId())
+                .set(FsCoursePlaySourceConfig::getMerchantConfigId, null);
+        fsCoursePlaySourceConfigService.update(updateWrapper);
+        return AjaxResult.success();
+    }
+
     @PreAuthorize("@ss.hasPermi('course:playSourceConfig:remove')")
     @Log(title = "点播播放源配置", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")

+ 42 - 13
fs-admin/src/main/java/com/fs/course/controller/FsCourseRedPacketLogController.java

@@ -1,17 +1,17 @@
 package com.fs.course.controller;
 
-import java.util.ArrayList;
-import java.util.List;
-
+import java.util.*;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 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.config.CourseConfig;
+import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.mapper.FsUserCourseMapper;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.param.FsCourseRedPacketLogParam;
+import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.vo.FsCourseRedPacketLogListPVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.utils.PhoneUtil;
@@ -21,14 +21,7 @@ import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -52,6 +45,9 @@ public class FsCourseRedPacketLogController extends BaseController
 {
     @Autowired
     private IFsCourseRedPacketLogService fsCourseRedPacketLogService;
+
+    @Autowired
+    private IFsUserCoursePeriodService fsUserCoursePeriodService;
     @Autowired
     FsUserCourseMapper fsUserCourseMapper;
     @Autowired
@@ -82,12 +78,42 @@ public class FsCourseRedPacketLogController extends BaseController
 
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
-
+            if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){
+                FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsCourseRedPacketLogListPVO.getPeriodId().longValue());
+                fsCourseRedPacketLogListPVO.setPeriodName(fsUserCoursePeriod.getPeriodName());
+            }
             fsCourseRedPacketLogListPVO.setPhone(PhoneUtil.decryptAutoPhoneMk(fsCourseRedPacketLogListPVO.getPhone()));
         }
         return R.ok().put("data",new PageInfo<>(list));
     }
 
+    /**
+     * 查询短链课程看课记录列表分页
+     * 与上面请求方式不同,list集合数据在get传输掉包
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseRedPacketLog:pageList')")
+    @PostMapping("/pageList")
+    public R pageList(@RequestBody FsCourseRedPacketLogParam fsCourseRedPacketLog)
+    {
+
+        if (fsCourseRedPacketLog.getPhoneMk() != null && fsCourseRedPacketLog.getPhoneMk() != "") {
+            fsCourseRedPacketLog.setPhone(encryptPhone(fsCourseRedPacketLog.getPhoneMk()));
+        }
+
+        PageHelper.startPage(fsCourseRedPacketLog.getPageNum(), fsCourseRedPacketLog.getPageSize());
+
+        List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
+        for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
+            if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){
+                FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsCourseRedPacketLogListPVO.getPeriodId().longValue());
+                fsCourseRedPacketLogListPVO.setPeriodName(fsUserCoursePeriod.getPeriodName());
+            }
+            fsCourseRedPacketLogListPVO.setPhone(PhoneUtil.decryptAutoPhoneMk(fsCourseRedPacketLogListPVO.getPhone()));
+        }
+
+        return R.ok().put("data", new PageInfo<>(list));
+    }
+
     /**
      * 导出短链课程看课记录列表
      */
@@ -101,7 +127,10 @@ public class FsCourseRedPacketLogController extends BaseController
         }
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
-
+            if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){
+                FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsCourseRedPacketLogListPVO.getPeriodId().longValue());
+                fsCourseRedPacketLogListPVO.setPeriodName(fsUserCoursePeriod.getPeriodName());
+            }
             fsCourseRedPacketLogListPVO.setPhone(PhoneUtil.decryptAutoPhoneMk(fsCourseRedPacketLogListPVO.getPhone()));
         }
         ExcelUtil<FsCourseRedPacketLogListPVO> util = new ExcelUtil<FsCourseRedPacketLogListPVO>(FsCourseRedPacketLogListPVO.class);

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

@@ -2,7 +2,10 @@ package com.fs.course.controller;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
+import cn.hutool.core.collection.CollectionUtil;
 import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
@@ -55,20 +58,31 @@ public class FsCourseWatchLogController extends BaseController
      * 查询短链课程看课记录列表
      */
     @PreAuthorize("@ss.hasPermi('course:courseWatchLog:list')")
-    @PostMapping("/list")
-    public R list(@RequestBody FsCourseWatchLogListParam param)
+    @GetMapping("/list")
+    public TableDataInfo list(FsCourseWatchLogListParam param)
     {
-        if(param.getPageNum() == null) {
-            param.setPageNum(1);
-        }
-        if(param.getPageSize() == null) {
-            param.setPageSize(10);
+        startPage();
+        if(CollectionUtil.isNotEmpty(param.getUserIds())){
+            param.setUserIds(param.getUserIds().stream()
+                    .filter(userId -> userId != null && userId.startsWith("user_"))
+                    .map(userId -> userId.substring(5))
+                    .collect(Collectors.toList())
+            );
         }
-        PageHelper.startPage(param.getPageNum(), param.getPageSize());
-
         List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
+        return getDataTable(list);
+    }
 
-        return R.ok().put("data",new PageInfo<>(list));
+    /**
+     * 查询短链课程看课记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:pageList')")
+    @PostMapping("/pageList")
+    public R pageList(@RequestBody FsCourseWatchLogListParam param)
+    {
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
+        return R.ok().put("data", new PageInfo<>(list));
     }
 
     @GetMapping("/qwWatchLogAllStatisticsList")
@@ -120,6 +134,32 @@ public class FsCourseWatchLogController extends BaseController
         return getDataTable(list);
     }
 
+    /**
+     * 会员看课统计导出
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:statisticsExport')")
+    @Log(title = "会员看课统计导出", businessType = BusinessType.EXPORT)
+    @PostMapping("/statisticsExport")
+    public AjaxResult statisticsExport(@RequestBody FsCourseWatchLogStatisticsListParam param)
+    {
+
+        // 如果看指定用户就不用设置公司
+        if(param.getCompanyId() == null){
+            if(param.getUserId() == null) {
+                throw new CustomException("查看公司或者用户必填!");
+            }
+        }
+        if (param.getSTime()==null||param.getETime()==null){
+            throw new CustomException("必须选择开始时间和结束时间!");
+        }
+
+        List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVONew(param);
+
+        ExcelUtil<FsCourseWatchLogStatisticsListVO> util = new ExcelUtil<FsCourseWatchLogStatisticsListVO>(FsCourseWatchLogStatisticsListVO.class);
+        return util.exportExcel(list, "会员看课统计");
+    }
+
+
     /**
      * 导出短链课程看课记录列表
      */

+ 27 - 0
fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java

@@ -89,6 +89,33 @@ public class QwFsCourseWatchLogController extends BaseController
         return getDataTable(list);
     }
 
+    /**
+     * 会员看课统计导出
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:statisticsExport')")
+    @Log(title = "企微看课统计导出", businessType = BusinessType.EXPORT)
+    @PostMapping("/statisticsExport")
+    public AjaxResult statisticsExport(@RequestBody FsCourseWatchLogStatisticsListParam param)
+    {
+
+        if (param.getSTime()==null||param.getETime()==null){
+            throw new CustomException("必须选择开始时间和结束时间!");
+        }
+
+        param.setSendType(2); //企微
+        List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
+        if("济南联志健康".equals(signProjectName)){
+            FsCourseWatchLogStatisticsListVO totalData = fsCourseWatchLogService.getTotalDataAddItem(param);
+            list.add(totalData);
+        }
+
+        ExcelUtil<FsCourseWatchLogStatisticsListVO> util = new ExcelUtil<FsCourseWatchLogStatisticsListVO>(FsCourseWatchLogStatisticsListVO.class);
+
+        return util.exportExcel(list, "企微看课统计");
+    }
+
+
+
     @GetMapping("/getSignProjectName")
     public R getSignProjectName(){
         return R.ok().put("signProjectName", signProjectName);

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

@@ -40,7 +40,7 @@ public class FsAdvController extends BaseController
     /**
      * 查询广告列表
      */
-    @PreAuthorize("@ss.hasPermi('his:adv:list')")
+    @PreAuthorize("@ss.hasPermi('store:adv:list')")
     @GetMapping("/list")
     public TableDataInfo list(FsAdv fsAdv)
     {
@@ -52,7 +52,7 @@ public class FsAdvController extends BaseController
     /**
      * 导出广告列表
      */
-    @PreAuthorize("@ss.hasPermi('his:adv:export')")
+    @PreAuthorize("@ss.hasPermi('store:adv:export')")
     @Log(title = "广告", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
     public AjaxResult export(FsAdv fsAdv)
@@ -65,7 +65,7 @@ public class FsAdvController extends BaseController
     /**
      * 获取广告详细信息
      */
-    @PreAuthorize("@ss.hasPermi('his:adv:query')")
+    @PreAuthorize("@ss.hasPermi('store:adv:query')")
     @GetMapping(value = "/{advId}")
     public AjaxResult getInfo(@PathVariable("advId") String advId)
     {
@@ -76,7 +76,7 @@ public class FsAdvController extends BaseController
     /**
      * 新增广告
      */
-    @PreAuthorize("@ss.hasPermi('his:adv:add')")
+    @PreAuthorize("@ss.hasPermi('store:adv:add')")
     @Log(title = "广告", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody FsAdv fsAdv)
@@ -91,7 +91,7 @@ public class FsAdvController extends BaseController
     /**
      * 修改广告
      */
-    @PreAuthorize("@ss.hasPermi('his:adv:edit')")
+    @PreAuthorize("@ss.hasPermi('store:adv:edit')")
     @Log(title = "广告", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody FsAdv fsAdv)
@@ -105,7 +105,7 @@ public class FsAdvController extends BaseController
     /**
      * 删除广告
      */
-    @PreAuthorize("@ss.hasPermi('his:adv:remove')")
+    @PreAuthorize("@ss.hasPermi('store:adv:remove')")
     @Log(title = "广告", businessType = BusinessType.DELETE)
 	@DeleteMapping("/{advIds}")
     public AjaxResult remove(@PathVariable String[] advIds)

+ 107 - 0
fs-admin/src/main/java/com/fs/his/controller/MerchantAppConfigController.java

@@ -0,0 +1,107 @@
+package com.fs.his.controller;
+
+import java.util.List;
+
+import com.fs.his.domain.MerchantAppConfig;
+import com.fs.his.service.IMerchantAppConfigService;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 商户应用配置Controller
+ *
+ * @author fs
+ * @date 2025-12-05
+ */
+@RestController
+@RequestMapping("/his/merchantAppConfig")
+public class MerchantAppConfigController extends BaseController
+{
+    @Autowired
+    private IMerchantAppConfigService merchantAppConfigService;
+
+    /**
+     * 查询商户应用配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:merchantAppConfig:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(MerchantAppConfig merchantAppConfig)
+    {
+        startPage();
+        List<MerchantAppConfig> list = merchantAppConfigService.selectMerchantAppConfigList(merchantAppConfig);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出商户应用配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:merchantAppConfig:export')")
+    @Log(title = "商户应用配置", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(MerchantAppConfig merchantAppConfig)
+    {
+        List<MerchantAppConfig> list = merchantAppConfigService.selectMerchantAppConfigList(merchantAppConfig);
+        ExcelUtil<MerchantAppConfig> util = new ExcelUtil<MerchantAppConfig>(MerchantAppConfig.class);
+        return util.exportExcel(list, "商户应用配置数据");
+    }
+
+    /**
+     * 获取商户应用配置详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('his:merchantAppConfig:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(merchantAppConfigService.selectMerchantAppConfigById(id));
+    }
+
+    /**
+     * 新增商户应用配置
+     */
+    @PreAuthorize("@ss.hasPermi('his:merchantAppConfig:add')")
+    @Log(title = "商户应用配置", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody MerchantAppConfig merchantAppConfig)
+    {
+        merchantAppConfig.setCreatedBy(getUsername());
+        merchantAppConfig.setUpdatedBy(getUsername());
+        return toAjax(merchantAppConfigService.insertMerchantAppConfig(merchantAppConfig));
+    }
+
+    /**
+     * 修改商户应用配置
+     */
+    @PreAuthorize("@ss.hasPermi('his:merchantAppConfig:edit')")
+    @Log(title = "商户应用配置", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody MerchantAppConfig merchantAppConfig)
+    {
+        merchantAppConfig.setUpdatedBy(getUsername());
+        return toAjax(merchantAppConfigService.updateMerchantAppConfig(merchantAppConfig));
+    }
+
+    /**
+     * 删除商户应用配置
+     */
+    @PreAuthorize("@ss.hasPermi('his:merchantAppConfig:remove')")
+    @Log(title = "商户应用配置", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long ids)
+    {
+        return toAjax(merchantAppConfigService.deleteMerchantAppConfigById(ids));
+    }
+}

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

@@ -244,6 +244,11 @@ public class Task {
             String value = (String) cacheMap.get(key);
             //获取sessionId
             Long sessionId = Long.parseLong(key);
+            try {
+                Thread.sleep(800);
+            } catch (Exception e) {
+                log.error("定时消息处理异常,会话id:{},文本:{}",sessionId,value,e);
+            }
             try {
                 if (value != null && !value.isEmpty()) {
                     FastGptChatSession chatSession = fastGptChatSessionMapper.selectFastGptChatSessionBySessionId(sessionId);

+ 9 - 9
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreScrmController.java

@@ -37,7 +37,7 @@ public class FsStoreScrmController extends BaseController
     /**
      * 查询店铺管理列表
      */
-    @PreAuthorize("@ss.hasPermi('his:store:list')")
+    @PreAuthorize("@ss.hasPermi('store:his:store:list')")
     @GetMapping("/list")
     public TableDataInfo list(FsStoreScrm fsStore)
     {
@@ -52,7 +52,7 @@ public class FsStoreScrmController extends BaseController
     /**
      * 导出店铺管理列表
      */
-    @PreAuthorize("@ss.hasPermi('his:store:export')")
+    @PreAuthorize("@ss.hasPermi('store:his:store:export')")
     @Log(title = "店铺管理", businessType = BusinessType.EXPORT,logParam = {"店铺","导出店铺信息"},isStoreLog = true)
     @GetMapping("/export")
     public AjaxResult export(FsStoreScrm fsStore)
@@ -69,7 +69,7 @@ public class FsStoreScrmController extends BaseController
     /**
      * 获取店铺管理详细信息
      */
-    @PreAuthorize("@ss.hasPermi('his:store:query')")
+    @PreAuthorize("@ss.hasPermi('store:his:store:query')")
     @GetMapping(value = "/{storeId}")
     public AjaxResult getInfo(@PathVariable("storeId") Long storeId)
     {
@@ -81,7 +81,7 @@ public class FsStoreScrmController extends BaseController
     /**
      * 新增店铺管理
      */
-    @PreAuthorize("@ss.hasPermi('his:store:add')")
+    @PreAuthorize("@ss.hasPermi('store:his:store:add')")
     @Log(title = "店铺管理", businessType = BusinessType.INSERT,logParam = {"店铺","新增店铺信息"},isStoreLog = true)
     @PostMapping
     public AjaxResult add(@RequestBody FsStoreScrm fsStore)
@@ -93,7 +93,7 @@ public class FsStoreScrmController extends BaseController
     /**
      * 修改店铺管理
      */
-    @PreAuthorize("@ss.hasPermi('his:store:edit')")
+    @PreAuthorize("@ss.hasPermi('store:his:store:edit')")
     @Log(title = "店铺管理", businessType = BusinessType.UPDATE,logParam = {"店铺","修改店铺信息"},isStoreLog = true)
     @PutMapping
     public AjaxResult edit(@RequestBody FsStoreScrm fsStore)
@@ -108,7 +108,7 @@ public class FsStoreScrmController extends BaseController
     /**
      * 删除店铺管理
      */
-    @PreAuthorize("@ss.hasPermi('his:store:remove')")
+    @PreAuthorize("@ss.hasPermi('store:his:store:remove')")
     @Log(title = "店铺管理", businessType = BusinessType.DELETE,logParam = {"店铺","删除店铺信息"},isStoreLog = true)
 	@DeleteMapping("/{storeIds}")
     public AjaxResult remove(@PathVariable Long[] storeIds)
@@ -119,7 +119,7 @@ public class FsStoreScrmController extends BaseController
     /**
      * 店铺审核
      */
-    @PreAuthorize("@ss.hasPermi('his:store:audit')")
+    @PreAuthorize("@ss.hasPermi('store:his:store:audit')")
     @Log(title = "店铺审核", businessType = BusinessType.AUDIT,logParam = {"店铺","店铺审核"},isStoreLog = true
     ,logParamExpression = "#p0.getIsAudit()==1?new String[]{'店铺','店铺审核通过'}: new String[]{'店铺','店铺审核退回'}")
     @PutMapping("/audit")
@@ -131,7 +131,7 @@ public class FsStoreScrmController extends BaseController
     /**
      * 重置店铺密码
      * */
-    @PreAuthorize("@ss.hasPermi('his:store:refresh')")
+    @PreAuthorize("@ss.hasPermi('store:his:store:refresh')")
     @Log(title = "店铺管理", businessType = BusinessType.UPDATE,logParam = {"店铺","重置店铺密码"},isStoreLog = true)
     @PutMapping("/refresh/{storeId}")
     public AjaxResult refresh(@PathVariable Long storeId)
@@ -143,7 +143,7 @@ public class FsStoreScrmController extends BaseController
     /**
      * 店铺审核日志
      * */
-    @PreAuthorize("@ss.hasPermi('his:store:auditLog')")
+    @PreAuthorize("@ss.hasPermi('store:his:store:auditLog')")
     @GetMapping("/auditLog/{storeId}")
     public R auditLog(@PathVariable Long storeId){
         return R.ok().put("auditLog",storeAuditLogUtil.selectOperLogByMainId(storeId));

+ 55 - 0
fs-admin/src/main/java/com/fs/live/controller/OrderController.java

@@ -0,0 +1,55 @@
+package com.fs.live.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ParseUtils;
+import com.fs.his.utils.PhoneUtil;
+import com.fs.hisStore.service.IMergedOrderService;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 合并订单Controller
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Api("合并订单管理")
+@RestController
+@RequestMapping("/order")
+public class OrderController extends BaseController
+{
+    @Autowired
+    private IMergedOrderService mergedOrderService;
+
+    /**
+     * 查询合并订单列表
+     */
+    @ApiOperation("查询合并订单列表")
+    @GetMapping("/list")
+    public TableDataInfo list(MergedOrderQueryParam param)
+    {
+        startPage();
+        List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
+        for (MergedOrderVO vo : list) {
+            vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
+            vo.setPhone(ParseUtils.parsePhone(vo.getPhone()));
+            vo.setSalesPhone(ParseUtils.parsePhone(vo.getSalesPhone()));
+            vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
+        }
+        return getDataTable(list);
+    }
+}

+ 25 - 1
fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java

@@ -13,10 +13,12 @@ import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseRedPacketLog;
+import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.mapper.FsUserCourseMapper;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.param.FsCourseRedPacketLogParam;
 import com.fs.course.service.IFsCourseRedPacketLogService;
+import com.fs.course.service.IFsUserCoursePeriodService;
 import com.fs.course.vo.FsCourseRedPacketLogListPVO;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
@@ -51,6 +53,9 @@ public class FsCourseRedPacketLogController extends BaseController
     private TokenService tokenService;
     @Autowired
     private ISysConfigService configService;
+
+    @Autowired
+    private IFsUserCoursePeriodService fsUserCoursePeriodService;
     /**
      * 查询短链课程看课记录列表
      */
@@ -73,6 +78,10 @@ public class FsCourseRedPacketLogController extends BaseController
 
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
+            if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){
+                FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsCourseRedPacketLogListPVO.getPeriodId().longValue());
+                fsCourseRedPacketLogListPVO.setPeriodName(fsUserCoursePeriod.getPeriodName());
+            }
 
             fsCourseRedPacketLogListPVO.setPhone(PhoneUtil.decryptAutoPhoneMk(fsCourseRedPacketLogListPVO.getPhone()));
         }
@@ -96,6 +105,10 @@ public class FsCourseRedPacketLogController extends BaseController
 
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
+            if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){
+                FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsCourseRedPacketLogListPVO.getPeriodId().longValue());
+                fsCourseRedPacketLogListPVO.setPeriodName(fsUserCoursePeriod.getPeriodName());
+            }
             fsCourseRedPacketLogListPVO.setPhone(PhoneUtil.decryptAutoPhoneMk(fsCourseRedPacketLogListPVO.getPhone()));
         }
         return getDataTable(list);
@@ -115,6 +128,10 @@ public class FsCourseRedPacketLogController extends BaseController
 
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVONew(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
+            if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){
+                FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsCourseRedPacketLogListPVO.getPeriodId().longValue());
+                fsCourseRedPacketLogListPVO.setPeriodName(fsUserCoursePeriod.getPeriodName());
+            }
             fsCourseRedPacketLogListPVO.setPhone(PhoneUtil.decryptAutoPhoneMk(fsCourseRedPacketLogListPVO.getPhone()));
         }
         return getDataTable(list);
@@ -136,7 +153,10 @@ public class FsCourseRedPacketLogController extends BaseController
         }
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
-
+            if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){
+                FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsCourseRedPacketLogListPVO.getPeriodId().longValue());
+                fsCourseRedPacketLogListPVO.setPeriodName(fsUserCoursePeriod.getPeriodName());
+            }
             fsCourseRedPacketLogListPVO.setPhone(PhoneUtil.decryptAutoPhoneMk(fsCourseRedPacketLogListPVO.getPhone()));
         }
         ExcelUtil<FsCourseRedPacketLogListPVO> util = new ExcelUtil<FsCourseRedPacketLogListPVO>(FsCourseRedPacketLogListPVO.class);
@@ -158,6 +178,10 @@ public class FsCourseRedPacketLogController extends BaseController
         }
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
+            if (ObjectUtil.isNotEmpty(fsCourseRedPacketLogListPVO.getPeriodId())){
+                FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsCourseRedPacketLogListPVO.getPeriodId().longValue());
+                fsCourseRedPacketLogListPVO.setPeriodName(fsUserCoursePeriod.getPeriodName());
+            }
             fsCourseRedPacketLogListPVO.setPhone(PhoneUtil.decryptAutoPhoneMk(fsCourseRedPacketLogListPVO.getPhone()));
         }
         ExcelUtil<FsCourseRedPacketLogListPVO> util = new ExcelUtil<FsCourseRedPacketLogListPVO>(FsCourseRedPacketLogListPVO.class);

+ 10 - 2
fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java

@@ -22,6 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -60,7 +61,9 @@ public class LiveDataController extends BaseController
     @GetMapping("/getLiveUserDetailListBySql")
     public R getLiveUserDetailListBySql(@RequestParam Long liveId, HttpServletRequest request) {
         CompanyUser user = tokenService.getLoginUser(request).getUser();
-
+        if ("00".equals(user.getUserType())) {
+            return liveDataService.getLiveUserDetailListBySql(liveId,user.getCompanyId(),null);
+        }
         return liveDataService.getLiveUserDetailListBySql(liveId,user.getCompanyId(),user.getUserId());
     }
 
@@ -97,7 +100,12 @@ public class LiveDataController extends BaseController
     @GetMapping("/exportLiveUserDetail")
     public AjaxResult exportLiveUserDetail(@RequestParam Long liveId, HttpServletRequest request) {
         CompanyUser user = tokenService.getLoginUser(request).getUser();
-        List<LiveUserDetailExportVO> list = liveDataService.exportLiveUserDetail(liveId,user.getCompanyId(),user.getUserId());
+        List<LiveUserDetailExportVO> list = new ArrayList<>();
+        if ("00".equals(user.getUserType())) {
+            liveDataService.getLiveUserDetailListBySql(liveId, user.getCompanyId(), null);
+        } else {
+            list = liveDataService.exportLiveUserDetail(liveId,user.getCompanyId(),user.getUserId());
+        }
         if (list == null || list.isEmpty()) {
             return AjaxResult.error("未找到用户详情数据");
         }

+ 31 - 4
fs-company/src/main/java/com/fs/framework/service/CompanyLoginService.java

@@ -58,13 +58,13 @@ public class CompanyLoginService
     @Autowired
     private ICompanyUserService companyUserService;
 
-    @Value("${wechat.company.appid}")
+    @Value("${wechat.company.appid:#{null}}")
     private String appId;
-    @Value("${wechat.company.secret}")
+    @Value("${wechat.company.secret:#{null}}")
     private String secret;
-    @Value("${wechat.company.redirectUri}")
+    @Value("${wechat.company.redirectUri:#{null}}")
     private String redirectUri;
-    @Value("${wechat.isNeedScan}")
+    @Value("${wechat.isNeedScan:false}")
     private Boolean isNeedScan;
 
     @Autowired
@@ -119,8 +119,35 @@ public class CompanyLoginService
             }
         }
         LoginUser loginUser = (LoginUser) authentication.getPrincipal();
+        //查询当前登录用户信息
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(loginUser.getUser().getUserId());
         AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginUser.getUser().getCompanyId(),username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
         redisCache.setCacheObject("companyId:"+loginUser.getUser().getUserId(),loginUser.getUser().getCompanyId(),604800, TimeUnit.SECONDS);
+        String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest()).split(",")[0].trim();
+        log.info("销售用户{}正常登录获取到的ip地址{}", loginUser.getUser().getUserId(), ipAddr);
+
+        companyUser.setUserId(loginUser.getUser().getUserId());
+        String loginIp = companyUser.getLoginIp();
+        List<String> ipList = new ArrayList<>();
+        if (StringUtils.isNotEmpty(loginIp)) {
+            String[] ips = loginIp.split(",");
+            for (String ip : ips) {
+                ip = ip.trim();                  // 去掉前后空格
+                if (!ip.isEmpty()) {
+                    ipList.add(ip);              // 先加入已有 IP
+                }
+            }
+        }
+
+        ipList.add(ipAddr);
+        List<String> distinctList = ipList.stream()
+                .map(String::trim)       // 再次确保去掉空格
+                .distinct()
+                .collect(Collectors.toList());
+        companyUser.setLoginIp(String.join(",", distinctList));
+
+        companyUser.setLoginDate(new Date());
+        companyUserService.updateCompanyUser(companyUser);
         // 生成token
         return tokenService.createToken(loginUser);
     }

+ 15 - 4
fs-framework/src/main/java/com/fs/framework/web/service/SysLoginService.java

@@ -59,13 +59,13 @@ public class SysLoginService
     @Autowired
     private WechatLoginService wechatLoginService;
 
-    @Value("${wechat.admin.appid}")
+    @Value("${wechat.admin.appid:#{null}}")
     private String appId;
-    @Value("${wechat.admin.secret}")
+    @Value("${wechat.admin.secret:#{null}}")
     private String secret;
-    @Value("${wechat.admin.redirectUri}")
+    @Value("${wechat.admin.redirectUri:#{null}}")
     private String redirectUri;
-    @Value("${wechat.isNeedScan}")
+    @Value("${wechat.isNeedScan:false}")
     private Boolean isNeedScan;
 
     /**
@@ -143,6 +143,17 @@ public class SysLoginService
      */
     public void recordLoginInfo(SysUser user)
     {
+        String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest());
+        String loginIp = user.getLoginIp();
+        if (com.fs.common.utils.StringUtils.isEmpty(loginIp)) {
+            user.setLoginIp(ipAddr);
+        } else {
+            List<String> ipList = new ArrayList<>(Arrays.asList(loginIp.split(",")));
+            if (!ipList.contains(ipAddr)) {
+                ipList.add(ipAddr);
+                user.setLoginIp(String.join(",", ipList));
+            }
+        }
         user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
         user.setLoginDate(DateUtils.getNowDate());
         userService.updateUserProfile(user);

+ 1 - 1
fs-live-app/src/main/java/com/fs/live/controller/LiveController.java

@@ -77,7 +77,7 @@ public class LiveController {
 		live.setLiveId(Long.valueOf(params.get("stream_id")));
 		live.setStatus(3);
 		live.setFinishTime(LocalDateTime.now());
-		liveService.updateLiveEntity(live);
+//		liveService.updateLiveEntity(live);
 		return R.ok();
 //		{app=200149.push.tlivecloud.com, appid=1319721001, appname=live, channel_id=673,
 //				errcode=1, errmsg=The push client actively stopped the push, event_time=1755571239,

+ 18 - 2
fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -157,7 +157,7 @@ public class WebSocketServer {
                 sendMsgVo.setNickName(fsUser.getNickname());
                 sendMsgVo.setAvatar(fsUser.getAvatar());
                 // 广播连接消息
-                broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+                broadcastWebMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
             }
 
             LiveUserFirstEntry liveUserFirstEntry = liveUserFirstEntryService.selectEntityByLiveIdUserId(liveId, userId);
@@ -242,7 +242,7 @@ public class WebSocketServer {
                 sendMsgVo.setData(JSONObject.toJSONString(liveWatchUserVO));
                 sendMsgVo.setNickName(fsUser.getNickname());
                 sendMsgVo.setAvatar(fsUser.getAvatar());
-                broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+                broadcastWebMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
             }
 
         } else {
@@ -588,6 +588,22 @@ public class WebSocketServer {
         session.getAsyncRemote().sendText(JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
     }
 
+    /**
+     * 广播消息
+     * @param liveId   直播间ID
+     * @param message  消息内容
+     */
+    public void broadcastWebMessage(Long liveId, String message) {
+        ConcurrentHashMap<Long, Session> room = getRoom(liveId);
+
+        // 普通用户房间:并行发送
+        room.forEach((k, v) -> {
+            if (v.isOpen()) {
+                sendWithRetry(v,message,1);
+            }
+        });
+    }
+
     /**
      * 广播消息
      * @param liveId   直播间ID

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

@@ -202,10 +202,11 @@ public class CompanyServiceImpl implements ICompanyService
             List<CompanyMiniapp> miniApp = companyMiniappService.getMiniAppListByCompanyList(Collections.singletonList(company.getCompanyId()));
             company.setMiniAppMaster(GET_MINI_APP_STR.apply(0, miniApp));
             company.setMiniAppServer(GET_MINI_APP_STR.apply(1, miniApp));
-        }
-        String redPackageMoney = redisCache.getCacheObject(FsConstants.COMPANY_MONEY_KEY+company.getCompanyId());
-        if(redPackageMoney!=null){
-            company.setRedPackageMoney(new BigDecimal(redPackageMoney));
+
+            String redPackageMoney = redisCache.getCacheObject(FsConstants.COMPANY_MONEY_KEY+company.getCompanyId());
+            if(redPackageMoney!=null){
+                company.setRedPackageMoney(new BigDecimal(redPackageMoney));
+            }
         }
         return company;
     }

+ 5 - 0
fs-service/src/main/java/com/fs/course/domain/FsCoursePlaySourceConfig.java

@@ -104,4 +104,9 @@ public class FsCoursePlaySourceConfig {
      * 小程序状态:0正常,1半封禁,2封禁
      */
     private Integer status;
+
+    /**
+     * 商户支付配置id
+     */
+    private Long merchantConfigId;
 }

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

@@ -14,4 +14,6 @@ public interface FsCoursePlaySourceConfigMapper extends BaseMapper<FsCoursePlayS
      * 查询点播配置列表
      */
     List<FsCoursePlaySourceConfigVO> selectCoursePlaySourceConfigVOListByMap(@Param("params") Map<String, Object> params);
+
+    FsCoursePlaySourceConfig selectCoursePlaySourceConfigByAppId(String appId);
 }

+ 7 - 7
fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java

@@ -107,14 +107,13 @@ public interface FsCourseRedPacketLogMapper
     List<FsCourseRedPacketLogListPVO> selectRedPacketLogListVO(@Param("maps") FsCourseRedPacketLogParam param);
 
     @Select({"<script> " +
-            "select l.*,v.title,u.nick_name as fsNickName,u.avatar as fsAvatar,u.phone,cu.nick_name company_user_name,c.company_name,qu.qw_user_name,fuc.course_name,cd.dept_name,u.phone as phoneNumber   from fs_course_red_packet_log l  \n" +
+            "select l.*,v.title,u.nick_name as fsNickName,u.avatar as fsAvatar,u.phone,cu.nick_name company_user_name,c.company_name,qu.qw_user_name,fuc.course_name,u.phone as phoneNumber,cu.dept_id   from fs_course_red_packet_log l  \n" +
             "left join fs_user_course_video v on v.video_id = l.video_id \n" +
             "left join fs_user u on u.user_id = l.user_id \n" +
             "left join fs_user_course fuc on fuc.course_id = l.course_id \n" +
             "left join company_user cu on cu.user_id=l.company_user_id \n" +
-            "left join company c on c.company_id=l.company_id \n" +
-            "LEFT JOIN qw_user qu on qu.id= l.qw_user_id  " +
-            "LEFT JOIN company_dept cd on cd.dept_id= cu.dept_id  " +
+            "left join company c on c.company_id=cu.company_id \n" +
+            "LEFT JOIN qw_user qu on qu.id= l.qw_user_id  \n" +
             "where 1=1   " +
             "<if test = ' maps.userId !=null '> and l.user_id = #{maps.userId} </if>" +
             "<if test = ' maps.watchLogId !=null '> and l.watch_log_id = #{maps.watchLogId} </if>" +
@@ -124,15 +123,16 @@ public interface FsCourseRedPacketLogMapper
             "<if test = ' maps.nickName !=null '> and u.nick_name  like concat('%', #{maps.nickName}, '%') </if>" +
             "<if test = ' maps.courseId !=null '> and l.course_id = #{maps.courseId} </if>" +
             "<if test = ' maps.videoId !=null '> and l.video_id = #{maps.videoId} </if>" +
+            "<if test = ' maps.periodId !=null '> and l.period_id = #{maps.periodId} </if>" +
             "<if test = ' maps.status !=null '> and l.status = #{maps.status} </if>" +
             "<if test = \"maps.phone !=null and maps.phone != '' \"> and u.phone = #{maps.phone} </if>" +
             "<if test = ' maps.qwUserId !=null '> and l.qw_user_id = #{maps.qwUserId} </if>" +
             "<if test=\"maps.sTime != null \">  and DATE(l.create_time) &gt;= DATE(#{maps.sTime})</if>\n" +
             "<if test=\"maps.eTime != null \">  and DATE(l.create_time) &lt;= DATE(#{maps.eTime})</if>\n" +
-            "            <if test=\"maps.companyUserIds != null and !maps.companyUserIds.isEmpty()\">\n" +
+            "<if test=\"maps.userIds != null and maps.userIds.size() > 0\">\n" +
             "                AND l.company_user_id IN\n" +
-            "                <foreach collection='maps.companyUserIds' item='item' open='(' separator=',' close=')'>\n" +
-            "                    #{item}\n" +
+            "                <foreach collection=\"maps.userIds\" open=\"(\" close=\")\" separator=\",\" item=\"item\">\n" +
+            "                    ${item}\n" +
             "                </foreach>\n" +
             "            </if>" +
             " order by l.log_id desc  "+

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

@@ -265,10 +265,10 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "       and  send_type= #{sendType} " +
             "</if>\n" +
             "<if test= 'sTime != null '> " +
-            "       and DATE(o.create_time) &gt;= DATE(#{sTime})\n" +
+            "       and o.create_time &gt;= #{sTime}\n" +
             "</if>\n" +
             "<if test='eTime != null '> " +
-            "      and DATE(o.create_time) &lt;= DATE(#{eTime})\n" +
+            "      and o.create_time &lt; DATE_ADD(#{eTime}, INTERVAL 1 DAY)\n" +
             "</if>" +
             "<if test ='sendType != 1 and nickName !=null and nickName!=\"\"'>\n" +
             "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
@@ -295,6 +295,10 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "</script>"})
     List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO(FsCourseWatchLogStatisticsListParam param);
 
+    List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO_COUNT(FsCourseWatchLogStatisticsListParam param);
+
+
+
     @Select({"<script> " +
             " \tselect \n" +
             "\t'总合计' as qw_user_name,\n" +

+ 3 - 0
fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigEditParam.java

@@ -55,4 +55,7 @@ public class FsCoursePlaySourceConfigEditParam {
 
     @ApiModelProperty("小程序状态:0正常,1半封禁,2封禁")
     private Integer status;
+
+    @ApiModelProperty("商户支付配置id")
+    private Long merchantConfigId;
 }

+ 29 - 24
fs-service/src/main/java/com/fs/course/param/FsCourseRedPacketLogParam.java

@@ -1,15 +1,15 @@
 package com.fs.course.param;
 
-import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
-import java.util.ArrayList;
+import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @Data
-public class FsCourseRedPacketLogParam {
+public class FsCourseRedPacketLogParam implements Serializable {
 
     private Long userId;
 
@@ -23,6 +23,11 @@ public class FsCourseRedPacketLogParam {
 
     private Long videoId;
 
+    /**
+     * 营期id
+     */
+    private Long periodId;
+
     private Long status;
 
     private String phone;
@@ -38,31 +43,31 @@ public class FsCourseRedPacketLogParam {
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date sTime;
 
-    @TableField(exist = false)
-    private List<String> companyUserIds=new ArrayList<>();
-
     private Integer pageNum;
     private Integer pageSize;
 
-    public List<String> getCompanyUserIds() {
-        if (companyUserIds == null || companyUserIds.isEmpty()) {
-            return companyUserIds;
-        }
+    private List<String> userIds;
 
-        // 直接在原始列表上修改
-        for (int i = 0; i < companyUserIds.size(); i++) {
-            String id = companyUserIds.get(i);
-            if (id != null) {
-                if (id.startsWith("dept_")) {
-                    companyUserIds.set(i, id.substring(5));
-                } else if (id.startsWith("company_")) {
-                    companyUserIds.set(i, id.substring(8));
-                } else if (id.startsWith("user_")) {
-                    companyUserIds.set(i, id.substring(5));
-                }
-            }
+    public List<String> getUserIds() {
+        if (userIds == null || userIds.isEmpty()) {
+            return userIds;
         }
-        return companyUserIds;
-    }
 
+        return userIds.stream()
+                .map(id -> {
+                    if (id == null) {
+                        return null;
+                    }
+                    // 去除前缀
+                    if (id.startsWith("dept_")) {
+                        return "0";
+                    } else if (id.startsWith("company_")) {
+                        return "0";
+                    } else if (id.startsWith("user_")) {
+                        return id.substring(5);
+                    }
+                    return "0";
+                })
+                .collect(Collectors.toList());
+    }
 }

+ 18 - 20
fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java

@@ -1,13 +1,12 @@
 package com.fs.course.param;
 
-import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @Data
 public class FsCourseWatchLogListParam implements Serializable {
@@ -110,31 +109,30 @@ public class FsCourseWatchLogListParam implements Serializable {
     private List<Long> deptIds;
     private String ids;
 
-
     private Integer pageNum;
     private Integer pageSize;
-
-    @TableField(exist = false)
-    private List<String> userIds = new ArrayList<>();
+    private List<String> userIds;
 
     public List<String> getUserIds() {
         if (userIds == null || userIds.isEmpty()) {
             return userIds;
         }
 
-        // 直接在原始列表上修改
-        for (int i = 0; i < userIds.size(); i++) {
-            String id = userIds.get(i);
-            if (id != null) {
-                if (id.startsWith("dept_")) {
-                    userIds.set(i, id.substring(5));
-                } else if (id.startsWith("company_")) {
-                    userIds.set(i, id.substring(8));
-                } else if (id.startsWith("user_")) {
-                    userIds.set(i, id.substring(5));
-                }
-            }
-        }
-        return userIds;
+        return userIds.stream()
+                .map(id -> {
+                    if (id == null) {
+                        return null;
+                    }
+                    // 去除前缀
+                    if (id.startsWith("dept_")) {
+                        return "0";
+                    } else if (id.startsWith("company_")) {
+                        return "0";
+                    } else if (id.startsWith("user_")) {
+                        return id.substring(5);
+                    }
+                    return "0";
+                })
+                .collect(Collectors.toList());
     }
 }

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

@@ -18,6 +18,11 @@ public class PeriodStatisticCountParam implements Serializable {
     @ApiModelProperty(value = "营期id")
     private Long periodId;
 
+    /**
+     * 营期名称
+     */
+    private String periodName;
+
     /**
      * 训练营Id
      */

+ 7 - 0
fs-service/src/main/java/com/fs/course/service/IFsCoursePlaySourceConfigService.java

@@ -15,4 +15,11 @@ public interface IFsCoursePlaySourceConfigService extends IService<FsCoursePlayS
     List<FsCoursePlaySourceConfigVO> selectCoursePlaySourceConfigVOListByMap(Map<String, Object> params);
 
     List<FsCoursePlaySourceConfig> selectByAppIds(List<String> miniAppList);
+
+    /**
+     * 根据appId查询
+     * @param appId
+     * @return
+     */
+    FsCoursePlaySourceConfig selectCoursePlaySourceConfigByAppId(String appId);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCoursePlaySourceConfigServiceImpl.java

@@ -30,4 +30,9 @@ public class FsCoursePlaySourceConfigServiceImpl extends ServiceImpl<FsCoursePla
     public List<FsCoursePlaySourceConfig> selectByAppIds(List<String> miniAppList) {
         return baseMapper.selectList(new QueryWrapper<FsCoursePlaySourceConfig>().in("appid", miniAppList));
     }
+
+    @Override
+    public FsCoursePlaySourceConfig selectCoursePlaySourceConfigByAppId(String appId) {
+        return baseMapper.selectCoursePlaySourceConfigByAppId(appId);
+    }
 }

+ 19 - 5
fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java

@@ -3,16 +3,16 @@ package com.fs.course.service.impl;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.stream.Collectors;
 
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.company.cache.ICompanyDeptCacheService;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMoneyLogs;
 import com.fs.company.mapper.CompanyMapper;
@@ -62,6 +62,8 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
 
     @Autowired
     private ISysConfigService configService;
+    @Autowired
+    private ICompanyDeptCacheService companyDeptCacheService;
     /**
      * 查询短链课程看课记录
      *
@@ -162,7 +164,19 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
 
     @Override
     public List<FsCourseRedPacketLogListPVO> selectFsCourseRedPacketLogListVO(FsCourseRedPacketLogParam fsCourseRedPacketLog) {
-        return fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
+        List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
+        // 应用到每个对象
+        if(CollectionUtil.isNotEmpty(list)){
+            list.forEach(item -> {
+                if(item.getDeptId() != null) {
+                    String deptNameById = companyDeptCacheService.getDeptNameById(item.getDeptId());
+                    if(StringUtils.isNotBlank(deptNameById)) {
+                        item.setDeptName(deptNameById);
+                    }
+                }
+            });
+        }
+        return list;
     }
 
     @Override

+ 19 - 6
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.course.service.impl;
 
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpRequest;
 import cn.hutool.json.JSONUtil;
@@ -13,6 +14,7 @@ import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.DictUtils;
 import com.fs.common.utils.date.DateUtil;
 import com.fs.company.cache.ICompanyCacheService;
+import com.fs.company.cache.ICompanyDeptCacheService;
 import com.fs.company.cache.ICompanyUserCacheService;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
@@ -138,6 +140,9 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     @Autowired
     private FsTagUpdateService fsTagUpdateService;
 
+    @Autowired
+    private ICompanyDeptCacheService companyDeptCacheService;
+
     /**
      * 查询短链课程看课记录
      *
@@ -677,12 +682,20 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                 ));
 
         // 遍历并赋值
-        fsCourseWatchLogListVOS.forEach(vo -> {
-            String periodName = periodIdToNameMap.get(vo.getPeriodId());
-            if (periodName != null) {
-                vo.setPeriodIdName(periodName);
-            }
-        });
+        if(CollectionUtil.isNotEmpty(fsCourseWatchLogListVOS)){
+            fsCourseWatchLogListVOS.forEach(vo -> {
+                String periodName = periodIdToNameMap.get(vo.getPeriodId());
+                if (periodName != null) {
+                    vo.setPeriodIdName(periodName);
+                }
+                if(vo.getDeptId() != null) {
+                    String deptNameById = companyDeptCacheService.getDeptNameById(vo.getDeptId());
+                    if(com.fs.common.utils.StringUtils.isNotBlank(deptNameById)) {
+                        vo.setDeptName(deptNameById);
+                    }
+                }
+            });
+        }
 
         return fsCourseWatchLogListVOS;
     }

+ 13 - 6
fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java

@@ -198,11 +198,13 @@ public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCourseP
             day.setVideoId(e);
             day.setCreateTime(new Date());
             // 默认开启今天及以后的两天
-            LocalDate compareDay = LocalDate.now().plusDays(1);
-            if(day.getDayDate().isBefore(compareDay)){
+            LocalDateTime compareDayTime = LocalDateTime.now();
+            if(compareDayTime.isAfter(day.getStartDateTime()) && compareDayTime.isBefore(day.getEndDateTime())){
                 day.setStatus(1);
-            } else {
+            } else if(compareDayTime.isBefore(day.getStartDateTime())){
                 day.setStatus(0);
+            } else {
+                day.setStatus(2);
             }
             return day;
         }).collect(Collectors.toList());
@@ -326,17 +328,20 @@ public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCourseP
         LocalDateTime tempStartDateTime = source.getStartDateTime();
         LocalDateTime tempEndDateTime = source.getEndDateTime();
         LocalDateTime tempLastJoinTime = source.getLastJoinTime();
+        Integer status = source.getStatus();
 
         // 将目标数据赋给源
         source.setDayDate(target.getDayDate());
         source.setLesson(target.getLesson());
         source.setStartDateTime(target.getStartDateTime());
         source.setEndDateTime(target.getEndDateTime());
+        source.setStatus(target.getStatus());
         source.setLastJoinTime(target.getLastJoinTime());
 
         // 将保存的源数据赋给目标
         target.setDayDate(tempDayDate);
         target.setLesson(tempLesson);
+        target.setStatus(status);
         target.setStartDateTime(tempStartDateTime);
         target.setEndDateTime(tempEndDateTime);
         target.setLastJoinTime(tempLastJoinTime);
@@ -440,11 +445,13 @@ public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCourseP
         day.setEndDateTime(LocalDateTime.of(day.getDayDate(), day.getEndDateTime().toLocalTime()));
         day.setLastJoinTime(LocalDateTime.of(day.getDayDate(), day.getLastJoinTime().toLocalTime()));
         // 默认开启今天及以后的两天,为进行中
-        LocalDate compareDay = LocalDate.now().plusDays(1);
-        if(day.getDayDate().isBefore(compareDay)){
+        LocalDateTime compareDayTime = LocalDateTime.now();
+        if(compareDayTime.isAfter(day.getStartDateTime()) && compareDayTime.isBefore(day.getEndDateTime())){
             day.setStatus(1);
-        } else {
+        } else if(compareDayTime.isBefore(day.getStartDateTime())){
             day.setStatus(0);
+        } else {
+            day.setStatus(2);
         }
         updateById(day);
         return R.ok();

+ 12 - 4
fs-service/src/main/java/com/fs/course/vo/FsCourseRedPacketLogListPVO.java

@@ -23,7 +23,7 @@ public class FsCourseRedPacketLogListPVO extends BaseEntity
 
     @Excel(name = "会员id")
     private Long userId;
-
+    @Excel(name = "会员名称")
     private String fsNickName;
 
     private String fsAvatar;
@@ -49,8 +49,8 @@ public class FsCourseRedPacketLogListPVO extends BaseEntity
     @Excel(name = "转帐金额")
     private BigDecimal amount;
 
-    @Excel(name = "状态",dictType = "sys_course_red_packet_status")
-    private Integer status;//状态 0 发送中  1  已发送
+    @Excel(name = "状态", readConverterExp = "0=发送中,1=已发送,2余额不足待发送")
+    private Integer status; // 状态 0 发送中 1 已发送
 
     @Excel(name = "分享人企微Id")
     private String qwUserId;
@@ -72,7 +72,13 @@ public class FsCourseRedPacketLogListPVO extends BaseEntity
 
 
     @Excel(name = "发送方式")
-    private Integer sendType; //归属发送方式:1 个微  2 企微
+    private Integer sendType;
+
+//    @Excel(name = "营期id")
+    private Long periodId;
+
+    @Excel(name = "营期名称")
+    private String periodName;
 
 
     /** 创建时间 */
@@ -95,4 +101,6 @@ public class FsCourseRedPacketLogListPVO extends BaseEntity
     @Excel(name = "播放时长")
     private String duration;
 
+    private Long deptId;
+    private Long companyId;
 }

+ 5 - 2
fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogListVO.java

@@ -60,7 +60,7 @@ public class FsCourseWatchLogListVO extends BaseEntity
     @Excel(name = "小节名称")
     private String videoName;
 
-    @Excel(name = "记录类型" ,dictType = "sys_course_watch_log_type")
+    @Excel(name = "记录类型" ,dictType = "sys_course_watch_log_type_new")
     private Integer logType;
 
     @Excel(name = "奖励类型 1红包 2积分")
@@ -85,6 +85,9 @@ public class FsCourseWatchLogListVO extends BaseEntity
     @Excel(name = "所属销售")
     private String companyUserName;
 
+    @Excel(name = "销售部门")
+    private String deptName;
+
     @Excel(name = "所属sop任务")
     private String sopId;
 
@@ -137,5 +140,5 @@ public class FsCourseWatchLogListVO extends BaseEntity
      */
     private Long imMsgSendDetailId;
 
-
+    private Long deptId;
 }

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

@@ -11,27 +11,35 @@ public class FsCourseWatchLogStatisticsListVO {
 
 
     private Integer project;
+
     @Excel(name = "项目名称")
     private String projectName;
 
     private Long courseId;
+
     @Excel(name = "课程名称")
     private String courseName;
 
     private Long videoId;
+
     @Excel(name = "小节名称")
     private String videoName;
 
     @Excel(name = "看课中")
     private String type1;
+
     @Excel(name = "已完课")
     private String type2;
+
     @Excel(name = "待看课")
     private String type3;
+
     @Excel(name = "看课中断")
     private String type4;
+
     @Excel(name = "上线数")
     private Long onLineNum;
+
     @Excel(name = "企业微信员工名称")
     private String qwUserName;
 

+ 5 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoDetailsVO.java

@@ -31,6 +31,11 @@ public class FsUserCourseVideoDetailsVO {
     @ApiModelProperty(value = "课程ID")
     private Long courseId;
 
+
+    @ApiModelProperty(value = "是否启用倍速(0:否;1:是)")
+    private Integer isSpeed;
+
+
     @ApiModelProperty(value = "题库内容")
     private List<FsUserVideoQuestionVO> questionBankList;
 

+ 1 - 1
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -878,7 +878,7 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         log.info("订单号: {},发货状态: {},是否发货后: {}",liveOrder.getOrderCode(),liveOrder.getStatus(),ObjectUtils.equals(liveOrder.getStatus(),2));
 
         // 发货后退款
-        if(ObjectUtils.equals(param.getOrderStatus(),2)){
+        if(ObjectUtils.equals(param.getOrderStatus(),2) || ObjectUtils.equals(param.getOrderStatus(),3)){
 
             FsJstAftersalePush fsJstAftersalePush = new FsJstAftersalePush();
             fsJstAftersalePush.setOrderId(liveOrder.getOrderCode());

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

@@ -15,6 +15,9 @@ public class FsPayConfig {
     private String appId;
     private String wxMchId;
     private String wxMchKey;
+    private String keyPath;
+    private String wxApiV3Key;
+    private String notifyUrlScrm;
 
     private String ybNotifyUrl;
     private String tzPayDecrypt;

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

@@ -183,6 +183,9 @@ public class FsUser extends BaseEntity
     /** 推广上级用户ID */
     private Long spreadUserId;
 
+    /** app登录后不为null(表示是否下载app) */
+    private String historyApp;
+
     public void setNickName(String nickname)
     {
         if(StringUtils.isNotEmpty(nickname)){

+ 66 - 0
fs-service/src/main/java/com/fs/his/domain/MerchantAppConfig.java

@@ -0,0 +1,66 @@
+package com.fs.his.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 商户应用配置对象 merchant_app_config
+ *
+ * @author fs
+ * @date 2025-12-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MerchantAppConfig extends BaseEntity{
+
+    /** 主键ID */
+    private Long id;
+
+    /** 商户类型 */
+    @Excel(name = "商户类型")
+    private String merchantType;
+
+    // 应用ID
+    @Excel(name = "应用ID")
+    private String appId;
+
+    /** 回调地址,用于接收支付结果等通知 */
+    @Excel(name = "回调地址,用于接收支付结果等通知")
+    private String callbackUrl;
+
+    /** 配置详情 */
+    @Excel(name = "配置详情")
+    private String dataJson;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date createdTime;
+
+    /** 修改时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "修改时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date updatedTime;
+
+    /** 删除状态:0-正常,1-已删除 */
+    @Excel(name = "删除状态:0-正常,1-已删除")
+    private Long isDeleted;
+
+    /** 创建人ID或用户名 */
+    private String createdBy;
+
+    /** 修改人ID或用户名 */
+    private String updatedBy;
+
+    /**
+     * 商户号
+     */
+    private String merchantId;
+
+
+}

+ 6 - 0
fs-service/src/main/java/com/fs/his/mapper/FsIntegralOrderMapper.java

@@ -71,6 +71,12 @@ public interface FsIntegralOrderMapper extends BaseMapper<FsIntegralOrder>
     @Select({"<script> select order_id, order_code, user_id, user_name, user_phone, user_address, item_json, integral,pay_money,is_pay,pay_time,pay_type, status, delivery_code, delivery_name, delivery_sn, delivery_time, create_time,qw_user_id,company_user_id,company_id, remark from fs_integral_order " +
             "<where>  \n" +
             "            <if test=\"orderCode != null  and orderCode != ''\"> and order_code = #{orderCode}</if>\n" +
+            "            <if test=\"orderCodes != null and orderCodes.size > 0\"> " +
+            "                and order_code in " +
+            "                <foreach collection='orderCodes' item='orderCode' open='(' close=')' separator=','> " +
+            "                    #{orderCode} " +
+            "                </foreach> " +
+            "            </if>\n" +
             "            <if test=\"userName != null  and userName != ''\"> and user_name like concat('%', #{userName}, '%')</if>\n" +
             "            <if test=\"userPhone != null  and userPhone != ''\"> and user_phone = #{userPhone}</if>\n" +
             "            <if test=\"integral != null  and integral != ''\"> and integral = #{integral}</if>\n" +

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

@@ -469,4 +469,6 @@ public interface FsUserMapper
     void updateUserOrderCountAndAmount(@Param("userId") Long userId, @Param("amount") BigDecimal amount);
 
     List<FsUser> selectFsUserListByPhone(String phone);
+
+    void updatePasswordByPhone(@Param("password")String password, @Param("encryptPhone")String encryptPhone);
 }

+ 74 - 0
fs-service/src/main/java/com/fs/his/mapper/MerchantAppConfigMapper.java

@@ -0,0 +1,74 @@
+package com.fs.his.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.MerchantAppConfig;
+
+/**
+ * 商户应用配置Mapper接口
+ *
+ * @author fs
+ * @date 2025-12-05
+ */
+public interface MerchantAppConfigMapper extends BaseMapper<MerchantAppConfig>{
+
+
+    /**
+     * 检查表是否存在
+     */
+    Integer checkTableExists();
+
+    /**
+     * 创建商户配置表
+     */
+    void createMerchantAppConfigTable();
+
+
+    /**
+     * 查询商户应用配置
+     *
+     * @param id 商户应用配置主键
+     * @return 商户应用配置
+     */
+    MerchantAppConfig selectMerchantAppConfigById(Long id);
+
+    /**
+     * 查询商户应用配置列表
+     *
+     * @param merchantAppConfig 商户应用配置
+     * @return 商户应用配置集合
+     */
+    List<MerchantAppConfig> selectMerchantAppConfigList(MerchantAppConfig merchantAppConfig);
+
+    /**
+     * 新增商户应用配置
+     *
+     * @param merchantAppConfig 商户应用配置
+     * @return 结果
+     */
+    int insertMerchantAppConfig(MerchantAppConfig merchantAppConfig);
+
+    /**
+     * 修改商户应用配置
+     *
+     * @param merchantAppConfig 商户应用配置
+     * @return 结果
+     */
+    int updateMerchantAppConfig(MerchantAppConfig merchantAppConfig);
+
+    /**
+     * 删除商户应用配置
+     *
+     * @param id 商户应用配置主键
+     * @return 结果
+     */
+    int deleteMerchantAppConfigById(Long id);
+
+    /**
+     * 批量删除商户应用配置
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteMerchantAppConfigByIds(Long[] ids);
+}

+ 2 - 0
fs-service/src/main/java/com/fs/his/service/IFsUserService.java

@@ -230,4 +230,6 @@ public interface IFsUserService
     HisFsUserVO getHisUserIntegralWithLogs(FsUser fsUser);
 
     List<FsUser> selectFsUserListByPhone(String phone);
+
+    R updatePasswordByPhone(String password, String encryptPhone);
 }

+ 61 - 0
fs-service/src/main/java/com/fs/his/service/IMerchantAppConfigService.java

@@ -0,0 +1,61 @@
+package com.fs.his.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.his.domain.MerchantAppConfig;
+
+/**
+ * 商户应用配置Service接口
+ *
+ * @author fs
+ * @date 2025-12-05
+ */
+public interface IMerchantAppConfigService extends IService<MerchantAppConfig>{
+    /**
+     * 查询商户应用配置
+     *
+     * @param id 商户应用配置主键
+     * @return 商户应用配置
+     */
+    MerchantAppConfig selectMerchantAppConfigById(Long id);
+
+    /**
+     * 查询商户应用配置列表
+     *
+     * @param merchantAppConfig 商户应用配置
+     * @return 商户应用配置集合
+     */
+    List<MerchantAppConfig> selectMerchantAppConfigList(MerchantAppConfig merchantAppConfig);
+
+    /**
+     * 新增商户应用配置
+     *
+     * @param merchantAppConfig 商户应用配置
+     * @return 结果
+     */
+    int insertMerchantAppConfig(MerchantAppConfig merchantAppConfig);
+
+    /**
+     * 修改商户应用配置
+     *
+     * @param merchantAppConfig 商户应用配置
+     * @return 结果
+     */
+    int updateMerchantAppConfig(MerchantAppConfig merchantAppConfig);
+
+    /**
+     * 批量删除商户应用配置
+     *
+     * @param ids 需要删除的商户应用配置主键集合
+     * @return 结果
+     */
+    int deleteMerchantAppConfigByIds(Long[] ids);
+
+    /**
+     * 删除商户应用配置信息
+     *
+     * @param id 商户应用配置主键
+     * @return 结果
+     */
+    int deleteMerchantAppConfigById(Long id);
+}

+ 8 - 2
fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderServiceImpl.java

@@ -380,15 +380,21 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService
 
         if(fsIntegralOrderMapper.insertFsIntegralOrder(order)>0){
             if (order.getPayType() != 2) {
+                // 验证积分是否足够,防止出现负数
+                long remainingIntegral = user.getIntegral() - totalIntegral;
+                if (remainingIntegral < 0) {
+                    throw new CustomException("积分不足,无法创建订单");
+                }
+                
                 //写入日志
                 FsUser userMap=new FsUser();
                 userMap.setUserId(user.getUserId());
-                userMap.setIntegral(user.getIntegral()-totalIntegral);
+                userMap.setIntegral(remainingIntegral);
                 fsUserMapper.updateFsUser(userMap);
                 FsUserIntegralLogs logs = new FsUserIntegralLogs();
                 logs.setIntegral(-totalIntegral);
                 logs.setUserId(order.getUserId());
-                logs.setBalance(userMap.getIntegral());
+                logs.setBalance(remainingIntegral);
                 logs.setLogType(5);
                 logs.setBusinessId(order.getOrderId().toString());
                 logs.setCreateTime(new Date());

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

@@ -1566,4 +1566,10 @@ public class FsUserServiceImpl implements IFsUserService {
         return fsUserMapper.selectFsUserListByPhone(phone);
     }
 
+    @Override
+    public R updatePasswordByPhone(String password, String encryptPhone) {
+        fsUserMapper.updatePasswordByPhone(password, encryptPhone);
+        return R.ok();
+    }
+
 }

+ 200 - 0
fs-service/src/main/java/com/fs/his/service/impl/MerchantAppConfigServiceImpl.java

@@ -0,0 +1,200 @@
+package com.fs.his.service.impl;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.domain.entity.SysDictType;
+import com.fs.his.domain.FsPayConfig;
+import com.fs.his.domain.MerchantAppConfig;
+import com.fs.his.mapper.MerchantAppConfigMapper;
+import com.fs.his.service.IMerchantAppConfigService;
+import com.fs.hisStore.domain.FsPayConfigScrm;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import com.fs.system.mapper.SysDictTypeMapper;
+import com.google.gson.Gson;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * 商户应用配置Service业务层处理
+ *
+ * @author fs
+ * @date 2025-12-05
+ */
+@Service
+@Slf4j
+public class MerchantAppConfigServiceImpl extends ServiceImpl<MerchantAppConfigMapper, MerchantAppConfig> implements IMerchantAppConfigService {
+
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+
+    /**
+     * 异步初始化方法
+     */
+    @PostConstruct
+    @Async("merchantInitExecutor")
+    public void init() {
+        log.info("开始异步初始化商户配置表...");
+
+        // 使用CompletableFuture进行异步初始化
+        CompletableFuture.runAsync(() -> {
+            try {
+                // 延迟5秒,等待数据库连接就绪
+                Thread.sleep(5000);
+                Integer count = baseMapper.checkTableExists();
+                if (ObjectUtil.isNotNull(count)&&count>0) {
+                    return;
+                }
+                // 1. 检查并创建表
+                initMerchantTable();
+
+            } catch (Exception e) {
+                log.error("初始化商户配置表失败", e);
+            }
+        });
+    }
+
+    /**
+     * 初始化商户配置表
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public void initMerchantTable() {
+        try {
+            // 检查表是否存在
+            log.info("商户配置表不存在,开始创建...");
+            baseMapper.createMerchantAppConfigTable();
+            log.info("商户配置表创建成功");
+        } catch (Exception e) {
+            log.error("初始化商户配置表失败", e);
+            throw e;
+        }
+    }
+    /**
+     * 查询商户应用配置
+     *
+     * @param id 商户应用配置主键
+     * @return 商户应用配置
+     */
+    @Override
+    public MerchantAppConfig selectMerchantAppConfigById(Long id)
+    {
+        return baseMapper.selectMerchantAppConfigById(id);
+    }
+
+    /**
+     * 查询商户应用配置列表
+     *
+     * @param merchantAppConfig 商户应用配置
+     * @return 商户应用配置
+     */
+    @Override
+    public List<MerchantAppConfig> selectMerchantAppConfigList(MerchantAppConfig merchantAppConfig)
+    {
+        return baseMapper.selectMerchantAppConfigList(merchantAppConfig);
+    }
+
+    /**
+     * 新增商户应用配置
+     *
+     * @param merchantAppConfig 商户应用配置
+     * @return 结果
+     */
+    @Override
+    public int insertMerchantAppConfig(MerchantAppConfig merchantAppConfig)
+    {
+        FsPayConfig fsPayConfig = JSON.parseObject(merchantAppConfig.getDataJson(), FsPayConfig.class);
+        switch (merchantAppConfig.getMerchantType()){
+            case "yb": // 易宝
+                merchantAppConfig.setMerchantId(fsPayConfig.getYbAccount());
+                merchantAppConfig.setCallbackUrl(fsPayConfig.getYbNotifyUrl());
+
+                break;
+            case "tz": // 台州
+                merchantAppConfig.setMerchantId(fsPayConfig.getTzPlatMerCstNo());
+                merchantAppConfig.setCallbackUrl(fsPayConfig.getTzPayDecrypt());
+                break;
+            case "wx": // 微信
+                merchantAppConfig.setMerchantId(fsPayConfig.getWxMchId());
+                merchantAppConfig.setCallbackUrl(fsPayConfig.getNotifyUrlScrm());
+                break;
+            case "hf": // 汇付
+                merchantAppConfig.setMerchantId(fsPayConfig.getHuifuId());
+                merchantAppConfig.setCallbackUrl(fsPayConfig.getHfPayNotifyUrl());
+                break;
+            default:
+                throw new RuntimeException("商户类型错误");
+        }
+
+        return baseMapper.insertMerchantAppConfig(merchantAppConfig);
+    }
+
+    /**
+     * 修改商户应用配置
+     *
+     * @param merchantAppConfig 商户应用配置
+     * @return 结果
+     */
+    @Override
+    public int updateMerchantAppConfig(MerchantAppConfig merchantAppConfig)
+    {
+        FsPayConfig fsPayConfig = JSON.parseObject(merchantAppConfig.getDataJson(), FsPayConfig.class);
+        switch (merchantAppConfig.getMerchantType()){
+            case "yb": // 易宝
+                merchantAppConfig.setMerchantId(fsPayConfig.getYbAccount());
+                merchantAppConfig.setCallbackUrl(fsPayConfig.getYbNotifyUrl());
+
+                break;
+            case "tz": // 台州
+                merchantAppConfig.setMerchantId(fsPayConfig.getTzPlatMerCstNo());
+                merchantAppConfig.setCallbackUrl(fsPayConfig.getTzPayDecrypt());
+                break;
+            case "wx": // 微信
+                merchantAppConfig.setMerchantId(fsPayConfig.getWxMchId());
+                merchantAppConfig.setCallbackUrl(fsPayConfig.getNotifyUrlScrm());
+                break;
+            case "hf": // 汇付
+                merchantAppConfig.setMerchantId(fsPayConfig.getHuifuId());
+                merchantAppConfig.setCallbackUrl(fsPayConfig.getHfPayNotifyUrl());
+                break;
+            default:
+                throw new RuntimeException("商户类型错误");
+        }
+        return baseMapper.updateMerchantAppConfig(merchantAppConfig);
+    }
+
+    /**
+     * 批量删除商户应用配置
+     *
+     * @param ids 需要删除的商户应用配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteMerchantAppConfigByIds(Long[] ids)
+    {
+        return baseMapper.deleteMerchantAppConfigByIds(ids);
+    }
+
+    /**
+     * 删除商户应用配置信息
+     *
+     * @param id 商户应用配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteMerchantAppConfigById(Long id)
+    {
+        MerchantAppConfig merchantAppConfig = new MerchantAppConfig();
+        merchantAppConfig.setId( id);
+        merchantAppConfig.setIsDeleted(1L);
+        return baseMapper.updateMerchantAppConfig(merchantAppConfig);
+    }
+}

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsPayConfigScrm.java

@@ -35,4 +35,6 @@ public class FsPayConfigScrm {
     private String notifyUrlScrm;
     private String publicKeyPath;
     private String publicKeyId;
+
+
 }

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

@@ -75,8 +75,8 @@ public interface FsStoreProductScrmMapper
      */
     public int deleteFsStoreProductByIds(Long[] productIds);
     @Select({"<script> " +
-            "select p.*,pc.cate_name, fs_store.store_name from fs_store_product_scrm p left join fs_store_product_category_scrm pc on p.cate_id=pc.cate_id  " +
-            "left join fs_store on fs_store.store_id = p.store_id " +
+            "select p.*,pc.cate_name, fs.store_name from fs_store_product_scrm p left join fs_store_product_category_scrm pc on p.cate_id=pc.cate_id  " +
+            "left join fs_store fs on fs.store_id = p.store_id " +
             "where 1=1 " +
             "<if test = 'maps.productName != null and  maps.productName !=\"\"    '> " +
             "and p.product_name like CONCAT('%',#{maps.productName},'%') " +
@@ -97,53 +97,53 @@ public interface FsStoreProductScrmMapper
             "and p.store_id = #{maps.storeId} " +
             "</if>"+
             "<if test = 'maps.storeIds != null '>" +
-            "and store_id in " +
+            "and p.store_id in " +
             "<foreach collection='maps.storeIds'  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach>" +
             "</if>" +
             "<if test = 'maps.isAudit != null '> " +
             "and p.is_audit = #{maps.isAudit} " +
             "</if>" +
             "<if test='maps.drugRegCertNo != null and maps.drugRegCertNo != \"\"'>" +
-            "            AND drug_reg_cert_no LIKE CONCAT('%', #{maps.drugRegCertNo}, '%')" +
+            "    AND p.drug_reg_cert_no LIKE CONCAT('%', #{maps.drugRegCertNo}, '%')" +
             "</if>" +
             "<if test='maps.commonName != null and maps.commonName != \"\"'>" +
-            "    AND common_name LIKE CONCAT('%', #{maps.commonName}, '%')" +
+            "    AND p.common_name LIKE CONCAT('%', #{maps.commonName}, '%')" +
             "</if>" +
             "<if test='maps.dosageForm != null and maps.dosageForm != \"\"'>" +
-            "    AND dosage_form LIKE CONCAT('%', #{maps.dosageForm}, '%')" +
+            "    AND p.dosage_form LIKE CONCAT('%', #{maps.dosageForm}, '%')" +
             "</if>" +
             "<if test='maps.unitPrice != null and maps.unitPrice != \"\"'>" +
-            "    AND unit_price = #{maps.unitPrice}" +
+            "    AND p.unit_price = #{maps.unitPrice}" +
             "</if>" +
             "<if test='maps.batchNumber != null and maps.batchNumber != \"\"'>" +
-            "    AND batch_number LIKE CONCAT('%', #{maps.batchNumber}, '%')" +
+            "    AND p.batch_number LIKE CONCAT('%', #{maps.batchNumber}, '%')" +
             "</if>" +
             "<if test='maps.mah != null and maps.mah != \"\"'>" +
-            "    AND mah LIKE CONCAT('%', #{maps.mah}, '%')" +
+            "    AND p.mah LIKE CONCAT('%', #{maps.mah}, '%')" +
             "</if>" +
             "<if test='maps.mahAddress != null and maps.mahAddress != \"\"'>" +
-            "    AND mah_address LIKE CONCAT('%', #{maps.mahAddress}, '%')" +
+            "    AND p.mah_address LIKE CONCAT('%', #{maps.mahAddress}, '%')" +
             "</if>" +
             "<if test='maps.manufacturer != null and maps.manufacturer != \"\"'>" +
-            "    AND manufacturer LIKE CONCAT('%', #{maps.manufacturer}, '%')" +
+            "    AND p.manufacturer LIKE CONCAT('%', #{maps.manufacturer}, '%')" +
             "</if>" +
             "<if test='maps.manufacturerAddress != null and maps.manufacturerAddress != \"\"'>" +
-            "    AND manufacturer_address LIKE CONCAT('%', #{maps.manufacturerAddress}, '%')" +
+            "    AND p.manufacturer_address LIKE CONCAT('%', #{maps.manufacturerAddress}, '%')" +
             " </if>" +
             " <if test='maps.indications != null and maps.indications != \"\"'>" +
-            "     AND indications LIKE CONCAT('%', #{maps.indications}, '%')" +
+            "     AND p.indications LIKE CONCAT('%', #{maps.indications}, '%')" +
             " </if>" +
             " <if test='maps.dosage != null and maps.dosage != \"\"'>" +
-            "     AND dosage LIKE CONCAT('%', #{maps.dosage}, '%')" +
+            "     AND p.dosage LIKE CONCAT('%', #{maps.dosage}, '%')" +
             " </if>" +
             " <if test='maps.adverseReactions != null and maps.adverseReactions != \"\"'>" +
-            "     AND adverse_reactions LIKE CONCAT('%', #{maps.adverseReactions}, '%')" +
+            "     AND p.adverse_reactions LIKE CONCAT('%', #{maps.adverseReactions}, '%')" +
             " </if>" +
             " <if test='maps.contraindications != null and maps.contraindications != \"\"'>" +
-            "     AND contraindications LIKE CONCAT('%', #{maps.contraindications}, '%')" +
+            "     AND p.contraindications LIKE CONCAT('%', #{maps.contraindications}, '%')" +
             " </if>" +
             " <if test='maps.precautions != null and maps.precautions != \"\"'>" +
-            "     AND precautions LIKE CONCAT('%', #{maps.precautions}, '%')" +
+            "     AND p.precautions LIKE CONCAT('%', #{maps.precautions}, '%')" +
             " </if>"+
             " order by p.product_id desc "+
             "</script>"})

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

@@ -0,0 +1,108 @@
+package com.fs.hisStore.mapper;
+
+import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * 合并订单Mapper接口
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+public interface MergedOrderMapper
+{
+    /**
+     * 查询合并的订单列表(销售订单+商城订单+直播订单)
+     *
+     * @param param 查询参数
+     * @return 合并后的订单列表
+     */
+    List<MergedOrderVO> selectMergedOrderList(@Param("maps") MergedOrderQueryParam param);
+
+    /**
+     * 查询合并的订单列表(商城订单+直播订单)
+     *
+     * @param param 查询参数
+     * @return 合并后的订单列表
+     */
+    @Select({"<script> " +
+            "SELECT * FROM ( " +
+            "  SELECT " +
+            "    o.id, " +
+            "    NULL AS order_id, " +
+            "    NULL AS live_id, " +
+            "    NULL AS after_sales_id, " +
+            "    o.order_code, " +
+            "    o.pay_price, " +
+            "    o.status, " +
+            "    o.is_package, " +
+            "    o.package_json, " +
+            "    o.item_json, " +
+            "    o.delivery_id, " +
+            "    o.finish_time, " +
+            "    o.create_time, " +
+            "    NULL AS total_num, " +
+            "    NULL AS discount_money, " +
+            "    1 AS order_type " +
+            "  FROM fs_store_order_scrm o " +
+            "  WHERE 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.keyword != null and maps.keyword != \"\"'> " +
+            "    AND o.order_code LIKE CONCAT('%', #{maps.keyword}, '%') " +
+            "  </if> " +
+            "  <if test = 'maps.deliveryStatus != null'> " +
+            "    AND o.delivery_status = #{maps.deliveryStatus} " +
+            "  </if> " +
+            "  <if test = 'maps.userId != null'> " +
+            "    AND o.user_id = #{maps.userId} " +
+            "  </if> " +
+            "  UNION ALL " +
+            "  SELECT " +
+            "    NULL AS id, " +
+            "    o.order_id, " +
+            "    o.live_id, " +
+            "    a.id AS after_sales_id, " +
+            "    o.order_code, " +
+            "    o.pay_price, " +
+            "    o.status, " +
+            "    NULL AS is_package, " +
+            "    NULL AS package_json, " +
+            "    o.item_json, " +
+            "    o.delivery_sn AS delivery_id, " +
+            "    o.finish_time, " +
+            "    o.create_time, " +
+            "    o.total_num, " +
+            "    o.discount_money, " +
+            "    2 AS order_type " +
+            "  FROM live_order o " +
+            "  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 " +
+            "  WHERE o.is_del = 0 " +
+            "  <if test = 'maps.status != null and maps.status != \"\"'> " +
+            "    AND o.status = #{maps.status} " +
+            "  </if> " +
+            "  <if test = 'maps.keyword != null and maps.keyword != \"\"'> " +
+            "    AND o.order_code LIKE CONCAT('%', #{maps.keyword}, '%') " +
+            "  </if> " +
+            "  <if test = 'maps.deliveryStatus != null'> " +
+            "    AND o.delivery_status = #{maps.deliveryStatus} " +
+            "  </if> " +
+            "  <if test = 'maps.userId != null'> " +
+            "    AND o.user_id = #{maps.userId} " +
+            "  </if> " +
+            ") AS merged_orders " +
+            "ORDER BY create_time DESC " +
+            "</script>"})
+    List<FsMergedOrderListQueryVO> selectMergedOrderListVO(@Param("maps") FsMyStoreOrderQueryParam param);
+}
+

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

@@ -0,0 +1,28 @@
+package com.fs.hisStore.service;
+
+import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
+
+import java.util.List;
+
+/**
+ * 合并订单Service接口
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+public interface IMergedOrderService
+{
+    /**
+     * 查询合并的订单列表(商城订单+直播订单)
+     *
+     * @param param 查询参数
+     * @return 合并后的订单列表
+     */
+    List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param);
+
+    List<MergedOrderVO> selectMergedOrderList(MergedOrderQueryParam param);
+}
+

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

@@ -5248,10 +5248,10 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         String uuid = IdUtil.randomUUID();
         redisCache.setCacheObject("createOrderKey:" + uuid, companyUser.getCompanyId() + "-" + companyUser.getUserId(), 24, TimeUnit.HOURS);
 
-        // 这里的carts是购物车信息,价格取的套餐包价格
-        for (FsStoreCartQueryVO vo : carts) {
-            vo.setPrice(storeProductPackage.getPayMoney());
-        }
+//        // 这里的carts是购物车信息,价格取的套餐包价格
+//        for (FsStoreCartQueryVO vo : carts) {
+//            vo.setPrice(storeProductPackage.getPayMoney());
+//        }
         redisCache.setCacheObject("orderCarts:" + uuid, carts, 24, TimeUnit.HOURS);
         if (orderType != null || orderMedium != null) {
             FsStoreOrderScrm fsStoreOrder = new FsStoreOrderScrm();

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

@@ -1514,12 +1514,38 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         FsStoreProductScrm fsStoreProductScrm = fsStoreProductMapper.selectFsStoreProductById(productId);
         if (fsStoreProductScrm == null) return R.error("商品不存在");
 
+        // 查询原商品的规格属性与属性值
+        List<FsStoreProductAttrScrm> attrList = fsStoreProductAttrMapper.selectFsStoreProductAttrByProductId(productId);
+        List<FsStoreProductAttrValueScrm> attrValueList = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueByProductId(productId);
+
         FsStoreProductScrm copy = new FsStoreProductScrm();
         BeanUtils.copyProperties(fsStoreProductScrm, copy);
         copy.setProductId(null);
         copy.setIsAudit("0");
         fsStoreProductMapper.insertFsStoreProduct(copy);
 
+        // 复制规格属性
+        if (attrList != null && !attrList.isEmpty()) {
+            for (FsStoreProductAttrScrm attr : attrList) {
+                FsStoreProductAttrScrm newAttr = new FsStoreProductAttrScrm();
+                BeanUtils.copyProperties(attr, newAttr);
+                newAttr.setId(null);
+                newAttr.setProductId(copy.getProductId());
+                fsStoreProductAttrMapper.insertFsStoreProductAttr(newAttr);
+            }
+        }
+
+        // 复制属性值
+        if (attrValueList != null && !attrValueList.isEmpty()) {
+            for (FsStoreProductAttrValueScrm val : attrValueList) {
+                FsStoreProductAttrValueScrm newVal = new FsStoreProductAttrValueScrm();
+                BeanUtils.copyProperties(val, newVal);
+                newVal.setId(null);
+                newVal.setProductId(copy.getProductId());
+                fsStoreProductAttrValueMapper.insertFsStoreProductAttrValue(newVal);
+            }
+        }
+
         return R.ok();
 
     }

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

@@ -68,7 +68,12 @@ public class FsUserAddressScrmServiceImpl implements IFsUserAddressScrmService
     {
         fsUserAddress.setDetail(fsUserAddress.getDetail().trim());
         fsUserAddress.setCreateTime(DateUtils.getNowDate());
-        return fsUserAddressMapper.insertFsUserAddress(fsUserAddress);
+        int result = fsUserAddressMapper.insertFsUserAddress(fsUserAddress);
+        // 将自增id赋值给addressId字段
+        if (result > 0 && fsUserAddress.getId() != null) {
+            fsUserAddress.setAddressId(fsUserAddress.getId());
+        }
+        return result;
     }
 
     /**

+ 134 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java

@@ -0,0 +1,134 @@
+package com.fs.hisStore.service.impl;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONUtil;
+import com.fs.common.utils.StringUtils;
+import com.fs.hisStore.enums.OrderInfoEnum;
+import com.fs.hisStore.mapper.MergedOrderMapper;
+import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.service.IMergedOrderService;
+import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.hisStore.vo.FsStoreOrderItemVO;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
+import com.fs.store.config.StoreConfig;
+import com.fs.system.service.ISysConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * 合并订单Service实现类
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Service
+public class MergedOrderServiceImpl implements IMergedOrderService
+{
+    @Autowired
+    private MergedOrderMapper mergedOrderMapper;
+
+    /*
+     * 后端合并
+     * */
+    @Override
+    public List<MergedOrderVO> selectMergedOrderList(MergedOrderQueryParam param)
+    {
+        List<MergedOrderVO> list = mergedOrderMapper.selectMergedOrderList(param);
+
+        // 处理商品JSON
+        for (MergedOrderVO vo : list)
+        {
+            if (StringUtils.isNotEmpty(vo.getItemJson()))
+            {
+                try
+                {
+                    JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+                    if (jsonArray != null && jsonArray.size() > 0)
+                    {
+                        vo.setItems(jsonArray);
+                    }
+                }
+                catch (Exception e)
+                {
+                    // JSON解析失败,忽略
+                }
+            }
+        }
+
+        return list;
+    }
+
+
+    @Autowired
+    private ISysConfigService configService;
+
+    /*
+     * 小程序合并
+     * */
+    @Override
+    public List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param)
+    {
+        List<FsMergedOrderListQueryVO> list = mergedOrderMapper.selectMergedOrderListVO(param);
+
+        for (FsMergedOrderListQueryVO vo : list)
+        {
+
+            // 处理商品JSON
+            if (StringUtils.isNotEmpty(vo.getItemJson()))
+            {
+                List<FsStoreOrderItemVO> items = new ArrayList<>();
+                if (2 == vo.getOrderType()) {
+                    FsStoreOrderItemVO bean = JSONUtil.toBean(vo.getItemJson(), FsStoreOrderItemVO.class);
+                    items.add(bean);
+                    vo.setItems(items);
+                }else {
+                    JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+                    items = JSONUtil.toList(jsonArray, FsStoreOrderItemVO.class);
+                    if (items != null && items.size() > 0)
+                    {
+                        vo.setItems(items);
+                    }
+                }
+
+
+            }
+
+            // 处理是否可以申请售后
+            vo.setIsAfterSales(0);
+            if (vo.getStatus() != null && vo.getStatus().equals(OrderInfoEnum.STATUS_3.getValue()))
+            {
+                // 已完成订单
+                vo.setIsAfterSales(1);
+                if (vo.getFinishTime() != null)
+                {
+                    String json = configService.selectConfigByKey("his.store");
+                    if (StringUtils.isNotEmpty(json))
+                    {
+                        StoreConfig storeConfig = JSONUtil.toBean(json, StoreConfig.class);
+                        if (storeConfig != null && storeConfig.getStoreAfterSalesDay() != null && storeConfig.getStoreAfterSalesDay() > 0)
+                        {
+                            // 判断完成时间是否超过指定时间
+                            Calendar calendar = new GregorianCalendar();
+                            calendar.setTime(vo.getFinishTime());
+                            calendar.add(Calendar.DATE, storeConfig.getStoreAfterSalesDay());
+                            if (calendar.getTime().getTime() < new Date().getTime())
+                            {
+                                vo.setIsAfterSales(0);
+                            }
+                        }
+                    }
+                }
+            }
+            else if (vo.getStatus() != null && (vo.getStatus() == 1 || vo.getStatus() == 2))
+            {
+                vo.setIsAfterSales(1);
+            }
+        }
+
+        return list;
+    }
+}
+

+ 78 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsMergedOrderListQueryVO.java

@@ -0,0 +1,78 @@
+package com.fs.hisStore.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 合并订单列表查询VO(商城订单+直播订单)
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class FsMergedOrderListQueryVO implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 订单ID */
+    private Long id;
+
+    /** 订单ID(直播订单使用) */
+    private Long orderId;
+
+    /** 直播ID(直播订单使用) */
+    private Long liveId;
+
+    /** 售后ID(直播订单使用) */
+    private Long afterSalesId;
+
+    /** 订单号 */
+    private String orderCode;
+
+    /** 实际支付金额 */
+    private BigDecimal payPrice;
+
+    /** 订单状态 */
+    private Integer status;
+
+    /** 是否套餐 */
+    private Integer isPackage;
+
+    /** 套餐JSON */
+    private String packageJson;
+
+    /** 商品JSON */
+    private String itemJson;
+
+    /** 物流单号 */
+    private String deliveryId;
+
+    /** 是否可以申请售后 */
+    private Integer isAfterSales;
+
+    /** 完成时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date finishTime;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 总数量(直播订单使用) */
+    private Integer totalNum;
+
+    /** 优惠金额(直播订单使用) */
+    private Integer discountMoney;
+
+    /** 订单类型:1-商城订单,2-直播订单 */
+    private Integer orderType;
+
+    /** 订单商品列表 */
+    private List<FsStoreOrderItemVO> items;
+}
+

+ 37 - 0
fs-service/src/main/java/com/fs/live/enums/LiveGoodsAddErrorEnum.java

@@ -0,0 +1,37 @@
+package com.fs.live.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 直播商品添加失败原因枚举
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Getter
+@AllArgsConstructor
+public enum LiveGoodsAddErrorEnum {
+
+    /**
+     * 未上架
+     */
+    NOT_SHELVED("未上架"),
+
+    /**
+     * 未审核
+     */
+    NOT_AUDITED("未审核"),
+
+    /**
+     * 已删除
+     */
+    DELETED("已删除");
+
+    /**
+     * 错误描述
+     */
+    private String desc;
+
+}
+

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

@@ -0,0 +1,85 @@
+package com.fs.live.param;
+
+import com.fs.common.param.BaseQueryParam;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 合并订单查询参数
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedOrderQueryParam extends BaseQueryParam implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 订单状态 */
+    private String status;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 公司ID */
+    private Long companyId;
+
+    /** 部门ID */
+    private Long deptId;
+
+    /** 订单号 */
+    private String orderCode;
+
+    /** 订单号列表(多个订单号用逗号分隔) */
+    private String orderCodeList;
+
+    /** 运单号 */
+    private String deliveryId;
+
+    /** 银行交易流水号 */
+    private String bankTransactionId;
+
+    /** 手机号 */
+    private String userPhone;
+
+    /** 收件人 */
+    private String realName;
+
+    /** 产品名称 */
+    private String productName;
+
+    /** 物流状态 */
+    private Integer deliveryStatus;
+
+    /** 物流结算状态 */
+    private Integer deliveryPayStatus;
+
+    /** 支付方式 */
+    private String payType;
+
+    /** 下单时间范围 */
+    private String createTimeRange;
+
+    /** 支付时间范围 */
+    private String payTimeRange;
+
+    /** 发货时间范围 */
+    private String deliverySendTimeRange;
+
+    /** 回单时间范围 */
+    private String deliveryImportTimeRange;
+
+    /** 小程序AppId */
+    private String appId;
+
+    /** 订单类型筛选:1-销售订单,2-商城订单,3-直播订单,null-全部 */
+    private Integer orderTypeFilter;
+
+    /** 销售名称(company_user表的user_name) */
+    private String salesName;
+
+    /** 员工姓名(company_user表的nick_name) */
+    private String companyUserNickName;
+}
+

+ 96 - 2
fs-service/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java

@@ -3,6 +3,7 @@ package com.fs.live.service.impl;
 
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.DateUtils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.his.domain.FsStoreProduct;
@@ -10,6 +11,7 @@ import com.fs.his.mapper.FsStoreProductMapper;
 import com.fs.hisStore.domain.FsStoreProductScrm;
 import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
 import com.fs.live.domain.LiveGoods;
+import com.fs.live.enums.LiveGoodsAddErrorEnum;
 import com.fs.live.mapper.LiveGoodsMapper;
 import com.fs.live.service.ILiveAutoTaskService;
 import com.fs.live.service.ILiveGoodsService;
@@ -91,8 +93,8 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         liveGoods.setUpdateTime(DateUtils.getNowDate());
         LiveGoods existGoods = baseMapper.selectLiveGoodsByGoodsId(liveGoods.getGoodsId());
         if (liveGoods.getStock() != null) {
-            if(liveGoods.getProductId() == null) return R.error("店铺已停止售卖商品!");
-            FsStoreProductScrm fsStoreProduct = fsStoreProductMapper.selectFsStoreProductById(liveGoods.getProductId());
+            if(existGoods.getProductId() == null) return R.error("店铺已停止售卖商品!");
+            FsStoreProductScrm fsStoreProduct = fsStoreProductMapper.selectFsStoreProductById(existGoods.getProductId());
             if(fsStoreProduct == null) return R.error("商品不存在");
             if(fsStoreProduct.getIsShow() == 0 || existGoods.getStatus() == 0) return R.error("商品已下架");
             if(fsStoreProduct.getStock() < liveGoods.getStock()) return R.error("商品库存不足");
@@ -223,6 +225,52 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         //  查询商品信息列表(假设返回 List<StoreProduct>)
         List<FsStoreProductScrm> productInfoList = fsStoreProductMapper.selectFsStoreProductByProductIds(productIdsLong);
 
+        // 检查商品状态:未上架、未审核、已删除
+        StringBuilder errorMsg = new StringBuilder();
+        List<String> notShelvedProducts = new ArrayList<>();
+        List<String> notAuditedProducts = new ArrayList<>();
+        List<String> deletedProducts = new ArrayList<>();
+
+        for (FsStoreProductScrm product : productInfoList) {
+            // 检查是否未上架
+            if (product.getIsShow() == null || product.getIsShow() == 0) {
+                notShelvedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否未审核
+            if (product.getIsAudit() == null || !"1".equals(product.getIsAudit())) {
+                notAuditedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否已删除
+            if (product.getIsDel() != null && product.getIsDel() == 1) {
+                deletedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+        }
+
+        // 构建错误信息
+        if (!notShelvedProducts.isEmpty()) {
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_SHELVED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notShelvedProducts));
+        }
+        if (!notAuditedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_AUDITED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notAuditedProducts));
+        }
+        if (!deletedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.DELETED.getDesc()).append(":");
+            errorMsg.append(String.join("、", deletedProducts));
+        }
+
+        // 如果有错误,返回错误信息
+        if (errorMsg.length() > 0) {
+            return R.error("添加失败原因:" + errorMsg.toString());
+        }
+
         //  转换为 LiveGoods 并批量插入
         List<LiveGoods> liveGoodsList = productInfoList.stream()
                 .map(product -> {
@@ -265,6 +313,52 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         //  查询商品信息列表(假设返回 List<StoreProduct>)
         List<FsStoreProductScrm> productInfoList = fsStoreProductMapper.selectFsStoreProductByProductIds(productIdsLong);
 
+        // 检查商品状态:未上架、未审核、已删除
+        StringBuilder errorMsg = new StringBuilder();
+        List<String> notShelvedProducts = new ArrayList<>();
+        List<String> notAuditedProducts = new ArrayList<>();
+        List<String> deletedProducts = new ArrayList<>();
+
+        for (FsStoreProductScrm product : productInfoList) {
+            // 检查是否未上架
+            if (product.getIsShow() == null || product.getIsShow() == 0) {
+                notShelvedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否未审核
+            if (product.getIsAudit() == null || !"1".equals(product.getIsAudit())) {
+                notAuditedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否已删除
+            if (product.getIsDel() != null && product.getIsDel() == 1) {
+                deletedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+        }
+
+        // 构建错误信息
+        if (!notShelvedProducts.isEmpty()) {
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_SHELVED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notShelvedProducts));
+        }
+        if (!notAuditedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_AUDITED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notAuditedProducts));
+        }
+        if (!deletedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.DELETED.getDesc()).append(":");
+            errorMsg.append(String.join("、", deletedProducts));
+        }
+
+        // 如果有错误,抛出异常(因为返回类型是int,不能返回R.error)
+        if (errorMsg.length() > 0) {
+            throw new ServiceException("添加失败原因:" + errorMsg.toString());
+        }
+
         //  转换为 LiveGoods 并批量插入
         List<LiveGoods> liveGoodsList = productInfoList.stream()
                 .map(product -> {

+ 34 - 3
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -748,7 +748,11 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
             order.setPayTime(LocalDateTime.now());
             order.setIsPay("1");
             baseMapper.updateLiveOrder(order);
-            this.createOmsOrderCall(order);
+            try {
+                this.createOmsOrderCall(order);
+            } catch (Exception e) {
+                log.error("推送erp失败:{}",e.getMessage());
+            }
             return "SUCCESS";
         }catch (Exception e){
             log.info("支付错误:"+e.getMessage());
@@ -1875,8 +1879,12 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         }
         BigDecimal payPrice = BigDecimal.ZERO;
         BigDecimal payDelivery = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (param.getCityId() != null) {
             payDelivery = handleDeliveryMoney(param.getCityId(), fsStoreProduct, param.getTotalNum());
+            if (payDelivery.compareTo(badCode) == 0) {
+                throw new ServiceException("偏远地区暂不可购买");
+            }
             totalPrice = totalPrice.add(payDelivery);
         }
         return LiveOrderComputeDTO.builder().payPrice(payPrice)
@@ -1944,6 +1952,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 //        payPrice = payPrice.add(serviceFee);
         // 生成
         BigDecimal deliveryMoney = handleDeliveryMoney(liveOrder);
+        if (deliveryMoney.compareTo(BigDecimal.valueOf(-1)) == 0) {
+            return R.error("偏远地区暂不可购买");
+        }
         totalPrice = totalPrice.add(deliveryMoney);
         liveOrder.setDiscountMoney(totalPrice);
 
@@ -2024,8 +2035,12 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         totalPrice = totalPrice.add(payPrice);
         BigDecimal payDelivery = BigDecimal.ZERO;
         BigDecimal deductionPrice = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (param.getCityId() != null) {
             payDelivery = handleDeliveryMoney(param.getCityId(), fsStoreProduct, param.getTotalNum());
+            if (payDelivery.compareTo(badCode) == 0) {
+                throw new ServiceException("偏远地区暂不可购买");
+            }
             payPrice = payPrice.add(payDelivery);
         }
 
@@ -2468,6 +2483,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
     private BigDecimal handleDeliveryMoney(Long cityId, FsStoreProductScrm fsStoreProduct, String totalNumSize) {
         BigDecimal storePostage = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (ObjectUtil.isNull(fsStoreProduct.getTempId())) {
             return storePostage;
         }
@@ -2483,6 +2499,11 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         String cityIds = String.join(",", citys.stream()
                 .map(String::valueOf).collect(Collectors.toList()));
         List<FsShippingTemplatesRegionScrm> shippingTemplatesRegionList = shippingTemplatesRegionService.selectFsShippingTemplatesRegionListByTempIdsAndCityIds(ids,cityIds);
+        // 有运费模板,但当前城市没有匹配的区域
+        if (shippingTemplatesList != null && !shippingTemplatesList.isEmpty()
+                && (shippingTemplatesRegionList == null || shippingTemplatesRegionList.isEmpty())) {
+            return badCode;
+        }
         Map<Long, Integer> shippingTemplatesMap = shippingTemplatesList
                 .stream()
                 .collect(Collectors.toMap(FsShippingTemplatesScrm::getId,
@@ -2517,7 +2538,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         FsShippingTemplatesRegionScrm shippingTemplatesRegion = shippingTemplatesRegionMap.get(tempId);
         if (shippingTemplatesRegion == null) {
             log.error("没有找到运费模板");
-            return storePostage;
+            return badCode;
         }
         BigDecimal price = NumberUtil.round(NumberUtil.mul(totalNum, fsStoreProduct.getPrice()), 2);
 
@@ -3478,8 +3499,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         FsStoreProductScrm fsStoreProduct = fsStoreProductService.selectFsStoreProductById(liveOrder.getProductId());
         LiveGoods goods = liveGoodsMapper.selectLiveGoodsByProductId(liveOrder.getLiveId(), liveOrder.getProductId());
         if(goods == null) return R.error("当前商品不存在");
-        if(fsStoreProduct == null) return R.error("店铺已下架商品,购买失败");
+        if(fsStoreProduct == null) return R.error("商品不存在,购买失败");
         if(fsStoreProduct.getIsShow() == 0 || goods.getStatus() == 0) return R.error("商品已下架,购买失败");
+        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("抱歉,这款商品已被抢光,暂时无库存~");
@@ -3537,6 +3559,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 //        payPrice = payPrice.add(serviceFee);
         // 生成
         BigDecimal deliveryMoney = handleDeliveryMoney(liveOrder);
+        if (deliveryMoney.compareTo(BigDecimal.valueOf(-1)) == 0) {
+            return R.error("偏远地区暂不可购买");
+        }
         payPrice = payPrice.add(deliveryMoney);
         liveOrder.setDiscountMoney(BigDecimal.ZERO);
 
@@ -3616,6 +3641,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
     private BigDecimal handleDeliveryMoney(LiveOrder liveOrder) {
         BigDecimal storePostage = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if(liveOrder.getUserAddress() == null || liveOrder.getCityId() == null) return storePostage;
         List<Long> citys = new ArrayList<>();
         citys.add(liveOrder.getCityId());
@@ -3628,6 +3654,11 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
         //获取运费模板区域列表按照城市排序
         List<FsShippingTemplatesRegionScrm> shippingTemplatesRegionList = shippingTemplatesRegionService.selectFsShippingTemplatesRegionListByTempIdsAndCityIds(ids, StringUtils.join(citys, ","));
+        // 有运费模板但城市未匹配到区域,返回 badCode
+        if (shippingTemplatesList != null && !shippingTemplatesList.isEmpty()
+                && (shippingTemplatesRegionList == null || shippingTemplatesRegionList.isEmpty())) {
+            return badCode;
+        }
 
         //提取运费模板类型
         Map<Long, Integer> shippingTemplatesMap = shippingTemplatesList

+ 1 - 1
fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -323,7 +323,7 @@ public class LiveServiceImpl implements ILiveService
         });
         notifyTask.setData(JSON.toJSONString(data));
 //        return R.ok("success");
-//        liveMiniprogramSubNotifyTaskMapper.insert(notifyTask);
+        liveMiniprogramSubNotifyTaskMapper.insert(notifyTask);
 
         return R.ok("success");
     }

+ 2 - 2
fs-service/src/main/java/com/fs/live/service/impl/LiveWatchConfigServiceImpl.java

@@ -75,7 +75,7 @@ public class LiveWatchConfigServiceImpl implements ILiveWatchConfigService {
     @Override
     public int insertLiveWatchConfig(String userId, String jsonConfig ,Long liveId)
     {
-        Live live = liveService.selectLiveByLiveId(liveId);
+        Live live = liveService.selectLiveDbByLiveId(liveId);
         if(live == null){
             return 0;
         }
@@ -95,7 +95,7 @@ public class LiveWatchConfigServiceImpl implements ILiveWatchConfigService {
     @Override
     public int updateLiveWatchConfig(String jsonConfig, Long liveId)
     {
-        Live live = liveService.selectLiveByLiveId(liveId);
+        Live live = liveService.selectLiveDbByLiveId(liveId);
         if(live == null){
             return 0;
         }

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

@@ -0,0 +1,195 @@
+package com.fs.live.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 合并订单VO(销售订单+商城订单+直播订单)
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedOrderVO implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 订单ID */
+    private Long id;
+
+    /** 订单ID(直播订单使用) */
+    private Long orderId;
+
+    /** 直播ID(直播订单使用) */
+    private Long liveId;
+
+    /** 售后ID(直播订单使用) */
+    private Long afterSalesId;
+
+    /** 订单号 */
+    private String orderCode;
+
+    /** 实际支付金额 */
+    private BigDecimal payPrice;
+
+    /** 会员等级 */
+    private Integer userLevel;
+
+    /** 实付金额 */
+    private BigDecimal payMoney;
+
+    /** 优惠金额(直播订单使用) */
+    private Integer discountMoney;
+
+    /** 运费 */
+    private BigDecimal payDelivery;
+
+    /** 成本价 */
+    private BigDecimal cost;
+
+    /** 订单状态 */
+    @Excel(name = "订单状态",dictType="sys_live_order_status")
+    private Integer status;
+
+    /** 订单总价 */
+    private BigDecimal totalPrice;
+
+    /** 是否套餐 */
+    private Integer isPackage;
+
+    /** 套餐JSON */
+    private String packageJson;
+
+    /** 商品JSON */
+    private String itemJson;
+
+    /** 物流单号 */
+    private String deliveryId;
+
+    /** 是否可以申请售后 */
+    private Integer isAfterSales;
+
+    /** 完成时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date finishTime;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 支付时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date payTime;
+
+    /** 发货时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date deliverySendTime;
+
+    /** 总数量(直播订单使用) */
+    private Integer totalNum;
+
+
+
+    /** 订单类型:1-销售订单,2-商城订单,3-直播订单 */
+    private Integer orderType;
+
+    /** 订单类型名称 */
+    private String orderTypeName;
+
+    /** 公司名称 */
+    private String companyName;
+
+    /** 销售名称(company_user表的user_name) */
+    private String salesName;
+
+    /** 销售创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date salesCreateTime;
+
+    /** 销售手机 */
+    private String salesPhone;
+
+    /** 员工昵称(company_user表的nick_name) */
+    private String companyUserNickName;
+
+    /** 用户昵称 */
+    private String nickname;
+
+    /** 用户手机号 */
+    private String phone;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 用户累计成交避暑 */
+    private Integer userOrderCount;
+
+    /** 用户累计成交避暑 */
+    private BigDecimal userTotalAmount;
+
+    /** 收货人姓名 */
+    private String realName;
+
+    /** 收货人电话 */
+    private String userPhone;
+
+    /** 店铺ID */
+    private Long storeId;
+
+    /** 店铺名字 */
+    private String storeName;
+
+    /** 产品ID */
+    private Long productId;
+
+    /** 产品名字 */
+    private String productName;
+    /** 商品规格 */
+    private String productSpec;
+
+    /** 商品编码 */
+    private String barCode;
+
+    /** 商品分类 */
+    private String cateName;
+
+
+
+    /** 收货地址 */
+    private String userAddress;
+
+    /** 支付方式 */
+    private String payType;
+
+    /** 物流状态 */
+    private Integer deliveryStatus;
+
+    /** 物流结算状态 */
+    private Integer deliveryPayStatus;
+
+
+
+    /** 订单商品列表 */
+    private List<?> items;
+
+    /** 小程序名称 */
+    private String miniProgramName;
+
+    /** ERP账号 */
+    private String erpAccount;
+
+    /** ERP电话 */
+    private String erpPhone;
+
+    /** 银行交易流水号 */
+    private String bankTransactionId;
+
+
+}
+

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

@@ -48,6 +48,8 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
 
     public List<QwExternalContact> selectQwExternalContactByIds(@Param("ids") List<Long> ids);
 
+    public List<QwExternalContact> selectQwExternalContactByIdsStatus(@Param("ids") List<Long> ids);
+
     @Select("SELECT id,stage_status,name,fs_user_id from qw_external_contact where id=#{id}")
     public QwExternalContact selectQwExternalContactByIdForStageStatus(@Param("id") Long id);
 

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

@@ -1258,10 +1258,11 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
             List<Future<?>> futures = new ArrayList<>();
 
 
-            // 1. 批量查询所有用户数据
+            // 1. 批量查询所有用户数据(流失客户不打了)
             List<QwExternalContact> contacts;
             try {
-                contacts = qwExternalContactMapper.selectQwExternalContactByIds(param.getUserIds());
+//                contacts = qwExternalContactMapper.selectQwExternalContactByIds(param.getUserIds());
+                contacts = qwExternalContactMapper.selectQwExternalContactByIdsStatus(param.getUserIds());
                 if (contacts == null || contacts.isEmpty()) {
                     return R.error("成功:0,失败:" + param.getUserIds().size());
                 }

+ 2 - 1
fs-service/src/main/java/com/fs/sop/domain/QwSopTemp.java

@@ -113,8 +113,9 @@ public class QwSopTemp implements Serializable
 
     @TableField(exist = false)
     private List<String> companyUserIds=new ArrayList<>();
-
+    @TableField(exist = false)
     private Integer pageNum;
+    @TableField(exist = false)
     private Integer pageSize;
 
     public List<String> getCompanyUserIds() {

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

@@ -68,6 +68,9 @@ public class QwSopTempRules{
     @Excel(name = "排序")
     private Integer sorts;
 
+    @TableField(exist = false)
+    private Long courseSort;
+
     @TableField(exist = false)
     private List<QwSopTempContent> settingList;
     @TableField(exist = false)

+ 19 - 0
fs-service/src/main/java/com/fs/sop/mapper/QwSopTempRulesMapper.java

@@ -70,6 +70,25 @@ public interface QwSopTempRulesMapper extends BaseMapper<QwSopTempRules> {
     @Select("select * from qw_sop_temp_rules where day_num is null ")
     List<QwSopTempRules> rulesNull();
 
+    @DataSource(DataSourceType.SOP)
+    @Select("<script>" +
+            "select tr.* from qw_sop_temp_rules tr " +
+            "left join qw_sop_temp st on tr.temp_id=st.id " +
+            " where tr.course_id = #{courseId}" +
+            " and st.send_type=11 and st.status = 1  " +
+            "</script>")
+    List<QwSopTempRules> listByCourseId(@Param("courseId") Long courseId);
+
+    @DataSource(DataSourceType.SOP)
+    @Select("<script>" +
+            "select tr.* from qw_sop_temp_rules tr " +
+            "left join qw_sop_temp st on tr.temp_id=st.id " +
+            " where tr.course_id = #{courseId}" +
+            " and tr.temp_id=#{tempId} " +
+            " and st.send_type=11 and st.status = 1  " +
+            "</script>")
+    List<QwSopTempRules> listByCourseIdAndTempId(@Param("courseId") Long courseId,@Param("tempId") String tempId);
+
 
 
     void deleteByTempId(@Param("id") String id);

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

@@ -92,5 +92,7 @@ public interface IQwSopTempContentService extends IService<QwSopTempContent>{
 
     List<QwSopTempContent> listByTempIds(Collection<String> tempIds);
 
+    List<QwSopTempContent> listByTempId(String tempId);
+
     void removeByWrapper(LambdaQueryWrapper<QwSopTempContent> wrapper);
 }

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

@@ -44,6 +44,7 @@ public interface IQwSopTempDayService extends IService<QwSopTempDay> {
     void removeByWrapper(LambdaQueryWrapper<QwSopTempDay> wrapper);
 
     List<QwSopTempDay> listByTempIds(LambdaQueryWrapper<QwSopTempDay> wrapper);
+    List<QwSopTempDay> listByTempById(LambdaQueryWrapper<QwSopTempDay> wrapper);
 
     void updateByWrapper(UpdateWrapper<QwSopTempDay> wrapper);
 }

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

@@ -94,6 +94,8 @@ public interface IQwSopTempRulesService extends IService<QwSopTempRules>{
 
     List<QwSopTempRules> listByCourseId(Long courseId);
 
+    List<QwSopTempRules> listByCourseIdAndTempId(Long courseId,String tempId);
+
     void removeByTempIds(Collection<String> tempIds);
 
     void removeByWrapper(LambdaQueryWrapper<QwSopTempRules> wrapper);

+ 6 - 0
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempContentServiceImpl.java

@@ -165,6 +165,12 @@ public class QwSopTempContentServiceImpl extends ServiceImpl<QwSopTempContentMap
         return baseMapper.selectList(new QueryWrapper<QwSopTempContent>().in("temp_id", tempIds));
     }
 
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public List<QwSopTempContent> listByTempId(String tempId) {
+        return baseMapper.selectList(new QueryWrapper<QwSopTempContent>().eq("temp_id", tempId));
+    }
+
     @Override
     @DataSource(DataSourceType.SOP)
     public void removeByWrapper(LambdaQueryWrapper<QwSopTempContent> wrapper) {

+ 7 - 0
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempDayServiceImpl.java

@@ -109,6 +109,13 @@ public class QwSopTempDayServiceImpl extends ServiceImpl<QwSopTempDayMapper, QwS
         return this.list(wrapper);
     }
 
+    @Override
+    @DataSource(DataSourceType.SOP)
+    public List<QwSopTempDay> listByTempById(LambdaQueryWrapper<QwSopTempDay> wrapper) {
+        return this.list(wrapper);
+    }
+
+
     @Override
     @DataSource(DataSourceType.SOP)
     public void updateByWrapper(UpdateWrapper<QwSopTempDay> wrapper) {

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

@@ -243,9 +243,14 @@ public class QwSopTempRulesServiceImpl extends ServiceImpl<QwSopTempRulesMapper,
     }
 
     @Override
-    @DataSource(DataSourceType.SOP)
     public List<QwSopTempRules> listByCourseId(Long courseId) {
-        return list(new QueryWrapper<QwSopTempRules>().eq("course_id", courseId));
+//        return list(new QueryWrapper<QwSopTempRules>().eq("course_id", courseId));
+        return qwSopTempRulesMapper.listByCourseId(courseId);
+    }
+
+    @Override
+    public List<QwSopTempRules> listByCourseIdAndTempId(Long courseId, String tempId) {
+        return qwSopTempRulesMapper.listByCourseIdAndTempId(courseId,tempId);
     }
 
     @Override

+ 111 - 35
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fs.common.BeanCopyUtils;
 import com.fs.common.annotation.DataSource;
 import com.fs.common.enums.DataSourceType;
 import com.fs.common.exception.base.BaseException;
@@ -321,6 +322,7 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
         if (day == null) return;
         qwSopTempDayService.removeById(day.getId());
         qwSopTempContentService.removeByDayId(day.getId());
+        qwSopTempRulesService.removeByDayId(day.getId());
         reorder(day.getTempId());
     }
 
@@ -633,6 +635,7 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
         if (CollectionUtils.isEmpty(rulesList)) {
             return;
         }
+
         // 获取这些规则关联的模板ID集合
         Set<String> tempIds = rulesList.stream()
                 .map(QwSopTempRules::getTempId)
@@ -641,11 +644,7 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
 
         FsUserCourse fsUserCourse = fsUserCourseMapper.selectFsUserCourseByCourseId(courseId);
         List<FsUserCourseVideo> videoList = fsUserCourseVideoMapper.selectVideoByCourseId(fsUserCourse.getCourseId());
-        List<QwSopTemp> tempList = qwSopTempMapper.selectListByIds(tempIds);
-        List<QwSopTempContent> contentList = qwSopTempContentService.listByTempIds(tempIds);
-        List<QwSopTempDay> dayList = qwSopTempDayService.listByTempIds(new LambdaQueryWrapper<QwSopTempDay>().in(QwSopTempDay::getTempId, tempIds));
         List<Long> videoIdList = videoList.stream().map(FsUserCourseVideo::getVideoId).collect(Collectors.toList());
-        // videoList转Map key 为videoId value 为 courseSort
         Map<Long, Long> videoSortMap = videoList.stream().collect(Collectors.toMap(FsUserCourseVideo::getVideoId, FsUserCourseVideo::getCourseSort));
 
         // 将课程中已删除的视频在规则和日期中删除
@@ -656,54 +655,131 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
             qwSopTempContentService.removeByWrapper(new LambdaQueryWrapper<QwSopTempContent>().in(QwSopTempContent::getDayId, dayIdList));
         }
 
-        // 需要添加的课程视频
-        List<FsUserCourseVideo> addVideoList = videoList.stream()
-                .filter(e -> rulesList.stream().noneMatch(f -> f.getVideoId().equals(e.getVideoId()))).collect(Collectors.toList());
 
-        if (CollectionUtils.isNotEmpty(addVideoList)) {
-            for (QwSopTemp temp : tempList) {
+        tempIds.forEach(teId->{
+            QwSopTemp sopTemp = qwSopTempMapper.selectQwSopTempById(teId);
+            List<QwSopTempContent> contentList = qwSopTempContentService.listByTempId(teId);
+            List<QwSopTempDay> dayList = qwSopTempDayService.listByTempById(new LambdaQueryWrapper<QwSopTempDay>().in(QwSopTempDay::getTempId, teId));
+            List<QwSopTempRules> tempRulesList = qwSopTempRulesService.listByCourseIdAndTempId(courseId, teId);
+
+            List<FsUserCourseVideo> addVideoList = videoList.stream()
+                    .filter(e -> tempRulesList.stream()
+                            .noneMatch(f -> f.getVideoId().equals(e.getVideoId())))
+                    .collect(Collectors.toList());
+
+            if (CollectionUtils.isNotEmpty(addVideoList)) {
+
                 // 通过历史中的第一课的数据来构建
-                Optional<QwSopTempDay> first = dayList.stream().filter(e -> e.getTempId().equals(temp.getId())).findFirst();
+                Optional<QwSopTempDay> first = dayList.stream().filter(e -> e.getTempId().equals(sopTemp.getId())).findFirst();
                 if (!first.isPresent()) {
-                    break;
+                    return;
                 }
                 QwSopTempDay qwSopTempDay = first.get();
+
                 // 构造timeList timeDesc time
-                temp.setTime(LocalTime.now());
+                sopTemp.setTime(LocalTime.now());
 
-                temp.setTimeList(rulesList.stream()
-                        .filter(e -> e.getTempId().equals(temp.getId()) && Objects.equals(e.getIsOfficial(), "0")
+                sopTemp.setTimeList(tempRulesList.stream()
+                        .filter(e -> e.getTempId().equals(sopTemp.getId()) && Objects.equals(e.getIsOfficial(), "0")
                                 && Objects.equals(e.getDayId(), qwSopTempDay.getId())
                         ).map(QwSopTempRules::getTime)
                         .collect(Collectors.toList()));
 
-                temp.setTimeDesc(contentList.stream().filter(e -> e.getTempId().equals(temp.getId())
+                sopTemp.setTimeDesc(contentList.stream().filter(e -> e.getTempId().equals(sopTemp.getId())
                                 && Objects.isNull(e.getIsBindUrl())
                                 && Objects.equals(qwSopTempDay.getId(), e.getDayId())
                         )
                         .map(m -> JSONObject.parseObject(m.getContent()).getString("value")).collect(Collectors.toList()));
-                temp.setProject(fsUserCourse.getProject());
-                temp.setCourseId(courseId);
-                temp.setOpenOfficial("1");
-                qwSopTempMapper.updateQwSopTemp(temp);
-                createSopTempRules(temp, addVideoList, fsUserCourse);
-            }
-        }
+                sopTemp.setProject(fsUserCourse.getProject());
+                sopTemp.setCourseId(courseId);
+                sopTemp.setOpenOfficial("1");
+
+                qwSopTempMapper.updateQwSopTemp(sopTemp);
+
+                createSopTempRules(sopTemp, addVideoList, fsUserCourse);
+
+                // 更新排序
+                List<QwSopTempRules> newTempRulesList = qwSopTempRulesService.listByCourseIdAndTempId(courseId, teId);
+                // 整理排序
+                Long[] dayIdArray = newTempRulesList.stream().filter(e -> e.getTempId().equals(sopTemp.getId())).sorted(Comparator.comparing(a -> videoSortMap.get(a.getVideoId())))
+                        .map(QwSopTempRules::getDayId).distinct().toArray(Long[]::new);
+                for (int i = 0; i < dayIdArray.length; i++) {
+                    qwSopTempDayService.updateByWrapper(new UpdateWrapper<QwSopTempDay>()
+                            .eq("id", dayIdArray[i])
+                            .set("sorts", i + 1)
+                            .set("name", "第" + (i + 1) + "天")
+                            .set("day_num", i + 1)
+                    );
+                }
 
-        // 整理排序
-        List<QwSopTempRules> afterRuleList = qwSopTempRulesService.listByCourseId(courseId);
-        for (QwSopTemp temp : tempList) {
-            Long[] dayIdArray = afterRuleList.stream().filter(e -> e.getTempId().equals(temp.getId())).sorted(Comparator.comparing(a -> videoSortMap.get(a.getVideoId())))
-                    .map(QwSopTempRules::getDayId).distinct().toArray(Long[]::new);
-            for (int i = 0; i < dayIdArray.length; i++) {
-                qwSopTempDayService.updateByWrapper(new UpdateWrapper<QwSopTempDay>()
-                        .eq("id", dayIdArray[i])
-                        .set("sorts", i + 1)
-                        .set("name", "第" + (i + 1) + "天")
-                        .set("day_num", i + 1)
-                );
             }
-        }
+        });
+
+//        FsUserCourse fsUserCourse = fsUserCourseMapper.selectFsUserCourseByCourseId(courseId);
+//        List<FsUserCourseVideo> videoList = fsUserCourseVideoMapper.selectVideoByCourseId(fsUserCourse.getCourseId());
+//        List<QwSopTemp> tempList = qwSopTempMapper.selectListByIds(tempIds);
+//        List<QwSopTempContent> contentList = qwSopTempContentService.listByTempIds(tempIds);
+//        List<QwSopTempDay> dayList = qwSopTempDayService.listByTempIds(new LambdaQueryWrapper<QwSopTempDay>().in(QwSopTempDay::getTempId, tempIds));
+//        List<Long> videoIdList = videoList.stream().map(FsUserCourseVideo::getVideoId).collect(Collectors.toList());
+//        // videoList转Map key 为videoId value 为 courseSort
+//        Map<Long, Long> videoSortMap = videoList.stream().collect(Collectors.toMap(FsUserCourseVideo::getVideoId, FsUserCourseVideo::getCourseSort));
+
+        // 将课程中已删除的视频在规则和日期中删除
+//        Set<Long> dayIdList = rulesList.stream().filter(e -> !videoIdList.contains(e.getVideoId())).map(QwSopTempRules::getDayId).collect(Collectors.toSet());
+//        if (CollectionUtils.isNotEmpty(dayIdList)) {
+//            qwSopTempDayService.removeByWrapper(new LambdaQueryWrapper<QwSopTempDay>().in(QwSopTempDay::getId, dayIdList));
+//            qwSopTempRulesService.removeByWrapper(new LambdaQueryWrapper<QwSopTempRules>().in(QwSopTempRules::getDayId, dayIdList));
+//            qwSopTempContentService.removeByWrapper(new LambdaQueryWrapper<QwSopTempContent>().in(QwSopTempContent::getDayId, dayIdList));
+//        }
+
+        // 需要添加的课程视频
+//        List<FsUserCourseVideo> addVideoList = videoList.stream()
+//                .filter(e -> rulesList.stream().noneMatch(f -> f.getVideoId().equals(e.getVideoId()))).collect(Collectors.toList());
+
+//        if (CollectionUtils.isNotEmpty(addVideoList)) {
+//            for (QwSopTemp temp : tempList) {
+//                // 通过历史中的第一课的数据来构建
+//                Optional<QwSopTempDay> first = dayList.stream().filter(e -> e.getTempId().equals(temp.getId())).findFirst();
+//                if (!first.isPresent()) {
+//                    break;
+//                }
+//                QwSopTempDay qwSopTempDay = first.get();
+//                // 构造timeList timeDesc time
+//                temp.setTime(LocalTime.now());
+//
+//                temp.setTimeList(rulesList.stream()
+//                        .filter(e -> e.getTempId().equals(temp.getId()) && Objects.equals(e.getIsOfficial(), "0")
+//                                && Objects.equals(e.getDayId(), qwSopTempDay.getId())
+//                        ).map(QwSopTempRules::getTime)
+//                        .collect(Collectors.toList()));
+//
+//                temp.setTimeDesc(contentList.stream().filter(e -> e.getTempId().equals(temp.getId())
+//                                && Objects.isNull(e.getIsBindUrl())
+//                                && Objects.equals(qwSopTempDay.getId(), e.getDayId())
+//                        )
+//                        .map(m -> JSONObject.parseObject(m.getContent()).getString("value")).collect(Collectors.toList()));
+//                temp.setProject(fsUserCourse.getProject());
+//                temp.setCourseId(courseId);
+//                temp.setOpenOfficial("1");
+//                qwSopTempMapper.updateQwSopTemp(temp);
+//                createSopTempRules(temp, addVideoList, fsUserCourse);
+//            }
+//        }
+
+//        // 整理排序
+//        List<QwSopTempRules> afterRuleList = qwSopTempRulesService.listByCourseId(courseId);
+//        for (QwSopTemp temp : tempList) {
+//            Long[] dayIdArray = afterRuleList.stream().filter(e -> e.getTempId().equals(temp.getId())).sorted(Comparator.comparing(a -> videoSortMap.get(a.getVideoId())))
+//                    .map(QwSopTempRules::getDayId).distinct().toArray(Long[]::new);
+//            for (int i = 0; i < dayIdArray.length; i++) {
+//                qwSopTempDayService.updateByWrapper(new UpdateWrapper<QwSopTempDay>()
+//                        .eq("id", dayIdArray[i])
+//                        .set("sorts", i + 1)
+//                        .set("name", "第" + (i + 1) + "天")
+//                        .set("day_num", i + 1)
+//                );
+//            }
+//        }
     }
 
 }

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

@@ -143,12 +143,3 @@ wechat:
   api:
     base-url: https://api.weixin.qq.com
     upload-shipping-info: /wxa/sec/order/upload_shipping_info
-  company:
-    appid: wxd7c1e221622a0ccf
-    secret: 70d3ed4f8eb68cca0cf525b8ce07405d
-    redirectUri: http://rfa96c48.natappfree.cc/callback
-  admin:
-    appid: wxd7c1e221622a0ccf
-    secret: 70d3ed4f8eb68cca0cf525b8ce07405d
-    redirectUri: http://rfa96c48.natappfree.cc/callback
-  isNeedScan: false

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

@@ -42,8 +42,8 @@ wx:
       port: 6379
       timeout: 2000
     configs:
-      - appId: wx17f36a56c701bdea # 第一个公众号的appid   //公众号名称
-        secret: 185030bbe7f8d7a0c16b94dd9d4ea542 # 公众号的appsecret
+      - appId: wxbf0cfcfbc92ccd72 # 第一个公众号的appid   //赤峰润元堂
+        secret: b08b0c6e763b5fa3c863b3dd114cd1c9 # 公众号的appsecret
         token: PPKOdAlCoMO # 接口配置里的Token值
         aesKey: Eswa6VjwtVcw03qZy6Wllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
 aifabu:  #爱链接

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

@@ -50,11 +50,11 @@ spring:
                     username: root
                     password: Ylrz_1q2w3e4r5t6y
                 # 初始连接数
-                initialSize: 5
+                initialSize: 50
                 # 最小连接池数量
-                minIdle: 10
+                minIdle: 50
                 # 最大连接池数量
-                maxActive: 20
+                maxActive: 500
                 # 配置获取连接等待超时的时间
                 maxWait: 60000
                 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒

+ 10 - 0
fs-service/src/main/resources/application-druid-jnmy-test.yml

@@ -232,5 +232,15 @@ isNewWxMerchant: true
 ipad:
     url: http://localhost:8999/dev-api
     companyId: 13
+#wechat:
+#    company:
+#        appid: wxd7c1e221622a0ccf
+#        secret: 70d3ed4f8eb68cca0cf525b8ce07405d
+#        redirectUri: http://ta6d97ec.natappfree.cc/callback
+#    admin:
+#        appid: wxd7c1e221622a0ccf
+#        secret: 70d3ed4f8eb68cca0cf525b8ce07405d
+#        redirectUri: http://ta6d97ec.natappfree.cc/callback
+#    isNeedScan: true
 
 

+ 11 - 0
fs-service/src/main/resources/application-druid-jnmy.yml

@@ -161,4 +161,15 @@ im:
 #是否为新商户,新商户不走mpOpenId
 isNewWxMerchant: true
 
+wechat:
+    company:
+        appid: wxd7c1e221622a0ccf
+        secret: 70d3ed4f8eb68cca0cf525b8ce07405d
+        redirectUri: https://company.jnmyunl.com/prod-api/callback
+    admin:
+        appid: wxd7c1e221622a0ccf
+        secret: 70d3ed4f8eb68cca0cf525b8ce07405d
+        redirectUri: https://admin.jnmyunl.com/prod-api/callback
+    isNeedScan: false
+
 

+ 125 - 0
fs-service/src/main/resources/mapper/MerchantAppConfigMapper.xml

@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.his.mapper.MerchantAppConfigMapper">
+
+    <resultMap type="MerchantAppConfig" id="MerchantAppConfigResult">
+        <result property="id"    column="id"    />
+        <result property="merchantType"    column="merchant_type"    />
+        <result property="appId"    column="app_id"    />
+        <result property="callbackUrl"    column="callback_url"    />
+        <result property="dataJson"    column="data_json"    />
+        <result property="createdTime"    column="created_time"    />
+        <result property="updatedTime"    column="updated_time"    />
+        <result property="isDeleted"    column="is_deleted"    />
+        <result property="createdBy"    column="created_by"    />
+        <result property="updatedBy"    column="updated_by"    />
+        <result property="merchantId"    column="merchant_id"    />
+    </resultMap>
+
+    <!-- 检查表是否存在 -->
+    <select id="checkTableExists" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM information_schema.tables
+        WHERE table_schema = DATABASE() AND table_name = 'merchant_app_config'
+    </select>
+
+    <!-- 创建商户配置表 -->
+    <update id="createMerchantAppConfigTable">
+        CREATE TABLE `merchant_app_config` (
+        `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+        `merchant_type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商户类型:如WECHAT_PAY、ALIPAY等',
+        `app_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用ID',
+        `callback_url` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '回调地址,用于接收支付结果等通知',
+        `data_json` json DEFAULT NULL COMMENT '扩展配置数据,JSON格式存储其他配置信息',
+        `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+        `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
+        `is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除状态:0-正常,1-已删除',
+        `created_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建人ID或用户名',
+        `updated_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '修改人ID或用户名',
+        `merchant_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '商户号',
+        PRIMARY KEY (`id`),
+        KEY `idx_merchant_type` (`merchant_type`),
+        KEY `idx_app_id` (`app_id`),
+        KEY `idx_created_time` (`created_time`),
+        KEY `merchant_app_config_merchant_type_IDX` (`merchant_type`,`is_deleted`,`merchant_id`) USING BTREE
+        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商户应用配置表'
+    </update>
+
+
+    <sql id="selectMerchantAppConfigVo">
+        select id, merchant_type, app_id, merchant_id,callback_url, data_json, created_time, updated_time, is_deleted, created_by, updated_by from merchant_app_config
+    </sql>
+
+    <select id="selectMerchantAppConfigList" parameterType="MerchantAppConfig" resultMap="MerchantAppConfigResult">
+        <include refid="selectMerchantAppConfigVo"/>
+        <where>
+            <if test="merchantId != null and merchantId != ''"> and merchant_id = #{merchantId}</if>
+            <if test="merchantType != null  and merchantType != ''"> and merchant_type = #{merchantType}</if>
+            <if test="appId != null  and appId != ''"> and app_id = #{appId}</if>
+            <if test="params.beginCreatedTime != null and params.beginCreatedTime != '' and params.endCreatedTime != null and params.endCreatedTime != ''"> and created_time between #{params.beginCreatedTime} and #{params.endCreatedTime}</if>
+            <if test="isDeleted != null "> and is_deleted = #{isDeleted}</if>
+        </where>
+    </select>
+
+    <select id="selectMerchantAppConfigById" parameterType="Long" resultMap="MerchantAppConfigResult">
+        <include refid="selectMerchantAppConfigVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertMerchantAppConfig" parameterType="MerchantAppConfig" useGeneratedKeys="true" keyProperty="id">
+        insert into merchant_app_config
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="merchantType != null and merchantType != ''">merchant_type,</if>
+            <if test="appId != null and appId != ''">app_id,</if>
+            <if test="callbackUrl != null">callback_url,</if>
+            <if test="dataJson != null">data_json,</if>
+            <if test="createdTime != null">created_time,</if>
+            <if test="updatedTime != null">updated_time,</if>
+            <if test="isDeleted != null">is_deleted,</if>
+            <if test="createdBy != null and createdBy != ''">created_by,</if>
+            <if test="updatedBy != null and updatedBy != ''">updated_by,</if>
+            <if test="merchantId != null and merchantId != ''">merchant_id,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="merchantType != null and merchantType != ''">#{merchantType},</if>
+            <if test="appId != null and appId != ''">#{appId},</if>
+            <if test="callbackUrl != null">#{callbackUrl},</if>
+            <if test="dataJson != null">#{dataJson},</if>
+            <if test="createdTime != null">#{createdTime},</if>
+            <if test="updatedTime != null">#{updatedTime},</if>
+            <if test="isDeleted != null">#{isDeleted},</if>
+            <if test="createdBy != null and createdBy != ''">#{createdBy},</if>
+            <if test="updatedBy != null and updatedBy != ''">#{updatedBy},</if>
+            <if test="merchantId != null and merchantId != ''">#{merchantId},</if>
+         </trim>
+    </insert>
+
+    <update id="updateMerchantAppConfig" parameterType="MerchantAppConfig">
+        update merchant_app_config
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="merchantType != null and merchantType != ''">merchant_type = #{merchantType},</if>
+            <if test="appId != null and appId != ''">app_id = #{appId},</if>
+            <if test="callbackUrl != null">callback_url = #{callbackUrl},</if>
+            <if test="dataJson != null">data_json = #{dataJson},</if>
+            <if test="createdTime != null">created_time = #{createdTime},</if>
+            <if test="updatedTime != null">updated_time = #{updatedTime},</if>
+            <if test="isDeleted != null">is_deleted = #{isDeleted},</if>
+            <if test="createdBy != null and createdBy != ''">created_by = #{createdBy},</if>
+            <if test="updatedBy != null and updatedBy != ''">updated_by = #{updatedBy},</if>
+            <if test="merchantId != null and merchantId != ''">merchant_id = #{merchantId},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteMerchantAppConfigById" parameterType="Long">
+        delete from merchant_app_config where id = #{id}
+    </delete>
+
+    <delete id="deleteMerchantAppConfigByIds" parameterType="String">
+        delete from merchant_app_config where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

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

@@ -29,4 +29,12 @@
         </if>
         order by fcpsc.id desc
     </select>
+
+    <select id="selectCoursePlaySourceConfigByAppId" resultType="com.fs.course.domain.FsCoursePlaySourceConfig">
+        select
+            fcpsc.*
+        from fs_course_play_source_config fcpsc
+        where fcpsc.is_del = 0 and fcpsc.appid= #{appid}
+       limit 1
+    </select>
 </mapper>

+ 438 - 345
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -60,7 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         l.log_type,SEC_TO_TIME(l.duration) as duration,c.company_name,l.camp_period_time,l.finish_time,
         cu.nick_name as company_user_name ,l.send_type,l.create_time,l.update_time,l.last_heartbeat_time,
         qu.qw_user_name,qec.name as external_user_name,c.company_id,u.avatar as fsAvatar,u.nick_name as fsNickName,qec.create_time as qec_create_time,
-        u.is_vip isVip,l.reward_type
+        u.is_vip isVip,l.reward_type,cu.dept_id
          from fs_course_watch_log l
          left join fs_user_course_video v on v.video_id = l.video_id
          left join fs_user_course uc on uc.course_id = l.course_id
@@ -172,358 +172,358 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                     #{item}
                 </foreach>
             </if>
-           <if test="maps.userIds != null and !maps.userIds.isEmpty()">
-            AND l.company_user_id IN
-               <foreach collection='maps.userIds' item='item' open='(' separator=',' close=')'>
-                  #{item}
-               </foreach>
+            <if test="maps.userIds != null and !maps.userIds.isEmpty()">
+                AND cu.user_id IN
+                    <foreach collection='maps.userIds' item='item' open='(' separator=',' close=')'>
+                        #{item}
+                    </foreach>
             </if>
-        </where>
-         order by l.finish_time desc,l.update_time desc,l.create_time desc
-    </select>
+                </where>
+                 order by l.finish_time desc,l.update_time desc,l.create_time desc
+            </select>
 
-    <select id="selectFsCourseWatchLogListByParam" resultType="com.fs.course.vo.FsCourseWatchLogListVO">
-        select l.log_id,l.user_id,uc.course_name,v.title as video_name,u.nick_name as fsNickName, u.avatar as fsAvatar,
-        l.log_type,SEC_TO_TIME(l.duration) as duration,c.company_name,l.camp_period_time,l.finish_time,
-        cu.nick_name as company_user_name ,l.send_type,l.create_time, qu.qw_user_name,qec.name as external_user_name
-        from fs_course_watch_log l
-        left join fs_user_course_video v on v.video_id = l.video_id
-        left join fs_user_course uc on uc.course_id = l.course_id
-        left join fs_user u on u.user_id = l.user_id
-        left join company_user cu on cu.user_id = l.company_user_id
-        left join company c on c.company_id = l.company_id
-        LEFT JOIN qw_user qu on qu.id= l.qw_user_id
-        LEFT JOIN qw_external_contact qec on l.qw_external_contact_id = qec.id
-        <where>
-            <if test ='userId !=null'>
-                and l.user_id = #{userId}
-            </if>
-            <if test ='qwUserId !=null'>
-                and l.qw_user_id = #{qwUserId}
-            </if>
-            <if test ='courseId !=null'>
-                and l.course_id = #{courseId}
-            </if>
-            <if test ='videoId !=null'>
-                and l.video_id = #{videoId}
-            </if>
-            <if test ='logType !=null'>
-                and l.log_type = #{logType}
-            </if>
-            <if test ='companyId !=null'>
-                and l.company_id = #{companyId}
-            </if>
-            <if test ='companyUserId !=null'>
-                and l.company_user_id = #{companyUserId}
-            </if>
-            <if test ='companyUserName !=null and maps.companyUserName!=""'>
-                and cu.nick_name  like concat('%', #{companyUserName}, '%')
-            </if>
-            <if test ='nickName !=null and maps.nickName!=""'>
-                and u.nick_name  like concat('%', #{nickName}, '%')
-            </if>
-            <if test= 'sTime != null '>
-                and DATE(l.create_time) &gt;= DATE(#{sTime})
-            </if>
-            <if test='eTime != null '>
-                and DATE(l.create_time) &lt;= DATE(#{eTime})
-            </if>
-            <if test= 'maps.scheduleStartTime != null '>
-                and DATE(l.camp_period_time) &gt;= DATE(#{maps.scheduleStartTime})
-            </if>
-            <if test='maps.scheduleEndTime != null '>
-                and DATE(l.camp_period_time) &lt;= DATE(#{maps.scheduleEndTime})
-            </if>
-            <if test="sopIds != null and sopIds.size() > 0">
-                and l.sop_id in
-                <foreach item="sopId" index="index" collection="sopIds" open="(" separator="," close=")">
-                    #{sopId}
+            <select id="selectFsCourseWatchLogListByParam" resultType="com.fs.course.vo.FsCourseWatchLogListVO">
+                select l.log_id,l.user_id,uc.course_name,v.title as video_name,u.nick_name as fsNickName, u.avatar as fsAvatar,
+                l.log_type,SEC_TO_TIME(l.duration) as duration,c.company_name,l.camp_period_time,l.finish_time,
+                cu.nick_name as company_user_name ,l.send_type,l.create_time, qu.qw_user_name,qec.name as external_user_name
+                from fs_course_watch_log l
+                left join fs_user_course_video v on v.video_id = l.video_id
+                left join fs_user_course uc on uc.course_id = l.course_id
+                left join fs_user u on u.user_id = l.user_id
+                left join company_user cu on cu.user_id = l.company_user_id
+                left join company c on c.company_id = l.company_id
+                LEFT JOIN qw_user qu on qu.id= l.qw_user_id
+                LEFT JOIN qw_external_contact qec on l.qw_external_contact_id = qec.id
+                <where>
+                    <if test ='userId !=null'>
+                        and l.user_id = #{userId}
+                    </if>
+                    <if test ='qwUserId !=null'>
+                        and l.qw_user_id = #{qwUserId}
+                    </if>
+                    <if test ='courseId !=null'>
+                        and l.course_id = #{courseId}
+                    </if>
+                    <if test ='videoId !=null'>
+                        and l.video_id = #{videoId}
+                    </if>
+                    <if test ='logType !=null'>
+                        and l.log_type = #{logType}
+                    </if>
+                    <if test ='companyId !=null'>
+                        and l.company_id = #{companyId}
+                    </if>
+                    <if test ='companyUserId !=null'>
+                        and l.company_user_id = #{companyUserId}
+                    </if>
+                    <if test ='companyUserName !=null and maps.companyUserName!=""'>
+                        and cu.nick_name  like concat('%', #{companyUserName}, '%')
+                    </if>
+                    <if test ='nickName !=null and maps.nickName!=""'>
+                        and u.nick_name  like concat('%', #{nickName}, '%')
+                    </if>
+                    <if test= 'sTime != null '>
+                        and DATE(l.create_time) &gt;= DATE(#{sTime})
+                    </if>
+                    <if test='eTime != null '>
+                        and DATE(l.create_time) &lt;= DATE(#{eTime})
+                    </if>
+                    <if test= 'maps.scheduleStartTime != null '>
+                        and DATE(l.camp_period_time) &gt;= DATE(#{maps.scheduleStartTime})
+                    </if>
+                    <if test='maps.scheduleEndTime != null '>
+                        and DATE(l.camp_period_time) &lt;= DATE(#{maps.scheduleEndTime})
+                    </if>
+                    <if test="sopIds != null and sopIds.size() > 0">
+                        and l.sop_id in
+                        <foreach item="sopId" index="index" collection="sopIds" open="(" separator="," close=")">
+                            #{sopId}
+                        </foreach>
+                    </if>
+                </where>
+                order by l.log_id desc
+            </select>
+            <insert id="insertFsCourseWatchLog" parameterType="FsCourseWatchLog" useGeneratedKeys="true" keyProperty="logId">
+                insert into fs_course_watch_log
+                <trim prefix="(" suffix=")" suffixOverrides=",">
+                    <if test="userId != null">user_id,</if>
+                    <if test="videoId != null">video_id,</if>
+                    <if test="logType != null">log_type,</if>
+                    <if test="createTime != null">create_time,</if>
+                    <if test="updateTime != null">update_time,</if>
+                    <if test="qwExternalContactId != null">qw_external_contact_id,</if>
+                    <if test="duration != null">duration,</if>
+                    <if test="qwUserId != null">qw_user_id,</if>
+                    <if test="companyUserId != null">company_user_id,</if>
+                    <if test="companyId != null">company_id,</if>
+                    <if test="courseId != null">course_id,</if>
+                    <if test="sendType != null">send_type,</if>
+                    <if test="rewardType != null">reward_type,</if>
+                    <if test="sopId != null">sop_id,</if>
+                    <if test="finishTime != null">finish_time,</if>
+                    <if test="sendFinishMsg != null">send_finish_msg,</if>
+                    <if test="campPeriodTime != null">camp_period_time,</if>
+                    <if test="periodId != null">period_id,</if>
+                    <if test="project != null">project,</if>
+                </trim>
+                <trim prefix="values (" suffix=")" suffixOverrides=",">
+                    <if test="userId != null">#{userId},</if>
+                    <if test="videoId != null">#{videoId},</if>
+                    <if test="logType != null">#{logType},</if>
+                    <if test="createTime != null">#{createTime},</if>
+                    <if test="updateTime != null">#{updateTime},</if>
+                    <if test="qwExternalContactId != null">#{qwExternalContactId},</if>
+                    <if test="duration != null">#{duration},</if>
+                    <if test="qwUserId != null">#{qwUserId},</if>
+                    <if test="companyUserId != null">#{companyUserId},</if>
+                    <if test="companyId != null">#{companyId},</if>
+                    <if test="courseId != null">#{courseId},</if>
+                    <if test="sendType != null">#{sendType},</if>
+                    <if test="rewardType != null">#{rewardType},</if>
+                    <if test="sopId != null">#{sopId},</if>
+                    <if test="finishTime != null">#{finishTime},</if>
+                    <if test="sendFinishMsg != null">#{sendFinishMsg},</if>
+                    <if test="campPeriodTime != null">#{campPeriodTime},</if>
+                    <if test="periodId != null">#{periodId},</if>
+                    <if test="project != null">#{project},</if>
+                </trim>
+            </insert>
+
+            <insert id="insertOrUpdateFsCourseWatchLog" parameterType="FsCourseWatchLog">
+                insert into fs_course_watch_log
+                <trim prefix="(" suffix=")" suffixOverrides=",">
+                    <if test="userId != null">user_id,</if>
+                    <if test="videoId != null">video_id,</if>
+                    <if test="logType != null">log_type,</if>
+                    <if test="createTime != null">create_time,</if>
+                    <if test="updateTime != null">update_time,</if>
+                    <if test="qwExternalContactId != null">qw_external_contact_id,</if>
+                    <if test="duration != null">duration,</if>
+                    <if test="qwUserId != null">qw_user_id,</if>
+                    <if test="companyUserId != null">company_user_id,</if>
+                    <if test="companyId != null">company_id,</if>
+                    <if test="courseId != null">course_id,</if>
+                    <if test="sendType != null">send_type,</if>
+                    <if test="rewardType != null">reward_type,</if>
+                    <if test="sopId != null">sop_id,</if>
+                    <if test="finishTime != null">finish_time,</if>
+                    <if test="sendFinishMsg != null">send_finish_msg,</if>
+                    <if test="campPeriodTime != null">camp_period_time,</if>
+                    <if test="project != null">project,</if>
+                </trim>
+                <trim prefix="values (" suffix=")" suffixOverrides=",">
+                    <if test="userId != null">#{userId},</if>
+                    <if test="videoId != null">#{videoId},</if>
+                    <if test="logType != null">#{logType},</if>
+                    <if test="createTime != null">#{createTime},</if>
+                    <if test="updateTime != null">#{updateTime},</if>
+                    <if test="qwExternalContactId != null">#{qwExternalContactId},</if>
+                    <if test="duration != null">#{duration},</if>
+                    <if test="qwUserId != null">#{qwUserId},</if>
+                    <if test="companyUserId != null">#{companyUserId},</if>
+                    <if test="companyId != null">#{companyId},</if>
+                    <if test="courseId != null">#{courseId},</if>
+                    <if test="sendType != null">#{sendType},</if>
+                    <if test="rewardType != null">#{rewardType},</if>
+                    <if test="sopId != null">#{sopId},</if>
+                    <if test="finishTime != null">#{finishTime},</if>
+                    <if test="sendFinishMsg != null">#{sendFinishMsg},</if>
+                    <if test="campPeriodTime != null">#{campPeriodTime},</if>
+                    <if test="project != null">#{project},</if>
+                </trim>
+                on duplicate key update
+                <trim suffixOverrides=",">
+                    <if test="updateTime != null">update_time = #{updateTime},</if>
+                </trim>
+
+            </insert>
+
+
+            <insert id="insertFsCourseWatchLogBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="logId">
+                INSERT INTO fs_course_watch_log (
+                user_id,
+                video_id,
+                log_type,
+                create_time,
+                update_time,
+                qw_external_contact_id,
+                duration,
+                qw_user_id,
+                company_user_id,
+                company_id,
+                course_id,
+                send_type,
+                reward_type,
+                sop_id,
+                camp_period_time,
+                project,
+                period_id,
+                im_msg_send_detail_id
+                )
+                VALUES
+                <foreach collection="watchLogs" item="log" separator=",">
+                    (
+                    #{log.userId},
+                    #{log.videoId},
+                    #{log.logType},
+                    #{log.createTime},
+                    #{log.updateTime},
+                    #{log.qwExternalContactId},
+                    #{log.duration},
+                    #{log.qwUserId},
+                    #{log.companyUserId},
+                    #{log.companyId},
+                    #{log.courseId},
+                    #{log.sendType},
+                    #{log.rewardType},
+                    #{log.sopId},
+                    #{log.campPeriodTime},
+                    #{log.project},
+                    #{log.periodId},
+                    #{log.imMsgSendDetailId}
+                    )
                 </foreach>
-            </if>
-        </where>
-        order by l.log_id desc
-    </select>
-    <insert id="insertFsCourseWatchLog" parameterType="FsCourseWatchLog" useGeneratedKeys="true" keyProperty="logId">
-        insert into fs_course_watch_log
-        <trim prefix="(" suffix=")" suffixOverrides=",">
-            <if test="userId != null">user_id,</if>
-            <if test="videoId != null">video_id,</if>
-            <if test="logType != null">log_type,</if>
-            <if test="createTime != null">create_time,</if>
-            <if test="updateTime != null">update_time,</if>
-            <if test="qwExternalContactId != null">qw_external_contact_id,</if>
-            <if test="duration != null">duration,</if>
-            <if test="qwUserId != null">qw_user_id,</if>
-            <if test="companyUserId != null">company_user_id,</if>
-            <if test="companyId != null">company_id,</if>
-            <if test="courseId != null">course_id,</if>
-            <if test="sendType != null">send_type,</if>
-            <if test="rewardType != null">reward_type,</if>
-            <if test="sopId != null">sop_id,</if>
-            <if test="finishTime != null">finish_time,</if>
-            <if test="sendFinishMsg != null">send_finish_msg,</if>
-            <if test="campPeriodTime != null">camp_period_time,</if>
-            <if test="periodId != null">period_id,</if>
-            <if test="project != null">project,</if>
-        </trim>
-        <trim prefix="values (" suffix=")" suffixOverrides=",">
-            <if test="userId != null">#{userId},</if>
-            <if test="videoId != null">#{videoId},</if>
-            <if test="logType != null">#{logType},</if>
-            <if test="createTime != null">#{createTime},</if>
-            <if test="updateTime != null">#{updateTime},</if>
-            <if test="qwExternalContactId != null">#{qwExternalContactId},</if>
-            <if test="duration != null">#{duration},</if>
-            <if test="qwUserId != null">#{qwUserId},</if>
-            <if test="companyUserId != null">#{companyUserId},</if>
-            <if test="companyId != null">#{companyId},</if>
-            <if test="courseId != null">#{courseId},</if>
-            <if test="sendType != null">#{sendType},</if>
-            <if test="rewardType != null">#{rewardType},</if>
-            <if test="sopId != null">#{sopId},</if>
-            <if test="finishTime != null">#{finishTime},</if>
-            <if test="sendFinishMsg != null">#{sendFinishMsg},</if>
-            <if test="campPeriodTime != null">#{campPeriodTime},</if>
-            <if test="periodId != null">#{periodId},</if>
-            <if test="project != null">#{project},</if>
-        </trim>
-    </insert>
-
-    <insert id="insertOrUpdateFsCourseWatchLog" parameterType="FsCourseWatchLog">
-        insert into fs_course_watch_log
-        <trim prefix="(" suffix=")" suffixOverrides=",">
-            <if test="userId != null">user_id,</if>
-            <if test="videoId != null">video_id,</if>
-            <if test="logType != null">log_type,</if>
-            <if test="createTime != null">create_time,</if>
-            <if test="updateTime != null">update_time,</if>
-            <if test="qwExternalContactId != null">qw_external_contact_id,</if>
-            <if test="duration != null">duration,</if>
-            <if test="qwUserId != null">qw_user_id,</if>
-            <if test="companyUserId != null">company_user_id,</if>
-            <if test="companyId != null">company_id,</if>
-            <if test="courseId != null">course_id,</if>
-            <if test="sendType != null">send_type,</if>
-            <if test="rewardType != null">reward_type,</if>
-            <if test="sopId != null">sop_id,</if>
-            <if test="finishTime != null">finish_time,</if>
-            <if test="sendFinishMsg != null">send_finish_msg,</if>
-            <if test="campPeriodTime != null">camp_period_time,</if>
-            <if test="project != null">project,</if>
-        </trim>
-        <trim prefix="values (" suffix=")" suffixOverrides=",">
-            <if test="userId != null">#{userId},</if>
-            <if test="videoId != null">#{videoId},</if>
-            <if test="logType != null">#{logType},</if>
-            <if test="createTime != null">#{createTime},</if>
-            <if test="updateTime != null">#{updateTime},</if>
-            <if test="qwExternalContactId != null">#{qwExternalContactId},</if>
-            <if test="duration != null">#{duration},</if>
-            <if test="qwUserId != null">#{qwUserId},</if>
-            <if test="companyUserId != null">#{companyUserId},</if>
-            <if test="companyId != null">#{companyId},</if>
-            <if test="courseId != null">#{courseId},</if>
-            <if test="sendType != null">#{sendType},</if>
-            <if test="rewardType != null">#{rewardType},</if>
-            <if test="sopId != null">#{sopId},</if>
-            <if test="finishTime != null">#{finishTime},</if>
-            <if test="sendFinishMsg != null">#{sendFinishMsg},</if>
-            <if test="campPeriodTime != null">#{campPeriodTime},</if>
-            <if test="project != null">#{project},</if>
-        </trim>
-        on duplicate key update
-        <trim suffixOverrides=",">
-            <if test="updateTime != null">update_time = #{updateTime},</if>
-        </trim>
-
-    </insert>
-
-
-    <insert id="insertFsCourseWatchLogBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="logId">
-        INSERT INTO fs_course_watch_log (
-        user_id,
-        video_id,
-        log_type,
-        create_time,
-        update_time,
-        qw_external_contact_id,
-        duration,
-        qw_user_id,
-        company_user_id,
-        company_id,
-        course_id,
-        send_type,
-        reward_type,
-        sop_id,
-        camp_period_time,
-        project,
-        period_id,
-        im_msg_send_detail_id
-        )
-        VALUES
-        <foreach collection="watchLogs" item="log" separator=",">
-            (
-            #{log.userId},
-            #{log.videoId},
-            #{log.logType},
-            #{log.createTime},
-            #{log.updateTime},
-            #{log.qwExternalContactId},
-            #{log.duration},
-            #{log.qwUserId},
-            #{log.companyUserId},
-            #{log.companyId},
-            #{log.courseId},
-            #{log.sendType},
-            #{log.rewardType},
-            #{log.sopId},
-            #{log.campPeriodTime},
-            #{log.project},
-            #{log.periodId},
-            #{log.imMsgSendDetailId}
-            )
-        </foreach>
-        ON DUPLICATE KEY UPDATE
-        update_time = NOW(),
-        im_msg_send_detail_id = VALUES(im_msg_send_detail_id)
-    </insert>
-
-
-
-    <update id="updateFsCourseWatchLog" parameterType="FsCourseWatchLog">
-        update fs_course_watch_log
-        <trim prefix="SET" suffixOverrides=",">
-            <if test="userId != null">user_id = #{userId},</if>
-            <if test="videoId != null">video_id = #{videoId},</if>
-            <if test="logType != null">log_type = #{logType},</if>
-            <if test="createTime != null">create_time = #{createTime},</if>
-            <if test="updateTime != null">update_time = #{updateTime},</if>
-            <if test="qwExternalContactId != null">qw_external_contact_id = #{qwExternalContactId},</if>
-            <if test="duration != null">duration = #{duration},</if>
-            <if test="qwUserId != null">qw_user_id = #{qwUserId},</if>
-            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
-            <if test="companyId != null">company_id = #{companyId},</if>
-            <if test="courseId != null">course_id = #{courseId},</if>
-            <if test="sendType != null">send_type = #{sendType},</if>
-            <if test="rewardType != null">reward_type = #{rewardType},</if>
-            <if test="sopId != null">sop_id = #{sopId},</if>
-            <if test="finishTime != null">finish_time = #{finishTime},</if>
-            <if test="sendFinishMsg != null">send_finish_msg = #{sendFinishMsg},</if>
-            <if test="lastHeartbeatTime != null">last_heartbeat_time = #{lastHeartbeatTime},</if>
-            <if test="periodId != null">period_id = #{periodId},</if>
-            <if test="project != null">project = #{project},</if>
-        </trim>
-        where log_id = #{logId}
-    </update>
+                ON DUPLICATE KEY UPDATE
+                update_time = NOW(),
+                im_msg_send_detail_id = VALUES(im_msg_send_detail_id)
+            </insert>
 
-    <delete id="deleteFsCourseWatchLogByLogId" parameterType="Long">
-        delete from fs_course_watch_log where log_id = #{logId}
-    </delete>
 
-    <delete id="deleteFsCourseWatchLogByLogIds" parameterType="String">
-        delete from fs_course_watch_log where log_id in
-        <foreach item="logId" collection="array" open="(" separator="," close=")">
-            #{logId}
-        </foreach>
-    </delete>
 
-    <select id="selectFsCourseWatchLogByFinishTime" resultType="com.fs.course.param.FsCourseWatchLogByFinishTimeParam">
-        <![CDATA[
-        SELECT
-            fcwl.log_id,
-            fcwl.create_time,
-            fcwl.qw_external_contact_id,
-            fcwl.qw_user_id,
-            fcwl.user_id,
-            fcwl.company_user_id,
-            fcwl.company_id,
-            fcwl.sop_id,
-            fcwl.finish_time,
-            fcwl.camp_period_time,
-            qec.corp_id,
-            qec.external_user_id,
-            qec.tag_ids,
-            qec.user_id AS qw_user,
-            qec.name AS external_contact_name
-        FROM
-            fs_course_watch_log fcwl
-                LEFT JOIN qw_external_contact qec ON fcwl.qw_external_contact_id = qec.id
-        WHERE
-            DATE(fcwl.finish_time)= '2025-02-09'
-          and fcwl.camp_period_time is not NULL
-        ]]>
-    </select>
-    <select id="getWatchLogByFsUser" resultType="com.fs.course.domain.FsCourseWatchLog">
-        SELECT
-            log_id,
-            user_id,
-            video_id,
-            log_type,
-            create_time,
-            update_time,
-            duration,
-            company_user_id,
-            company_id,
-            course_id,
-            send_type,
-            reward_type,
-            last_heartbeat_time,
-            sop_id,
-            finish_time,
-            send_finish_msg,
-            camp_period_time
-        FROM
-            fs_course_watch_log
-        WHERE
-            send_type = 1
-          AND video_id = #{videoId}
-          AND user_id = #{fsUserId}
-          AND company_user_id = #{companyUserId}  order by log_id desc limit 1
-    </select>
-    <select id="selectFsCourseWatchLogStatisticsListVONew"
-            resultType="com.fs.course.vo.FsCourseWatchLogStatisticsListVO">
-        SELECT
-        o.company_user_id,o.user_id,DATE(o.create_time) create_time,
-        SUM(CASE WHEN o.log_type = '1' THEN 1 ELSE 0 END) AS type1,
-        SUM(CASE WHEN o.log_type = '2' THEN 1 ELSE 0 END) AS type2,
-        SUM(CASE WHEN o.log_type = '3' THEN 1 ELSE 0 END) AS type3,
-        SUM(CASE WHEN o.log_type = '4' THEN 1 ELSE 0 END) AS type4,
-        o.project as project,
-        o.course_id as course_id,
-        o.video_id as video_id
-        FROM fs_course_watch_log o
-        <where>
-            send_type=1
-            <if test="companyId != null">
-                and o.company_id=#{companyId}
-            </if>
-            <if test= 'sTime != null '>
-                and DATE(o.create_time) &gt;= #{sTime}
-            </if>
-            <if test='eTime != null '>
-                and DATE(o.create_time) &lt;= #{eTime}
-            </if>
-            <if test ='courseId !=null'>
-                and o.course_id = #{courseId}
-            </if>
-            <if test ='videoId !=null'>
-                and o.video_id = #{videoId}
-            </if>
-            <if test="companyUserId != null">
-                and o.company_user_id = #{companyUserId}
-            </if>
-            <if test="project != null">
-                and o.project = #{project}
-            </if>
-            <if test="userId != null">
-                and o.user_id = #{userId}
-            </if>
-        </where>
+            <update id="updateFsCourseWatchLog" parameterType="FsCourseWatchLog">
+                update fs_course_watch_log
+                <trim prefix="SET" suffixOverrides=",">
+                    <if test="userId != null">user_id = #{userId},</if>
+                    <if test="videoId != null">video_id = #{videoId},</if>
+                    <if test="logType != null">log_type = #{logType},</if>
+                    <if test="createTime != null">create_time = #{createTime},</if>
+                    <if test="updateTime != null">update_time = #{updateTime},</if>
+                    <if test="qwExternalContactId != null">qw_external_contact_id = #{qwExternalContactId},</if>
+                    <if test="duration != null">duration = #{duration},</if>
+                    <if test="qwUserId != null">qw_user_id = #{qwUserId},</if>
+                    <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+                    <if test="companyId != null">company_id = #{companyId},</if>
+                    <if test="courseId != null">course_id = #{courseId},</if>
+                    <if test="sendType != null">send_type = #{sendType},</if>
+                    <if test="rewardType != null">reward_type = #{rewardType},</if>
+                    <if test="sopId != null">sop_id = #{sopId},</if>
+                    <if test="finishTime != null">finish_time = #{finishTime},</if>
+                    <if test="sendFinishMsg != null">send_finish_msg = #{sendFinishMsg},</if>
+                    <if test="lastHeartbeatTime != null">last_heartbeat_time = #{lastHeartbeatTime},</if>
+                    <if test="periodId != null">period_id = #{periodId},</if>
+                    <if test="project != null">project = #{project},</if>
+                </trim>
+                where log_id = #{logId}
+            </update>
 
-        GROUP BY o.video_id,o.user_id,DATE(o.create_time),o.project,o.course_id
-        ORDER BY o.video_id ,DATE(o.create_time)
+            <delete id="deleteFsCourseWatchLogByLogId" parameterType="Long">
+                delete from fs_course_watch_log where log_id = #{logId}
+            </delete>
+
+            <delete id="deleteFsCourseWatchLogByLogIds" parameterType="String">
+                delete from fs_course_watch_log where log_id in
+                <foreach item="logId" collection="array" open="(" separator="," close=")">
+                    #{logId}
+                </foreach>
+            </delete>
 
-<!--        limit ${(pageNum-1)*pageSize},${pageSize}-->
+            <select id="selectFsCourseWatchLogByFinishTime" resultType="com.fs.course.param.FsCourseWatchLogByFinishTimeParam">
+                <![CDATA[
+                SELECT
+                    fcwl.log_id,
+                    fcwl.create_time,
+                    fcwl.qw_external_contact_id,
+                    fcwl.qw_user_id,
+                    fcwl.user_id,
+                    fcwl.company_user_id,
+                    fcwl.company_id,
+                    fcwl.sop_id,
+                    fcwl.finish_time,
+                    fcwl.camp_period_time,
+                    qec.corp_id,
+                    qec.external_user_id,
+                    qec.tag_ids,
+                    qec.user_id AS qw_user,
+                    qec.name AS external_contact_name
+                FROM
+                    fs_course_watch_log fcwl
+                        LEFT JOIN qw_external_contact qec ON fcwl.qw_external_contact_id = qec.id
+                WHERE
+                    DATE(fcwl.finish_time)= '2025-02-09'
+                  and fcwl.camp_period_time is not NULL
+                ]]>
+            </select>
+            <select id="getWatchLogByFsUser" resultType="com.fs.course.domain.FsCourseWatchLog">
+                SELECT
+                    log_id,
+                    user_id,
+                    video_id,
+                    log_type,
+                    create_time,
+                    update_time,
+                    duration,
+                    company_user_id,
+                    company_id,
+                    course_id,
+                    send_type,
+                    reward_type,
+                    last_heartbeat_time,
+                    sop_id,
+                    finish_time,
+                    send_finish_msg,
+                    camp_period_time
+                FROM
+                    fs_course_watch_log
+                WHERE
+                    send_type = 1
+                  AND video_id = #{videoId}
+                  AND user_id = #{fsUserId}
+                  AND company_user_id = #{companyUserId}  order by log_id desc limit 1
+            </select>
+            <select id="selectFsCourseWatchLogStatisticsListVONew"
+                    resultType="com.fs.course.vo.FsCourseWatchLogStatisticsListVO">
+                SELECT
+                o.company_user_id,o.user_id,DATE(o.create_time) create_time,
+                SUM(CASE WHEN o.log_type = '1' THEN 1 ELSE 0 END) AS type1,
+                SUM(CASE WHEN o.log_type = '2' THEN 1 ELSE 0 END) AS type2,
+                SUM(CASE WHEN o.log_type = '3' THEN 1 ELSE 0 END) AS type3,
+                SUM(CASE WHEN o.log_type = '4' THEN 1 ELSE 0 END) AS type4,
+                o.project as project,
+                o.course_id as course_id,
+                o.video_id as video_id
+                FROM fs_course_watch_log o
+                <where>
+                    send_type=1
+                    <if test="companyId != null">
+                        and o.company_id=#{companyId}
+                    </if>
+                    <if test= 'sTime != null '>
+                        and DATE(o.create_time) &gt;= #{sTime}
+                    </if>
+                    <if test='eTime != null '>
+                        and DATE(o.create_time) &lt;= #{eTime}
+                    </if>
+                    <if test ='courseId !=null'>
+                        and o.course_id = #{courseId}
+                    </if>
+                    <if test ='videoId !=null'>
+                        and o.video_id = #{videoId}
+                    </if>
+                    <if test="companyUserId != null">
+                        and o.company_user_id = #{companyUserId}
+                    </if>
+                    <if test="project != null">
+                        and o.project = #{project}
+                    </if>
+                    <if test="userId != null">
+                        and o.user_id = #{userId}
+                    </if>
+                </where>
+
+                GROUP BY o.video_id,o.user_id,DATE(o.create_time),o.project,o.course_id
+                ORDER BY o.video_id ,DATE(o.create_time)
+
+        <!--        limit ${(pageNum-1)*pageSize},${pageSize}-->
     </select>
     <select id="selectFsCourseWatchLogStatisticsListVONewCount" resultType="java.lang.Long">
         SELECT COUNT(*)
@@ -1005,4 +1005,97 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             o.send_type
     </select>
 
+
+    <select id="selectFsCourseWatchLogStatisticsListVO_COUNT"
+            resultType="java.lang.Long">
+        SELECT COUNT(*) FROM (
+        SELECT
+        o.video_id,
+        <!-- 用choose保证sendType逻辑互斥,避免GROUP BY字段缺失 -->
+        <choose>
+            <when test="sendType != 1">
+                o.qw_user_id,
+            </when>
+            <when test="sendType == 1">
+                o.company_user_id,
+            </when>
+            <!-- sendType为null时兜底,避免空指针 -->
+            <otherwise>
+                o.qw_user_id,
+            </otherwise>
+        </choose>
+        DATE(o.create_time) create_time
+        FROM fs_course_watch_log o
+        <!-- 动态关联qw_user表 -->
+        <if test="sendType != 1">
+            LEFT JOIN qw_user qu ON qu.id = o.qw_user_id
+        </if>
+        LEFT JOIN fs_user_course_video v ON v.video_id = o.video_id
+        LEFT JOIN fs_user_course uc ON uc.course_id = v.course_id
+        <!-- 动态关联company_user表 -->
+        <if test="sendType == 1">
+            LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+        </if>
+        WHERE o.company_id = #{companyId}
+        <!-- 发送类型筛选 -->
+        <if test="sendType != null">
+            AND send_type = #{sendType}
+        </if>
+        <!-- 开始时间筛选:区分String/Date类型,避免类型比较异常 -->
+        <if test="sTime != null">
+            <choose>
+                <!-- sTime是String类型(yyyy-MM-dd) -->
+                <when test="sTime instanceof java.lang.String and sTime.trim() != ''">
+                    AND o.create_time &gt;= STR_TO_DATE(#{sTime}, '%Y-%m-%d')
+                </when>
+                <!-- sTime是Date类型 -->
+                <otherwise>
+                    AND o.create_time &gt;= #{sTime}
+                </otherwise>
+            </choose>
+        </if>
+        <!-- 结束时间筛选:区分String/Date类型 -->
+        <if test="eTime != null">
+            <choose>
+                <when test="eTime instanceof java.lang.String and eTime.trim() != ''">
+                    AND o.create_time &lt; DATE_ADD(STR_TO_DATE(#{eTime}, '%Y-%m-%d'), INTERVAL 1 DAY)
+                </when>
+                <otherwise>
+                    AND o.create_time &lt; DATE_ADD(#{eTime}, INTERVAL 1 DAY)
+                </otherwise>
+            </choose>
+        </if>
+        <!-- 昵称筛选:sendType!=1时关联qw_user -->
+        <if test="sendType != 1 and nickName != null and nickName.trim() != ''">
+            AND qu.qw_user_name LIKE CONCAT(#{nickName}, '%')
+        </if>
+        <!-- 昵称筛选:sendType==1时关联company_user -->
+        <if test="sendType == 1 and nickName != null and nickName.trim() != ''">
+            AND cu.nick_name LIKE CONCAT(#{nickName}, '%')
+        </if>
+        <!-- 课程ID筛选:加>0判断,避免null值拼接 -->
+        <if test="courseId != null and courseId > 0">
+            AND o.course_id = #{courseId}
+        </if>
+        <!-- 视频ID筛选:加>0判断 -->
+        <if test="videoId != null and videoId > 0">
+            AND o.video_id = #{videoId}
+        </if>
+        <!-- 分组条件:与子查询字段一致 -->
+        GROUP BY
+        o.video_id,
+        <choose>
+            <when test="sendType != 1">
+                o.qw_user_id,
+            </when>
+            <when test="sendType == 1">
+                o.company_user_id,
+            </when>
+            <otherwise>
+                o.qw_user_id,
+            </otherwise>
+        </choose>
+        DATE(o.create_time)
+        ) AS t
+    </select>
 </mapper>

+ 6 - 1
fs-service/src/main/resources/mapper/course/FsUserCoursePeriodDaysMapper.xml

@@ -194,7 +194,12 @@
                     when id=#{item.id} then #{item.lesson}
                 </if>
             </foreach>
-
+            `status` =
+            <foreach collection="list" item="item" open="case" close=" end,">
+                <if test="item.status != null">
+                    when id=#{item.id} then #{item.status}
+                </if>
+            </foreach>
         </trim>
         where id in
         <foreach collection="list" index="index" item="item" separator="," open="(" close=")">

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

@@ -397,6 +397,7 @@
             <if test="trainingCampId != null">and `period`.training_camp_id = #{trainingCampId}</if>
             <if test="userId != null">and watch.user_id = #{userId}</if>
             <if test="periodId != null">and `period`.period_id = #{periodId}</if>
+            <if test="periodName != null  and periodName != ''"> and `period`.period_name like concat('%', #{periodName}, '%')</if>
         </where>
         group by
         `period`.period_id

+ 2 - 2
fs-service/src/main/resources/mapper/his/FsStoreOrderMapper.xml

@@ -579,7 +579,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             so.store_id in (select store_id from fs_store where delivery_type=2 or delivery_type=1)
             )
             and  (so.extend_order_id is null or  so.extend_order_id like '')
-            and (fuic.doctor_type2_confirm = 1 or fuic.doctor_type2_confirm = null)
+            and (fuic.doctor_type2_confirm = 1 or fuic.doctor_type2_confirm IS null)
         </if>
         <if test="maps.status == 7">
             and so.`status`= 2
@@ -796,7 +796,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 so.store_id in (select store_id from fs_store where delivery_type=2 or delivery_type=1)
                 )
                 and  (so.extend_order_id is null or  so.extend_order_id like '')
-                and (fuic.doctor_type2_confirm = 1 or fuic.doctor_type2_confirm = null)
+                and (fuic.doctor_type2_confirm = 1 or fuic.doctor_type2_confirm IS null)
             </if>
             <if test="maps.status == 7">
                 and so.`status`= 2

+ 3 - 0
fs-service/src/main/resources/mapper/his/FsUserMapper.xml

@@ -721,6 +721,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             user_id = #{userId}
         </foreach>
     </update>
+    <update id="updatePasswordByPhone">
+        update fs_user set password = #{password} where phone = #{encryptPhone}
+    </update>
 
     <select id="selectUserListByMap" resultType="com.fs.his.vo.OptionsVO">
         select

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

@@ -1795,7 +1795,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 <!--        </if>-->
 
         <where>
-            <if test="maps.appId != null and map.appId != ''">
+            <if test="maps.appId != null and maps.appId != ''">
                 and csc.appid = #{maps.appId}
             </if>
             <if test="maps.orderCodes != null  and maps.orderCodes.size > 0">

+ 1 - 0
fs-service/src/main/resources/mapper/hisStore/FsUserAddressScrmMapper.xml

@@ -6,6 +6,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <resultMap type="FsUserAddressScrm" id="FsUserAddressResult">
         <result property="id"    column="address_id"    />
+        <result property="addressId"    column="address_id"    />
         <result property="userId"    column="user_id"    />
         <result property="realName"    column="real_name"    />
         <result property="phone"    column="phone"    />

+ 403 - 0
fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml

@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.hisStore.mapper.MergedOrderMapper">
+
+    <select id="selectMergedOrderList" parameterType="com.fs.live.param.MergedOrderQueryParam" resultType="com.fs.live.vo.MergedOrderVO">
+        SELECT * FROM (
+      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,
+      o.item_json,
+      o.delivery_id,
+      o.finish_time,
+      o.create_time,
+      o.pay_time,
+      o.delivery_send_time,
+      NULL AS total_num,
+      NULL AS discount_money,
+      1 AS order_type,
+
+        cu.phonenumber as salesPhone,
+        cu.create_time as salesCreateTime,
+        u.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.cost as cost,
+        o.pay_postage as payDelivery,
+        o.coupon_price as discountMoney,
+        fspc.prescribe_spec as productSpec,
+        fss.store_id as storeId,
+        fss.store_name as storeName,
+        fspcs.cate_name as cateName,
+        GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(o.item_json, '$.barCode')) SEPARATOR ',') AS barCode,
+
+      c.company_name,
+      cu.user_name AS sales_name,
+      cu.nick_name AS company_user_nick_name,
+      u.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
+      FROM
+      fs_store_order_scrm o
+      left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION 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 c ON c.company_id = o.company_id
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_code = o.order_code
+      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 AND o.is_sys_del = 0 AND o.company_user_id IS NOT NULL AND o.company_user_id != 0
+          <if test="maps.status != null and maps.status != ''">
+            AND o.status = #{maps.status}
+          </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.bankTransactionId != null and maps.bankTransactionId != ''">
+            AND o.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+          </if>
+          <if test="maps.userPhone != null and maps.userPhone != ''">
+            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+          </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.productName LIKE CONCAT('%', #{maps.productName}, '%')
+          </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.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.createTimeRange != null and maps.createTimeRange != ''">
+            AND DATE(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 DATE(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>
+          <if test="maps.appId != null and maps.appId != ''">
+            AND csc.appid = #{maps.appId}
+          </if>
+          group by o.id
+          UNION ALL
+          -- 商城订单(没有company_user_id的商城订单)
+      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,
+      o.item_json,
+      o.delivery_id,
+      o.finish_time,
+      o.create_time,
+      o.pay_time,
+      o.delivery_send_time,
+      NULL AS total_num,
+      NULL AS discount_money,
+      2 AS order_type,
+
+    cu.phonenumber as salesPhone,
+    cu.create_time as salesCreateTime,
+    u.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,
+        fspc.cost 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(o.item_json, '$.barCode')) SEPARATOR ',') AS barCode,
+
+      c.company_name,
+      cu.user_name AS sales_name,
+      cu.nick_name AS company_user_nick_name,
+      u.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
+      FROM
+      fs_store_order_scrm o
+        left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.item_id  ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id
+      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 c ON c.company_id = o.company_id
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_code = o.order_code
+      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 AND o.is_sys_del = 0 AND (o.company_user_id IS NULL OR o.company_user_id = 0)
+          <if test="maps.status != null and maps.status != ''">
+            AND o.status = #{maps.status}
+          </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.bankTransactionId != null and maps.bankTransactionId != ''">
+            AND o.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+          </if>
+          <if test="maps.userPhone != null and maps.userPhone != ''">
+            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+          </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}, '%')
+          </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.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.createTimeRange != null and maps.createTimeRange != ''">
+            AND DATE(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 DATE(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>
+          <if test="maps.appId != null and maps.appId != ''">
+            AND csc.appid = #{maps.appId}
+          </if>
+        group by o.id
+          UNION ALL
+          -- 直播订单
+      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,
+      o.item_json,
+      o.delivery_sn AS delivery_id,
+      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,
+        u.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,
+        fspc.cost as cost,
+        o.pay_delivery 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,
+      u.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
+      FROM
+      live_order 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 c ON c.company_id = o.company_id
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_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_code ORDER BY sp.create_time DESC ) AS rn FROM live_order_payment sp ) sp_latest ON sp_latest.business_code = o.order_code
+      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
+          <if test="maps.status != null and maps.status != ''">
+            AND o.status = #{maps.status}
+          </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.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}, '%')
+          </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.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.createTimeRange != null and maps.createTimeRange != ''">
+            AND DATE(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 DATE(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>
+        group by o.order_id
+        ) AS merged_orders
+        WHERE 1=1
+        <if test="maps.orderTypeFilter != null">
+          AND order_type = #{maps.orderTypeFilter}
+        </if>
+        ORDER BY create_time DESC
+    </select>
+
+</mapper>
+

+ 25 - 9
fs-service/src/main/resources/mapper/live/LiveDataMapper.xml

@@ -283,7 +283,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <!-- 查询直播间统计数据 -->
     <select id="selectLiveDataStatistics" resultType="com.fs.live.vo.LiveDataStatisticsVo">
         SELECT
-            COUNT( lwu.user_id) AS totalViewers,
+            COUNT( distinct lwu.user_id) AS totalViewers,
             COUNT( CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.user_id END) AS liveViewers,
             COUNT( CASE WHEN lwu.live_flag = 0 and lwu.replay_flag = 1 THEN lwu.user_id END) AS playbackViewers,
             COALESCE(AVG(CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.online_seconds END), 0) AS liveAvgDuration,
@@ -360,7 +360,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             l.status AS status,
             l.start_time AS startTime,
             l.finish_time AS finishTime,
-            COUNT(1) AS totalViewers,
+            COUNT(distinct lwu.user_id) AS totalViewers,
             COUNT(CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.user_id END) AS liveViewers,
             COUNT(CASE WHEN lwu.live_flag = 0 and lwu.replay_flag = 1 THEN lwu.user_id END) AS playbackViewers,
             COALESCE(AVG(CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.online_seconds END), 0) AS liveAvgDuration,
@@ -412,15 +412,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectLiveDataDetailBySql" resultType="com.fs.live.vo.LiveDataDetailVo">
         SELECT
             COALESCE(video_duration.total_duration, 0) AS videoDuration,
-            (COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END) +  COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END)) AS totalViewers,
+            COUNT(DISTINCT lwu.user_id) AS totalViewers,
             COUNT(DISTINCT CASE
-                WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                WHEN COALESCE(user_duration.total_duration, 0) >= 1800
                 THEN lwu.user_id
             END) AS totalCompletedCourses,
             CASE
                 WHEN COUNT(DISTINCT lwu.user_id) > 0 THEN
                     ROUND(COUNT(DISTINCT CASE
-                        WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                        WHEN COALESCE(user_duration.total_duration, 0) >= 1200
                         THEN lwu.user_id
                     END) * 100.0 / COUNT(DISTINCT lwu.user_id), 2)
                 ELSE 0
@@ -473,8 +473,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 ELSE 0
             END AS totalViewerConversionRate,
             CASE
-                WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) > 0 THEN
-                    ROUND(order_stats.paidUsers * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END), 2)
+                WHEN COUNT(DISTINCT CASE
+                    WHEN COALESCE(user_duration.total_duration, 0) >= 1800
+                    THEN lwu.user_id
+                END) > 0 THEN
+                    ROUND(order_stats.paidUsers * 100.0 / COUNT(DISTINCT CASE
+                        WHEN COALESCE(user_duration.total_duration, 0) >= 1800
+                        THEN lwu.user_id
+                    END), 2)
                 ELSE 0
             END AS completion30MinConversionRate,
             CASE
@@ -484,11 +490,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             END AS peakRValue,
             CASE
                 WHEN COUNT(DISTINCT CASE
-                    WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                    WHEN COALESCE(user_duration.total_duration, 0) >= 1800
                     THEN lwu.user_id
                 END) > 0 THEN
                     ROUND(order_stats.gmv / COUNT(DISTINCT CASE
-                        WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                        WHEN COALESCE(user_duration.total_duration, 0) >= 1800
                         THEN lwu.user_id
                     END), 2)
                 ELSE 0
@@ -502,6 +508,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             WHERE video_type IN (1, 2)
             GROUP BY live_id
         ) video_duration ON l.live_id = video_duration.live_id
+        LEFT JOIN (
+            SELECT
+                live_id,
+                user_id,
+                COALESCE(SUM(CASE WHEN live_flag = 1 AND replay_flag = 0 THEN COALESCE(online_seconds, 0) ELSE 0 END), 0) +
+                COALESCE(SUM(CASE WHEN live_flag = 0 AND replay_flag = 1 THEN COALESCE(online_seconds, 0) ELSE 0 END), 0) AS total_duration
+            FROM live_watch_user
+            WHERE live_id = #{liveId}
+            GROUP BY live_id, user_id
+        ) user_duration ON l.live_id = user_duration.live_id AND lwu.user_id = user_duration.user_id
         LEFT JOIN (
             SELECT
                 live_id,

+ 2 - 2
fs-service/src/main/resources/mapper/qw/CustomerTransferApprovalMapper.xml

@@ -40,8 +40,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="approverUserId != null "> and approver_user_id = #{approverUserId}</if>
             <if test="approvalRemark != null  and approvalRemark != ''"> and approval_remark = #{approvalRemark}</if>
             <if test="processedAt != null "> and processed_at = #{processedAt}</if>
-            <if test="createdAt != null "> and created_at = #{createdAt}</if>
-            <if test="updatedAt != null "> and updated_at = #{updatedAt}</if>
+            <if test="createdAt != null "> and date_format(created_at,'%y%m%d') = date_format(#{createdAt},'%y%m%d')</if>
+            <if test="updatedAt != null "> and date_format(updated_at,'%y%m%d') = date_format(#{updatedAt},'%y%m%d')</if>
         </where>
         order by id desc
     </select>

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

@@ -89,7 +89,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="selectQwExternalContactByIds" resultType="com.fs.qw.domain.QwExternalContact">
         select * from qw_external_contact
-        where id in
+        where  id in
+        <foreach collection="ids" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
+
+    <select id="selectQwExternalContactByIdsStatus" resultType="com.fs.qw.domain.QwExternalContact">
+        select * from qw_external_contact
+        where status !=3 and id in
         <foreach collection="ids" item="id" open="(" separator="," close=")">
             #{id}
         </foreach>

+ 92 - 0
fs-user-app/src/main/java/com/fs/app/controller/AppLoginController.java

@@ -10,6 +10,7 @@ import com.fs.common.VerifyCodeUtil;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.ServiceException;
 import com.fs.common.service.ISmsService;
 import com.fs.common.utils.sign.Md5Utils;
 import com.fs.core.config.WxOpenProperties;
@@ -300,6 +301,61 @@ public class AppLoginController extends AppBaseController{
 
     }
 
+    @PostMapping("/loginByPhone")
+    public R loginByPhone(@RequestBody Map<String,String> map){
+        String phone = map.get("phone");
+        String code = map.get("code");
+        String encryptPhone = encryptPhone(phone);
+        List<FsUser> user = userService.selectFsUserListByPhone(encryptPhone);
+        if (CollectionUtil.isEmpty(user)){
+            user = userService.selectFsUserListByPhone(encryptPhoneOldKey(phone));
+        }
+        if (CollectionUtil.isEmpty(user)){
+            return R.error("此电话号码未绑定用户");
+        }
+        if (user.size()>1){
+            //如果出现了一个手机号多个用户的情况,找出登陆过app的那个用户
+            user.removeIf(fsUser -> StringUtils.isEmpty(fsUser.getHistoryApp()));
+        }
+        String redisCode = redisCache.getCacheObject("sms:code:" + phone);
+        if (StringUtils.isEmpty(redisCode)){
+            return R.error("验证码已过期,请重新发送");
+        }
+        if (!redisCode.equals(code)) {
+            return R.error("验证码错误");
+        }
+        updateExistingUserJpushId(user.get(0), map.get("jpushId"));
+        return generateTokenAndReturn(user.get(0));
+    }
+
+    @PostMapping("/resetPassword")
+    public R resetPassword(@RequestBody Map<String, String> body){
+        String phone = body.get("phone");
+        String code = body.get("code");
+        String newPassword = body.get("newPassword");
+        String confirmPassword = body.get("confirmPassword");
+        if (!newPassword.equals(confirmPassword)){
+            throw new ServiceException("两次输入密码不一致,请检查");
+        }
+        String encryptPhone = encryptPhone(phone);
+        List<FsUser> user = userService.selectFsUserListByPhone(encryptPhone);
+        if (CollectionUtil.isEmpty(user)){
+            user = userService.selectFsUserListByPhone(encryptPhoneOldKey(phone));
+        }
+        if (CollectionUtil.isEmpty(user)){
+            return R.error("此电话号码未绑定用户");
+        }
+        String redisCode = redisCache.getCacheObject("sms:code:" + phone);
+        if (StringUtils.isEmpty(redisCode)){
+            return R.error("验证码已过期,请重新发送");
+        }
+        if (!redisCode.equals(code)) {
+            return R.error("验证码错误");
+        }
+        String password = Md5Utils.hash(newPassword);
+        return userService.updatePasswordByPhone(password,encryptPhone);
+    }
+
     @ApiOperation("绑定手机号")
     @PostMapping("/setPhone")
     public R setPhone(@Validated @RequestBody FsUserEditPhoneParam param) {
@@ -456,6 +512,42 @@ public class AppLoginController extends AppBaseController{
 
     }
 
+    @PostMapping("/sendCode")
+    public R sendCode(@RequestBody Map<String, String> body){
+        String phone = body.get("phone");
+        String encryptPhone = encryptPhone(phone);
+        List<FsUser> user = userService.selectFsUserListByPhone(encryptPhone);
+        if(CollectionUtil.isEmpty(user)){
+            user = userService.selectFsUserListByPhone(encryptPhoneOldKey(phone));
+        }
+        if (CollectionUtil.isEmpty(user)){
+            return R.error("此电话号码未绑定用户");
+        }
+
+        // 验证码 key(3分钟有效)
+        String smsCodeKey = "sms:code:" + phone;
+        // 冷却 key(60秒内不能重复发送)
+        String smsCooldownKey = "sms:cooldown:" + phone;
+
+        // 判断是否在 60 秒冷却期
+        if (redisCache.getCacheObject(smsCooldownKey) != null) {
+            return R.error("验证码已发送,请稍后再试");
+        }
+
+        // 生成新验证码
+        String smsCode = VerifyCodeUtil.generateCode();
+
+        // 发送短信
+        smsService.sendCaptcha(phone, smsCode, "验证码");
+
+        // 缓存验证码(3分钟有效)
+        redisCache.setCacheObject(smsCodeKey, smsCode, 180, TimeUnit.SECONDS);
+        // 设置冷却时间(60秒内不能再发)
+        redisCache.setCacheObject(smsCooldownKey, "1", 60, TimeUnit.SECONDS);
+
+        return R.ok("验证码已发送");
+    }
+
     private List<FsUser> findUsersByPhone(String phone) {
         // 先根据加密手机号查询用户
         String jiami = (encryptPhone(phone));

+ 15 - 0
fs-user-app/src/main/java/com/fs/app/controller/IntegralController.java

@@ -66,6 +66,21 @@ public class IntegralController extends  AppBaseController {
     @Cacheable(value = "getIntegralGoodsById", key = "#goodsId")
     public R getIntegralGoodsById(@RequestParam("goodsId")Long goodsId, HttpServletRequest request){
         FsIntegralGoods goods=goodsService.selectFsIntegralGoodsByGoodsId(goodsId);
+        // 填充默认值
+        if (goods != null) {
+            if (goods.getImages() == null) {
+                goods.setImages("");
+            }
+            if (goods.getNum() == null) {
+                goods.setNum(0);
+            }
+            if (goods.getRemark() == null) {
+                goods.setRemark("");
+            }
+            if (goods.getSearchValue() == null) {
+                goods.setSearchValue("");
+            }
+        }
         return R.ok().put("data",goods);
     }
 

+ 57 - 0
fs-user-app/src/main/java/com/fs/app/controller/live/OrderController.java

@@ -0,0 +1,57 @@
+package com.fs.app.controller.live;
+
+import com.fs.app.annotation.Login;
+import com.fs.app.controller.AppBaseController;
+import com.fs.common.core.domain.R;
+import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.service.IMergedOrderService;
+import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@Api("订单信息接口")
+@RestController("AppOrderController")
+@AllArgsConstructor
+@RequestMapping(value="/app/order")
+public class OrderController extends AppBaseController {
+
+    private final IMergedOrderService mergedOrderService;
+
+    @Login
+    @ApiOperation("获取我的合并订单列表(商城订单+直播订单)")
+    @GetMapping("/getMyMergedOrderList")
+    public R getMyMergedOrderList(FsMyStoreOrderQueryParam param, HttpServletRequest request)
+    {
+        // 设置分页参数
+        if (param.getPage() == null || param.getPage() < 1)
+        {
+            param.setPage(1);
+        }
+        if (param.getPageSize() == null || param.getPageSize() < 1)
+        {
+            param.setPageSize(10);
+        }
+        
+        PageHelper.startPage(param.getPage(), param.getPageSize());
+        
+        // 设置用户ID
+        param.setUserId(Long.parseLong(getUserId()));
+        
+        // 查询合并订单列表
+        List<FsMergedOrderListQueryVO> list = mergedOrderService.selectMergedOrderListVO(param);
+        
+        // 封装分页信息
+        PageInfo<FsMergedOrderListQueryVO> listPageInfo = new PageInfo<>(list);
+        
+        return R.ok().put("data", listPageInfo);
+    }
+}

+ 1 - 1
fs-user-app/src/main/java/com/fs/app/controller/store/AddressScrmController.java

@@ -113,7 +113,7 @@ public class AddressScrmController extends AppBaseController {
         FsUserAddressScrm userAddress=new FsUserAddressScrm();
         BeanUtil.copyProperties(address, userAddress);
         addressService.insertFsUserAddress(userAddress);
-        return R.ok("操作成功");
+        return R.ok("操作成功").put("addressId", userAddress.getId());
     }
 
     @Login