Browse Source

Merge remote-tracking branch 'origin/master'

peicj 1 week ago
parent
commit
0b073eda5e
100 changed files with 3159 additions and 591 deletions
  1. 1 1
      deploy.sh
  2. 6 0
      fs-admin/Dockerfile
  3. 41 0
      fs-admin/src/main/java/com/fs/course/business/FsVideoResourceBusinessService.java
  4. 27 23
      fs-admin/src/main/java/com/fs/course/controller/FsVideoResourceController.java
  5. 41 5
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  6. 5 1
      fs-admin/src/main/java/com/fs/his/controller/FsUserCouponController.java
  7. 61 0
      fs-admin/src/main/java/com/fs/his/task/Task.java
  8. 60 3
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreAfterSalesScrmController.java
  9. 45 1
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreHealthOrderScrmController.java
  10. 51 4
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  11. 16 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java
  12. 60 4
      fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java
  13. 16 1
      fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java
  14. 57 6
      fs-admin/src/main/java/com/fs/live/controller/LiveAfterSalesController.java
  15. 19 2
      fs-admin/src/main/java/com/fs/live/controller/LiveController.java
  16. 66 0
      fs-admin/src/main/java/com/fs/live/controller/LiveDataController.java
  17. 29 1
      fs-admin/src/main/java/com/fs/live/controller/LiveOrderController.java
  18. 107 24
      fs-admin/src/main/java/com/fs/qw/qwTask/qwTask.java
  19. 1 1
      fs-admin/src/main/resources/application.yml
  20. 6 0
      fs-common-api/Dockerfile
  21. 24 0
      fs-common-api/pom.xml
  22. 1 1
      fs-common-api/src/main/resources/application.yml
  23. 6 0
      fs-common/src/main/java/com/fs/common/constant/LiveKeysConstant.java
  24. 6 0
      fs-company/Dockerfile
  25. 6 6
      fs-company/src/main/java/com/fs/company/controller/live/LiveAfterSalesController.java
  26. 32 14
      fs-company/src/main/java/com/fs/company/controller/live/LiveController.java
  27. 8 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java
  28. 3 3
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactTransferLogController.java
  29. 8 8
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java
  30. 3 2
      fs-company/src/main/resources/application.yml
  31. 1 1
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  32. 0 1
      fs-live-app/src/main/java/com/fs/framework/aspectj/LiveWatchUserAspect.java
  33. 1 1
      fs-live-app/src/main/java/com/fs/framework/aspectj/RateLimiterAspect.java
  34. 0 2
      fs-live-app/src/main/java/com/fs/live/controller/LiveDataController.java
  35. 12 11
      fs-live-app/src/main/java/com/fs/live/websocket/handle/LiveChatHandler.java
  36. 230 56
      fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java
  37. 69 31
      fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
  38. 34 0
      fs-qwhook/src/main/java/com/fs/app/controller/CommonController.java
  39. 5 5
      fs-service/pom.xml
  40. 13 13
      fs-service/src/main/java/com/cloud/host/CloudHostConfig.java
  41. 4 1
      fs-service/src/main/java/com/fs/company/domain/CompanyUser.java
  42. 3 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCourseVideo.java
  43. 19 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseTrafficLogMapper.java
  44. 18 13
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java
  45. 2 5
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoRedPackageMapper.java
  46. 2 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseTrafficLogService.java
  47. 16 6
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  48. 82 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseTrafficLogServiceImpl.java
  49. 2 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCompanyBindServiceImpl.java
  50. 1 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
  51. 1 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java
  52. 171 215
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  53. 4 4
      fs-service/src/main/java/com/fs/course/vo/FsCourseProductOrderVO.java
  54. 1 0
      fs-service/src/main/java/com/fs/course/vo/FsUserCourseVideoQVO.java
  55. 2 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoPageListVO.java
  56. 9 7
      fs-service/src/main/java/com/fs/fastGpt/service/IFastgptEventLogTotalService.java
  57. 237 7
      fs-service/src/main/java/com/fs/fastGpt/service/impl/FastgptEventLogTotalServiceImpl.java
  58. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsAdv.java
  59. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsUserInformationCollection.java
  60. 0 1
      fs-service/src/main/java/com/fs/his/mapper/FsAdvMapper.java
  61. 3 4
      fs-service/src/main/java/com/fs/his/mapper/FsPackageOrderMapper.java
  62. 3 4
      fs-service/src/main/java/com/fs/his/service/IFsPackageOrderService.java
  63. 30 12
      fs-service/src/main/java/com/fs/his/service/impl/FsPackageOrderServiceImpl.java
  64. 6 4
      fs-service/src/main/java/com/fs/his/service/impl/FsPackageServiceImpl.java
  65. 34 10
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java
  66. 1 1
      fs-service/src/main/java/com/fs/his/service/impl/FsUserInformationCollectionServiceImpl.java
  67. 12 0
      fs-service/src/main/java/com/fs/his/vo/FsUserInfoCollectionAndStoreOrderVo.java
  68. 14 1
      fs-service/src/main/java/com/fs/hisStore/domain/FsStoreProductPackageScrm.java
  69. 39 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsUserCompanyPackageScrm.java
  70. 13 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsUserScrm.java
  71. 11 11
      fs-service/src/main/java/com/fs/hisStore/mapper/FsShippingTemplatesScrmMapper.java
  72. 12 2
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesScrmMapper.java
  73. 14 4
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java
  74. 2 1
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
  75. 14 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStorePaymentScrmMapper.java
  76. 1 1
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreProductScrmMapper.java
  77. 61 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsUserCompanyPackageScrmMapper.java
  78. 11 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsUserScrmMapper.java
  79. 3 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderParam.java
  80. 21 0
      fs-service/src/main/java/com/fs/hisStore/param/FsUsePackageScrmSendParam.java
  81. 24 0
      fs-service/src/main/java/com/fs/hisStore/param/FsUserCouponScrmSendParam.java
  82. 11 11
      fs-service/src/main/java/com/fs/hisStore/service/IFsShippingTemplatesScrmService.java
  83. 11 8
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreCouponIssueScrmService.java
  84. 5 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStorePaymentScrmService.java
  85. 5 1
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreProductPackageScrmService.java
  86. 5 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsUserScrmService.java
  87. 3 3
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsShippingTemplatesScrmServiceImpl.java
  88. 45 3
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java
  89. 53 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreCouponIssueScrmServiceImpl.java
  90. 98 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  91. 95 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStorePaymentScrmServiceImpl.java
  92. 37 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductAttrScrmServiceImpl.java
  93. 37 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductAttrValueScrmServiceImpl.java
  94. 32 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductPackageScrmServiceImpl.java
  95. 322 3
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  96. 11 4
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserScrmServiceImpl.java
  97. 43 3
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreAfterSalesVO.java
  98. 133 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportRefundZMVO.java
  99. 3 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportVO.java
  100. 124 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportZMVO.java

+ 1 - 1
deploy.sh

@@ -87,7 +87,7 @@ deploy_jar() {
     echo "启动服务..."
     ssh -f "$REMOTE_USER@$remote_host" \
     "cd $REMOTE_BASE_DIR/$remote_dir && \
-     nohup java -jar  -Dfile.encoding=UTF-8 $app_name.jar --spring.profiles.active=druid-bjzm --server.port=7114  \
+     nohup java -jar -Xms8g -Xmx8g -XX:+UseG1GC -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 $app_name.jar --spring.profiles.active=druid-bjzm --server.port=7114  \
      >> $app_name.log 2>&1 &"
 
     # 检查进程是否启动成功

+ 6 - 0
fs-admin/Dockerfile

@@ -0,0 +1,6 @@
+FROM openjdk:8-jre
+# java版本,最好使用openjdk,而不是类似于Java:1.8
+COPY ./target/fs-admin.jar fs-admin.jar
+# 向外暴露的接口,最好与项目yml文件中的端口一致
+ENTRYPOINT ["java","-jar","fs-admin.jar"]
+# 执行启动命令java -jar

+ 41 - 0
fs-admin/src/main/java/com/fs/course/business/FsVideoResourceBusinessService.java

@@ -0,0 +1,41 @@
+package com.fs.course.business;
+
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.fs.course.domain.FsUserCourseVideo;
+import com.fs.course.domain.FsVideoResource;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.service.IFsVideoResourceService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class FsVideoResourceBusinessService {
+
+    private final IFsVideoResourceService fsVideoResourceService;
+
+    private final IFsUserCourseVideoService fsUserCourseVideoService;
+
+    public FsVideoResourceBusinessService(IFsVideoResourceService fsVideoResourceService,
+                                          IFsUserCourseVideoService fsUserCourseVideoService) {
+        this.fsVideoResourceService = fsVideoResourceService;
+        this.fsUserCourseVideoService = fsUserCourseVideoService;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void edit(FsVideoResource fsVideoResource) {
+        FsVideoResource oldResource = fsVideoResourceService.getById(fsVideoResource.getId());
+        fsVideoResourceService.updateById(fsVideoResource);
+        fsUserCourseVideoService.update(new UpdateWrapper<FsUserCourseVideo>()
+                .eq("video_url", oldResource.getVideoUrl())
+                .set("video_url", fsVideoResource.getVideoUrl())
+                .set("line_one", fsVideoResource.getLine1())
+                .set("line_two", fsVideoResource.getLine2())
+                .set("line_three", fsVideoResource.getLine3())
+                .set("duration", fsVideoResource.getDuration())
+                .set("file_size", fsVideoResource.getFileSize())
+                .set("file_key", fsVideoResource.getFileKey())
+                .set("file_name", fsVideoResource.getFileName())
+                .set("thumbnail", fsVideoResource.getThumbnail())
+        );
+    }
+}

+ 27 - 23
fs-admin/src/main/java/com/fs/course/controller/FsVideoResourceController.java

@@ -11,6 +11,8 @@ import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
+import com.fs.config.cloud.CloudHostProper;
+import com.fs.course.business.FsVideoResourceBusinessService;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsVideoResource;
 import com.fs.course.service.IFsVideoResourceService;
@@ -19,7 +21,6 @@ import com.fs.framework.web.service.TokenService;
 import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import lombok.AllArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
@@ -37,11 +38,13 @@ public class FsVideoResourceController extends BaseController {
 
     private final IFsVideoResourceService fsVideoResourceService;
 
-    @Autowired
-    private TokenService tokenService;
+    private final TokenService tokenService;
 
-    @Autowired
-    private ISysConfigService configService;
+    private final ISysConfigService configService;
+
+    private final CloudHostProper cloudHostProper;
+
+    private final FsVideoResourceBusinessService videoResourceBusinessService;
 
     /**
      * 查询视频素材库列表
@@ -53,8 +56,7 @@ public class FsVideoResourceController extends BaseController {
                               @RequestParam(required = false) Integer typeId,
                               @RequestParam(required = false) Integer typeSubId,
                               @RequestParam(required = false, defaultValue = "1") Integer pageNum,
-                              @RequestParam(required = false, defaultValue = "10") Integer pageSize)
-    {
+                              @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
         Map<String, Object> params = new HashMap<>();
         params.put("resourceName", resourceName);
         params.put("fileName", fileName);
@@ -63,7 +65,7 @@ public class FsVideoResourceController extends BaseController {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
+        if (ObjectUtil.isNotEmpty(config.getIsBound()) && config.getIsBound()) {
             params.put("userId", loginUser.getUser().getUserId());
         }
         PageHelper.startPage(pageNum, pageSize);
@@ -77,8 +79,7 @@ public class FsVideoResourceController extends BaseController {
      */
     @PreAuthorize("@ss.hasPermi('course:videoResource:query')")
     @GetMapping(value = "/{id}")
-    public AjaxResult getInfo(@PathVariable("id") Long id)
-    {
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
         return AjaxResult.success(fsVideoResourceService.getById(id));
     }
 
@@ -88,13 +89,12 @@ public class FsVideoResourceController extends BaseController {
     @PreAuthorize("@ss.hasPermi('course:videoResource:add')")
     @Log(title = "视频素材库", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@RequestBody FsVideoResource fsVideoResource)
-    {
+    public AjaxResult add(@RequestBody FsVideoResource fsVideoResource) {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         Long userId = loginUser.getUser().getUserId();
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
+        if (ObjectUtil.isNotEmpty(config.getIsBound()) && config.getIsBound()) {
             fsVideoResource.setUserId(userId);
         }
         fsVideoResource.setCreateTime(LocalDateTime.now());
@@ -108,8 +108,12 @@ public class FsVideoResourceController extends BaseController {
     @PreAuthorize("@ss.hasPermi('course:videoResource:edit')")
     @Log(title = "视频素材库", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody FsVideoResource fsVideoResource)
-    {
+    public AjaxResult edit(@RequestBody FsVideoResource fsVideoResource) {
+        if (("今正科技".equals(cloudHostProper.getCompanyName()))) {
+            // 同步资源到课程
+            videoResourceBusinessService.edit(fsVideoResource);
+            return AjaxResult.success();
+        }
         fsVideoResourceService.updateById(fsVideoResource);
         return AjaxResult.success();
     }
@@ -120,8 +124,7 @@ public class FsVideoResourceController extends BaseController {
     @PreAuthorize("@ss.hasPermi('course:videoResource:remove')")
     @Log(title = "视频素材库", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
-    public AjaxResult remove(@PathVariable Long[] ids)
-    {
+    public AjaxResult remove(@PathVariable Long[] ids) {
         Wrapper<FsVideoResource> updateWrapper = Wrappers.<FsVideoResource>lambdaUpdate()
                 .set(FsVideoResource::getIsDel, 1)
                 .in(FsVideoResource::getId, Arrays.asList(ids));
@@ -131,6 +134,7 @@ public class FsVideoResourceController extends BaseController {
 
     /**
      * 批量修改视频分类
+     *
      * @param typeId
      * @param typeSubId
      * @param ids
@@ -142,13 +146,13 @@ public class FsVideoResourceController extends BaseController {
     public AjaxResult batchUpdateClass(@RequestParam("typeId") Long typeId,
                                        @RequestParam("typeSubId") Long typeSubId,
                                        @RequestParam("ids") String ids) {
-        if(typeId == null || typeId <= 0){
+        if (typeId == null || typeId <= 0) {
             return AjaxResult.error("请选择分类");
         }
-        if(typeSubId == null || typeSubId <= 0){
+        if (typeSubId == null || typeSubId <= 0) {
             return AjaxResult.error("请选择子分类");
         }
-        if(ids == null || ids.isEmpty()){
+        if (ids == null || ids.isEmpty()) {
             return AjaxResult.error("请选择要修改的分类");
         }
 
@@ -178,12 +182,12 @@ public class FsVideoResourceController extends BaseController {
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
-        list.forEach(v ->{
+        list.forEach(v -> {
             v.setCreateTime(LocalDateTime.now());
-            if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
+            if (ObjectUtil.isNotEmpty(config.getIsBound()) && config.getIsBound()) {
                 v.setUserId(userId);
             }
-        } );
+        });
         fsVideoResourceService.saveBatch(list);
         return AjaxResult.success();
     }

+ 41 - 5
fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java

@@ -47,6 +47,7 @@ import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.service.ISysRoleService;
 import com.github.pagehelper.PageHelper;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -73,6 +74,7 @@ import static com.fs.his.utils.PhoneUtil.*;
  */
 @RestController
 @RequestMapping("/his/storeOrder")
+@Slf4j
 public class FsStoreOrderController extends BaseController
 {
     @Autowired
@@ -128,6 +130,9 @@ public class FsStoreOrderController extends BaseController
     private IFsDfAccountService fsDfAccountService;
     @Autowired
     private FsUserInformationCollectionMapper userInformationCollectionMapper;
+    @Autowired
+    private IFsPackageOrderService fsPackageOrderService;
+
     /**
      * 查询订单列表
      */
@@ -418,6 +423,14 @@ public class FsStoreOrderController extends BaseController
             tuiMoneyLogs=moneyLogsService.selectCompanyStoreOrderMoneyLogsList(moneyLogsMap);
         }
         if ((CloudHostUtils.hasCloudHostName("金牛明医"))){
+            if (order.getStatus() == 2 ){
+                FsUserInfoCollectionAndStoreOrderVo infoVo = fsPackageOrderService.selectInformationCollectionByStoreOrderId(orderId);
+                if (infoVo!=null) {
+                    if (infoVo.getDoctorType2Confirm() == null || infoVo.getDoctorType2Confirm()!=1) {
+                        order.setStatus(7);
+                    }
+                }
+            }
             return R.ok().put("data",order).put("tuiMoneyLogs",tuiMoneyLogs).put("isUpdateRefund",1).put("isUpdatePayRemain",1);
         } else {
             return R.ok().put("data",order).put("tuiMoneyLogs",tuiMoneyLogs).put("isUpdateRefund",0).put("isUpdatePayRemain",0);
@@ -479,6 +492,17 @@ public class FsStoreOrderController extends BaseController
     {
         AjaxResult error = moneyCheck(fsStoreOrder);
         if (error != null) return error;
+        try {
+            FsUserInfoCollectionAndStoreOrderVo infoVo = fsPackageOrderService.selectInformationCollectionByStoreOrderId(fsStoreOrder.getOrderId());
+            if (infoVo != null) {
+                Integer doctorType2Confirm = infoVo.getDoctorType2Confirm();
+                if (doctorType2Confirm == null || doctorType2Confirm != 1) {
+                    return AjaxResult.error("信息采集订单药师未确认,不能修改状态!");
+                }
+            }
+        } catch (Exception e) {
+            log.error("信息采集查询错误,orderId: " + fsStoreOrder.getOrderId() + ",信息:{}"+ e);
+        }
         return toAjax(fsStoreOrderService.updateFsStoreOrder(fsStoreOrder));
     }
 
@@ -652,8 +676,8 @@ public class FsStoreOrderController extends BaseController
     {
         String nickName = getLoginUser().getUser().getNickName();
         if(CloudHostUtils.hasCloudHostName("金牛明医")){
-            FsUserInformationCollection fsUserInformationCollection = userInformationCollectionMapper.selectFsUserInformationCollectionByOrderCode(fsStoreOrder.getOrderCode());
-            if (fsUserInformationCollection != null&&fsUserInformationCollection.getDoctorType2Confirm()!=1) {
+            FsUserInfoCollectionAndStoreOrderVo info = fsPackageOrderService.selectInformationCollectionByStoreOrderId(fsStoreOrder.getOrderId());
+            if (info != null&&info.getDoctorType2Confirm()!=1) {
                 return AjaxResult.error("药师未确认");
             }
         }
@@ -760,8 +784,16 @@ public class FsStoreOrderController extends BaseController
         if (orderIds.isEmpty()){
             return R.ok();
         }
-        orderIds.forEach(orderId->{
+        //失败的订单
+        StringBuilder msg = new StringBuilder();
+        for (Long orderId : orderIds) {
             try {
+                //判断是否是信息采集订单 且 药师已经确认
+                FsUserInfoCollectionAndStoreOrderVo info = fsPackageOrderService.selectInformationCollectionByStoreOrderId(orderId);
+                if (info != null&&info.getDoctorType2Confirm()!=1) {
+                    msg.append("订单编号:").append(info.getStoreOrderCode()).append(",推送erp失败,失败原因-信息采集订单,药师暂未确认!\n");
+                    continue;
+                }
                 df.setOrderId(orderId);
                 FsStoreOrderDf temp = fsStoreOrderDfService.selectFsStoreOrderDfByOrderId(df.getOrderId());
                 if (temp == null){
@@ -777,8 +809,12 @@ public class FsStoreOrderController extends BaseController
                 throw new RuntimeException(e);
             }
 
-        });
-        return R.ok();
+        }
+        if (msg.toString().isEmpty()){
+            return R.ok();
+        } else {
+            return R.error(msg.toString());
+        }
     }
 
 

+ 5 - 1
fs-admin/src/main/java/com/fs/his/controller/FsUserCouponController.java

@@ -3,6 +3,7 @@ package com.fs.his.controller;
 import java.util.List;
 
 import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.utils.StringUtils;
 import com.fs.his.param.FsUserCouponParam;
 import com.fs.his.param.FsUserCouponSendParam;
 import com.fs.his.vo.FsFollowExcelListVO;
@@ -62,7 +63,10 @@ public class FsUserCouponController extends BaseController
         startPage();
         List<FsUserCouponListVO> list = fsUserCouponService.selectFsUserCouponListVO(fsUserCoupon);
         for (FsUserCouponListVO vo : list) {
-            vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+            String phone = vo.getPhone();
+            if (StringUtils.isNotBlank(phone)) {
+                vo.setPhone(phone.replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+            }
         }
         return getDataTable(list);
     }

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

@@ -57,6 +57,12 @@ import com.fs.his.service.impl.FsPackageOrderServiceImpl;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.FsSubOrderResultVO;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
+import com.fs.hisStore.domain.FsStorePaymentScrm;
+import com.fs.hisStore.mapper.FsStorePaymentScrmMapper;
+import com.fs.hisStore.service.IFsStorePaymentScrmService;
+import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
+import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
+import com.fs.huifuPay.service.HuiFuService;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
 import com.fs.im.service.OpenIMService;
@@ -81,6 +87,8 @@ import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.*;
@@ -1069,6 +1077,59 @@ public class Task {
         System.out.println(msgResponseDTO);
     }
 
+    @Autowired
+    FsStorePaymentScrmMapper fsStorePaymentScrmMapper;
+
+    @Autowired
+    HuiFuService huiFuService;
+
+    /**
+     * 查询同步商城订单的支付转台
+     */
+    public void syncOrderPayStatus(){
+        //查询支付状态是待支付的数据
+        FsStorePaymentScrm paymentScrm = new FsStorePaymentScrm();
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
+        paymentScrm.setStatus(0);
+        List<FsStorePaymentScrm> fsStorePaymentScrms = fsStorePaymentScrmMapper.selectFsStorePaymentList(paymentScrm);
+        if(null != fsStorePaymentScrms && !fsStorePaymentScrms.isEmpty()){
+            for (FsStorePaymentScrm payment : fsStorePaymentScrms) {
+                if(StringUtils.isBlank(payment.getTradeNo())){
+                    continue;
+                }
+                V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
+                request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
+                request.setOrgHfSeqId(payment.getTradeNo());
+                HuiFuQueryOrderResult o = null;
+                try {
+                    o = huiFuService.queryOrder(request);
+                } catch (Exception e) {
+                    log.error("查询失败:"+e.getMessage());
+                    continue;
+                }
+                log.info("汇付返回" + o);
+                if ("00000000".equals(o.getResp_code()) && "S".equals(o.getTrans_stat())) {
+                    try {
+                        Date payTime = formatter.parse(o.getEnd_time());
+                        FsStorePaymentScrm payUpdate = new FsStorePaymentScrm();
+                        payUpdate.setPaymentId(payment.getPaymentId());
+                        payUpdate.setPayTime(payTime);
+                        payUpdate.setStatus(1);
+                        payUpdate.setTradeNo(o.getOrg_hf_seq_id());
+                        payUpdate.setBankSerialNo(o.getParty_order_id());
+                        payUpdate.setBankTransactionId(o.getOut_trans_id());
+                        fsStorePaymentScrmMapper.updateFsStorePayment(payUpdate);
+                    } catch (ParseException e) {
+                        log.error("更新失败 payment:{}",payment ,e);
+//                        throw new RuntimeException(e);
+                    }
+
+
+                }
+            }
+        }
+    }
+
 //    @Autowired
 //    private FsStoreMapper fsStoreMapper;
 //    @Autowired

+ 60 - 3
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreAfterSalesScrmController.java

@@ -1,5 +1,6 @@
 package com.fs.hisStore.controller;
 
+import cn.hutool.core.bean.BeanUtil;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -27,12 +28,18 @@ import com.fs.hisStore.service.IFsStoreAfterSalesScrmService;
 import com.fs.hisStore.service.IFsStoreAfterSalesStatusScrmService;
 import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.vo.FsStoreAfterSalesVO;
+import com.fs.hisStore.vo.FsStoreOrderItemExportRefundZMVO;
+import com.fs.hisStore.vo.FsStoreOrderItemExportZMVO;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import java.math.BigDecimal;
 import java.text.ParseException;
 import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * 售后记录Controller
@@ -57,6 +64,10 @@ public class FsStoreAfterSalesScrmController extends BaseController
 
     @Autowired
     private TokenService tokenService;
+
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
+
     /**
      * 查询售后记录列表
      */
@@ -84,16 +95,62 @@ public class FsStoreAfterSalesScrmController extends BaseController
             fsStoreAfterSales.setBeginTime(null);
             fsStoreAfterSales.setEndTime(null);
         }
-        Boolean a = fsStoreAfterSalesService.isEntityNull(fsStoreAfterSales);
-        if (fsStoreAfterSalesService.isEntityNull(fsStoreAfterSales)){
+//        Boolean a = fsStoreAfterSalesService.isEntityNull(fsStoreAfterSales);
+        if (fsStoreAfterSalesService.isEntityNull(fsStoreAfterSales) && Objects.isNull(fsStoreAfterSales.getParams())){
             return AjaxResult.error("请筛选数据导出");
         }
+
         List<FsStoreAfterSalesVO> list = fsStoreAfterSalesService.selectFsStoreAfterSalesListVO(fsStoreAfterSales);
+        if("北京卓美".equals(signProjectName)){
+            List<FsStoreOrderItemExportRefundZMVO> zmvoList = list.stream()
+                    .map(vo -> {
+                        FsStoreOrderItemExportRefundZMVO zmvo = new FsStoreOrderItemExportRefundZMVO();
+                        try {
+                            zmvo.setOrderCode(vo.getOrderCode());
+                            zmvo.setStatus(vo.getOrderStatus().toString());
+                            zmvo.setUserId(vo.getUserId());
+                            zmvo.setProductName(vo.getProductName());
+                            zmvo.setBarCode(vo.getProductBarCode());
+                            zmvo.setSku(vo.getSku());
+                            zmvo.setNum(vo.getNum());
+                            zmvo.setPrice(vo.getPrice());
+                            zmvo.setCost(vo.getCost());
+//                            zmvo.setFPrice("");
+                            zmvo.setPayMoney(vo.getPayMoney());
+                            zmvo.setPayPostage(vo.getTotalPostage());
+                            zmvo.setCateName(vo.getCateName());
+                            zmvo.setRealName(vo.getUserName());
+                            zmvo.setUserPhone(vo.getUserPhone());
+                            zmvo.setUserAddress(vo.getUserAddress());
+                            zmvo.setCreateTime(vo.getCreateTime());
+                            zmvo.setPayTime(vo.getOrderPayTime());
+                            zmvo.setDeliverySn(vo.getOrderDeliverySn());
+                            zmvo.setDeliveryName(vo.getOrderDeliveryName());
+                            zmvo.setDeliveryId(vo.getOrderDeliveryId());
+                            zmvo.setCompanyName(vo.getCompanyName());
+                            zmvo.setCompanyUserNickName(vo.getCompanyUserNickName());
+                            zmvo.setRefundTime(vo.getCreateTime());
+//                            zmvo.setAfterSalesNumber
+                            zmvo.setRefundMoney(vo.getRefundAmount());
+                            zmvo.setBankTransactionId(vo.getBankTransactionId());
+                        } catch (Exception e) {
+                            // 处理异常
+                            e.printStackTrace();
+                        }
+                        return zmvo;
+                    })
+                    .collect(Collectors.toList());
+            for (FsStoreOrderItemExportRefundZMVO vo : zmvoList){
+                vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
+            }
+            ExcelUtil<FsStoreOrderItemExportRefundZMVO> util = new ExcelUtil<FsStoreOrderItemExportRefundZMVO>(FsStoreOrderItemExportRefundZMVO.class);
+            return util.exportExcel(zmvoList, "退款订单导出");
+        }
         for (FsStoreAfterSalesVO vo : list){
             vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
         }
         ExcelUtil<FsStoreAfterSalesVO> util = new ExcelUtil<FsStoreAfterSalesVO>(FsStoreAfterSalesVO.class);
-        return util.exportExcel(list, "storeAfterSales");
+        return util.exportExcel(list, "退款订单导出");
     }
     /**
      * 获取售后记录详细信息

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

@@ -28,6 +28,7 @@ import com.fs.hisStore.service.*;
 import com.fs.hisStore.vo.*;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
@@ -35,6 +36,7 @@ import org.springframework.web.multipart.MultipartFile;
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.stream.Collectors;
 
 @RestController
 @RequestMapping("/store/store/storeOrder")
@@ -71,6 +73,9 @@ public class FsStoreHealthOrderScrmController extends BaseController {
     // 最大文件大小(5MB)
     private static final long MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
 
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
+
     /**
      * 查询健康商城订单列表
      */
@@ -112,7 +117,7 @@ public class FsStoreHealthOrderScrmController extends BaseController {
                     }
                 }
                 //
-                if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                if ((loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*") && (vo.getCost() !=null && vo.getTotalNum() != null))) {
                     vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
                 } else {
                     vo.setPayPostage(BigDecimal.ZERO);
@@ -342,6 +347,45 @@ public class FsStoreHealthOrderScrmController extends BaseController {
         }
         param.setIsHealth("1");
         List<FsStoreOrderItemExportVO> list = orderItemService.selectFsStoreOrderItemListExportVO(param);
+        if("北京卓美".equals(signProjectName)){
+            List<FsStoreOrderItemExportZMVO> zmvoList = list.stream()
+                    .map(vo -> {
+                        FsStoreOrderItemExportZMVO zmvo = new FsStoreOrderItemExportZMVO();
+                        try {
+                            BeanUtil.copyProperties( vo,zmvo);
+                        } catch (Exception e) {
+                            // 处理异常
+                            e.printStackTrace();
+                        }
+                        return zmvo;
+                    })
+                    .collect(Collectors.toList());
+            //对手机号脱敏
+            if (zmvoList != null) {
+                    LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+                    for (FsStoreOrderItemExportZMVO vo : zmvoList) {
+                        if (!StringUtils.isEmpty(vo.getJsonInfo())) {
+                            try {
+                                StoreOrderProductDTO orderProductDTO = JSONObject.parseObject(vo.getJsonInfo(), StoreOrderProductDTO.class);
+                                BeanUtil.copyProperties(orderProductDTO, vo);
+                            } catch (Exception e) {
+                            }
+                        }
+                        if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                            vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
+                        } else {
+                            vo.setPayPostage(BigDecimal.ZERO);
+                            vo.setCost(BigDecimal.ZERO);
+                            vo.setFPrice(BigDecimal.ZERO);
+                            vo.setBarCode("");
+                            vo.setCateName("");
+                            vo.setBankTransactionId("");
+                        }
+                    }
+                }
+                ExcelUtil<FsStoreOrderItemExportZMVO> util = new ExcelUtil<FsStoreOrderItemExportZMVO>(FsStoreOrderItemExportZMVO.class);
+                return util.exportExcel(zmvoList, "订单明细数据");
+        }
         //对手机号脱敏
         if (list != null) {
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());

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

@@ -53,10 +53,12 @@ import com.fs.hisStore.service.*;
 import com.fs.hisStore.vo.*;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
+import com.github.pagehelper.PageHelper;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -136,6 +138,9 @@ public class FsStoreOrderScrmController extends BaseController {
     @Autowired
     private IFsStoreOrderLogsScrmService fsStoreOrderLogsService;
 
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
+
     private IErpOrderService getErpService(){
         //判断是否开启erp
         IErpOrderService erpOrderService = null;
@@ -173,7 +178,7 @@ public class FsStoreOrderScrmController extends BaseController {
     @PreAuthorize("@ss.hasPermi('store:storeOrder:list')")
     @PostMapping("/list")
     public TableDataInfo list(@RequestBody FsStoreOrderParam param) {
-        startPage();
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         if(!StringUtils.isEmpty(param.getCreateTimeRange())){
             param.setCreateTimeList(param.getCreateTimeRange().split("--"));
         }
@@ -208,7 +213,7 @@ public class FsStoreOrderScrmController extends BaseController {
                     }
                 }
                 //
-                if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                if ((loginUser.getPermissions().contains("his:storeAfterSales:finance")) || loginUser.getPermissions().contains("*:*:*") && (vo.getCost() != null && vo.getTotalNum() != null)) {
                     vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
                 } else {
                     vo.setPayPostage(BigDecimal.ZERO);
@@ -249,7 +254,7 @@ public class FsStoreOrderScrmController extends BaseController {
     @PreAuthorize("@ss.hasPermi('store:storeOrder:payRemainList')")
     @GetMapping("/payRemainList")
     public TableDataInfo payRemainList(FsStoreOrderParam param) {
-        startPage();
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         if(!StringUtils.isEmpty(param.getCreateTimeRange())){
             param.setCreateTimeList(param.getCreateTimeRange().split("--"));
         }
@@ -510,6 +515,47 @@ public class FsStoreOrderScrmController extends BaseController {
         }
         param.setNotHealth(1);
         List<FsStoreOrderItemExportVO> list = orderItemService.selectFsStoreOrderItemListExportVO(param);
+        if("北京卓美".equals(signProjectName)){
+            List<FsStoreOrderItemExportZMVO> zmvoList = list.stream()
+                    .map(vo -> {
+                        FsStoreOrderItemExportZMVO zmvo = new FsStoreOrderItemExportZMVO();
+                        try {
+                            BeanUtil.copyProperties( vo,zmvo);
+                        } catch (Exception e) {
+                            // 处理异常
+                            e.printStackTrace();
+                        }
+                        return zmvo;
+                    })
+                    .collect(Collectors.toList());
+            //对手机号脱敏
+            if (zmvoList != null) {
+                LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+                for (FsStoreOrderItemExportZMVO vo : zmvoList) {
+                    if (!StringUtils.isEmpty(vo.getJsonInfo())) {
+                        try {
+                            StoreOrderProductDTO orderProductDTO = JSONObject.parseObject(vo.getJsonInfo(), StoreOrderProductDTO.class);
+                            BeanUtil.copyProperties(orderProductDTO, vo);
+                        } catch (Exception e) {
+                        }
+                    }
+                    //
+                    if (loginUser.getPermissions().contains("his:storeAfterSales:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                        vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getTotalNum())));
+                    } else {
+                        vo.setPayPostage(BigDecimal.ZERO);
+                        vo.setCost(BigDecimal.ZERO);
+                        vo.setFPrice(BigDecimal.ZERO);
+                        vo.setBarCode("");
+                        vo.setCateName("");
+                        vo.setBankTransactionId("");
+                    }
+                }
+            }
+            ExcelUtil<FsStoreOrderItemExportZMVO> util = new ExcelUtil<FsStoreOrderItemExportZMVO>(FsStoreOrderItemExportZMVO.class);
+            return util.exportExcel(zmvoList, "订单明细数据");
+        }
+
         //对手机号脱敏
         if (list != null) {
             LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
@@ -793,7 +839,8 @@ public class FsStoreOrderScrmController extends BaseController {
 
     @GetMapping("/getCustomerOrderList")
     public TableDataInfo getCustomerOrderList(FsStoreOrderParam param) {
-        startPage();
+//        startPage();
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
         List<FsStoreOrderVO> list = fsStoreOrderService.selectFsCustomerStoreOrderListVO(param);
         if (list != null) {
             for (FsStoreOrderVO vo : list) {

+ 16 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java

@@ -334,4 +334,20 @@ public class FsStorePaymentScrmController extends BaseController
     {
         return toAjax(fsStorePaymentService.deleteFsStorePaymentByIds(paymentIds));
     }
+
+    /**
+     * 一键发货
+     * @return R
+     * **/
+    @Log(title = "发货同步导入", businessType = BusinessType.IMPORT)
+    @PostMapping("/oneClickShipping")
+    public R oneClickShipping() {
+
+        try {
+            return fsStorePaymentService.oneClickShipping();
+        }catch (Exception e){
+            e.getStackTrace();
+        }
+        return R.ok();
+    }
 }

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

@@ -2,7 +2,9 @@ package com.fs.hisStore.task;
 
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
+import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.DateUtils;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
@@ -21,7 +23,11 @@ import com.fs.hisStore.dto.DateComparisonConfigDTO;
 import com.fs.hisStore.enums.ShipperCodeEnum;
 import com.fs.hisStore.mapper.FsStoreProductAttrValueScrmMapper;
 import com.fs.hisStore.param.FsStoreOrderAddTuiMoneyParam;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.service.IFsStoreProductScrmService;
+import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
+import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
+import com.fs.huifuPay.service.HuiFuService;
 import com.fs.live.domain.LiveAfterSales;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.domain.LiveOrderItem;
@@ -49,8 +55,10 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.time.LocalTime;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
@@ -89,9 +97,6 @@ public class LiveTask {
     @Autowired
     private ILiveOrderPaymentService liveOrderPaymentService;
 
-    @Autowired
-    private IFsUserService userService;
-
     @Autowired
     private ICompanyService companyService;
 
@@ -149,14 +154,60 @@ public class LiveTask {
     @Autowired
     private JdbcTemplate jdbcTemplate;
 
+    @Autowired
+    private HuiFuService huiFuService;
+
+    @Autowired
+    private IFsStoreOrderScrmService orderService;
+
+
+    // 订单银行回调数据丢失补偿
+    public void recoveryBankOrder() {
+        // 查询出来最近三十分钟的订单 待支付 未退款
+        List<LiveOrder> list = liveOrderService.selectBankOrder();
+        if(list == null || list.isEmpty()) return;
+        for (LiveOrder liveOrder : list) {
+            List<LiveOrderPayment> liveOrderPayments = liveOrderPaymentMapper.selectLiveOrderPaymentByOrderId(liveOrder.getOrderId());
+            if(liveOrderPayments == null || liveOrderPayments.isEmpty()) continue;
+            for (LiveOrderPayment payment : liveOrderPayments) {
+                V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
+                request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
+                request.setOrgHfSeqId(payment.getTradeNo());
+                HuiFuQueryOrderResult o = null;
+                try {
+                    o = huiFuService.queryOrder(request);
+                } catch (Exception e) {
+                    log.error("查询失败:"+e.getMessage());
+                    continue;
+                }
+                log.info("汇付返回"+o);
+                if ("00000000".equals(o.getResp_code()) && "S".equals(o.getTrans_stat())) {
+                    String[] order=o.getOrg_req_seq_id().split("-");
+                    if ("live".equals(order[0])) {
+                        liveOrderService.payConfirm(1, null, order[1], o.getOrg_hf_seq_id(), o.getOut_trans_id(), o.getParty_order_id());
+                    }
+                }
+            }
+        }
+    }
+
+
     public void PushErp() throws ParseException {
         List<Long> ids = liveOrderMapper.selectOrderIdByNoErp();
         if(ids == null) return;
         if (ids.size() > 50) {
             ids = ids.subList(0, 50);
         }
+//        liveOrderService.batchUpdateTimeIds(ids);
+        // 单个异常影响全部,跳过异常单子
         for (Long id : ids) {
-            liveOrderService.createOmsOrder(id);
+            try {
+                liveOrderService.createOmsOrder(id);
+            } catch (Exception e) {
+                log.error("创建直播oms订单失败:"+id);
+                log.error("创建直播oms订单失败:"+e.getMessage());
+            }
+
         }
     }
 
@@ -189,6 +240,11 @@ public class LiveTask {
         if (list.size() > 50) {
             list = list.subList(0, 50);
         }
+        Date nowDate = DateUtils.getNowDate();
+        for (LiveOrder order : list) {
+            order.setUpdateTime(nowDate);
+        }
+        liveOrderService.batchUpdateTime(list);
         for (LiveOrder order : list) {
             ErpOrderQueryRequert request = new ErpOrderQueryRequert();
             request.setCode(order.getExtendOrderId());

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

@@ -4,6 +4,7 @@ package com.fs.hisStore.task;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.DateUtils;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
@@ -32,6 +33,7 @@ import com.fs.hisStore.mapper.FsStorePaymentScrmMapper;
 import com.fs.hisStore.mapper.FsStoreProductAttrValueScrmMapper;
 import com.fs.hisStore.param.*;
 import com.fs.hisStore.service.*;
+import com.fs.live.domain.LiveOrder;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.service.IPayService;
 import com.fs.store.config.StoreConfig;
@@ -49,6 +51,7 @@ import java.math.BigDecimal;
 import java.text.ParseException;
 import java.time.LocalTime;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
@@ -168,8 +171,15 @@ public class MallStoreTask
         if (!ids.isEmpty() && ids.size() > 50) {
             ids = ids.subList(0, 50);
         }
+        // 单个异常影响全部,跳过异常单子
         for (Long id : ids) {
-            fsStoreOrderService.createOmsOrder(id);
+            try {
+                fsStoreOrderService.createOmsOrder(id);
+            } catch (Exception e) {
+                log.error("创建商城oms订单失败:"+id);
+                log.error("创建商城oms订单失败:"+e.getMessage());
+            }
+
         }
     }
 
@@ -210,6 +220,11 @@ public class MallStoreTask
         if (list != null && list.size() > 50) {
             list = list.subList(0, 50);
         }
+        Date nowDate = DateUtils.getNowDate();
+        for (FsStoreOrderScrm order : list) {
+            order.setUpdateTime(nowDate);
+        }
+        fsStoreOrderMapper.batchUpdateTime(list);
         for (FsStoreOrderScrm order : list){
             ErpOrderQueryRequert request = new ErpOrderQueryRequert();
             request.setCode(order.getExtendOrderId());

+ 57 - 6
fs-admin/src/main/java/com/fs/live/controller/LiveAfterSalesController.java

@@ -16,6 +16,7 @@ import com.fs.his.domain.FsStoreAfterSalesLogs;
 import com.fs.his.domain.FsUser;
 import com.fs.his.enums.FsStoreAfterSalesStatusEnum;
 import com.fs.his.service.IFsUserService;
+import com.fs.hisStore.vo.FsStoreOrderItemExportRefundZMVO;
 import com.fs.live.domain.LiveAfterSales;
 import com.fs.live.domain.LiveAfterSalesItem;
 import com.fs.live.domain.LiveAfterSalesLogs;
@@ -31,11 +32,13 @@ import com.fs.live.service.ILiveOrderService;
 import com.fs.live.vo.LiveAfterSalesVo;
 import com.github.pagehelper.PageHelper;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.text.ParseException;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 售后记录Controller
@@ -60,11 +63,14 @@ public class LiveAfterSalesController extends BaseController
     private IFsUserService userService;
     @Autowired
     private ILiveOrderService orderService;
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
+
 
     /**
      * 获取售后记录详细信息
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:query')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:query')")
     @GetMapping(value = "/{id}")
     public R getInfo(@PathVariable("id") Long id)
     {
@@ -82,7 +88,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 查询售后记录列表
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:list')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:list')")
     @GetMapping("/list")
     public TableDataInfo list(LiveAfterSalesVo liveAfterSales)
     {
@@ -97,7 +103,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 导出售后记录列表
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:export')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:export')")
     @Log(title = "售后记录", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
     public AjaxResult export(LiveAfterSalesVo liveAfterSales)
@@ -105,6 +111,51 @@ public class LiveAfterSalesController extends BaseController
         PageHelper.clearPage();
         PageHelper.startPage(1, 10000, "");
         List<LiveAfterSalesVo> list = liveAfterSalesService.selectLiveAfterSalesVoList(liveAfterSales);
+        if("北京卓美".equals(signProjectName)){
+            List<FsStoreOrderItemExportRefundZMVO> zmvoList = list.stream()
+                    .map(vo -> {
+                        FsStoreOrderItemExportRefundZMVO zmvo = new FsStoreOrderItemExportRefundZMVO();
+                        try {
+                            zmvo.setOrderCode(vo.getOrderCode());
+                            zmvo.setStatus(vo.getOrderStatus().toString());
+                            zmvo.setUserId(vo.getUserId());
+                            zmvo.setProductName(vo.getProductName());
+                            zmvo.setBarCode(vo.getProductBarCode());
+                            zmvo.setSku(vo.getSku());
+                            zmvo.setNum(vo.getNum());
+                            zmvo.setPrice(vo.getPrice());
+                            zmvo.setCost(vo.getCost());
+//                            zmvo.setFPrice("");
+                            zmvo.setPayMoney(vo.getPayMoney());
+                            zmvo.setPayPostage(vo.getTotalPostage());
+                            zmvo.setCateName(vo.getCateName());
+                            zmvo.setRealName(vo.getUserName());
+                            zmvo.setUserPhone(vo.getUserPhone());
+                            zmvo.setUserAddress(vo.getUserAddress());
+                            zmvo.setCreateTime(vo.getCreateTime());
+                            zmvo.setPayTime(vo.getOrderPayTime());
+                            zmvo.setDeliverySn(vo.getOrderDeliverySn());
+                            zmvo.setDeliveryName(vo.getOrderDeliveryName());
+                            zmvo.setDeliveryId(vo.getOrderDeliveryId());
+                            zmvo.setCompanyName(vo.getCompanyName());
+                            zmvo.setCompanyUserNickName(vo.getCompanyUserNickName());
+                            zmvo.setRefundTime(vo.getCreateTime());
+//                            zmvo.setAfterSalesNumber
+                            zmvo.setRefundMoney(vo.getRefundAmount());
+                            zmvo.setBankTransactionId(vo.getBankTransactionId());
+                        } catch (Exception e) {
+                            // 处理异常
+                            e.printStackTrace();
+                        }
+                        return zmvo;
+                    })
+                    .collect(Collectors.toList());
+            for (FsStoreOrderItemExportRefundZMVO vo : zmvoList){
+                vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
+            }
+            ExcelUtil<FsStoreOrderItemExportRefundZMVO> util = new ExcelUtil<FsStoreOrderItemExportRefundZMVO>(FsStoreOrderItemExportRefundZMVO.class);
+            return util.exportExcel(zmvoList, "退款订单导出");
+        }
         for (LiveAfterSalesVo liveAfterSalesVo : list) {
             liveAfterSalesVo.setUserPhone(liveAfterSalesVo.getUserPhone() == null ? "" : liveAfterSalesVo.getUserPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
             liveAfterSalesVo.setPhoneNumber(liveAfterSalesVo.getPhoneNumber() == null ? "" : liveAfterSalesVo.getPhoneNumber().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
@@ -117,7 +168,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 新增售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:add')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:add')")
     @Log(title = "售后记录", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody LiveAfterSales liveAfterSales)
@@ -128,7 +179,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 修改售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:edit')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:edit')")
     @Log(title = "售后记录", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody LiveAfterSales liveAfterSales)
@@ -149,7 +200,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 删除售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:remove')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:remove')")
     @Log(title = "售后记录", businessType = BusinessType.DELETE)
 	@DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)

+ 19 - 2
fs-admin/src/main/java/com/fs/live/controller/LiveController.java

@@ -4,14 +4,19 @@ 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.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.framework.web.service.TokenService;
 import com.fs.hisStore.task.LiveTask;
 import com.fs.hisStore.task.MallStoreTask;
 import com.fs.live.domain.Live;
 import com.fs.live.service.ILiveService;
 import com.fs.live.vo.LiveListVo;
+import com.hc.openapi.tool.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -28,10 +33,14 @@ import java.util.Map;
  */
 @RestController
 @RequestMapping("/live/live")
+@Slf4j
 public class LiveController extends BaseController {
     @Autowired
     private ILiveService liveService;
 
+    @Autowired
+    private TokenService tokenService;
+
 
     /**
      * 查询直播列表
@@ -63,7 +72,7 @@ public class LiveController extends BaseController {
     @PreAuthorize("@ss.hasPermi('live:live:query')")
     @GetMapping(value = "/{liveId}")
     public AjaxResult getInfo(@PathVariable("liveId") Long liveId) {
-        return AjaxResult.success(liveService.selectLiveByLiveId(liveId));
+        return AjaxResult.success(liveService.selectLiveDbByLiveId(liveId));
     }
 
     /**
@@ -83,6 +92,8 @@ public class LiveController extends BaseController {
     @Log(title = "直播", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody Live live) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        log.warn("loginUser:{},update:{}", loginUser.getUserId(), JSON.toJSONString( live));
         return toAjax(liveService.updateLive(live));
     }
 
@@ -93,7 +104,9 @@ public class LiveController extends BaseController {
     @Log(title = "直播", businessType = BusinessType.DELETE)
     @DeleteMapping("/{liveIds}")
     public AjaxResult remove(@PathVariable Long[] liveIds) {
-        return toAjax(liveService.deleteLiveByLiveIds(liveIds));
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        log.warn("loginUser:{},update:{}", loginUser.getUserId(), JSON.toJSONString( liveIds));
+        return toAjax(liveService.deleteLiveByLiveIds(liveIds, new Live()));
     }
 
     @PreAuthorize("@ss.hasPermi('live:live:query')")
@@ -116,6 +129,8 @@ public class LiveController extends BaseController {
     @PreAuthorize("@ss.hasPermi('live:live:edit')")
     @PostMapping("/handleShelfOrUn")
     public R handleShelfOrUn(@RequestBody LiveListVo listVo) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        log.warn("loginUser:{},update:{}", loginUser.getUserId(), JSON.toJSONString( listVo));
         return liveService.handleShelfOrUnAdmin(listVo);
     }
 
@@ -125,6 +140,8 @@ public class LiveController extends BaseController {
     @PreAuthorize("@ss.hasPermi('live:live:edit')")
     @PostMapping("/handleDeleteSelected")
     public R handleDeleteSelected(@RequestBody LiveListVo listVo) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        log.warn("loginUser:{},update:{}", loginUser.getUserId(), JSON.toJSONString( listVo));
         return liveService.handleDeleteSelectedAdmin(listVo);
     }
     /**

+ 66 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveDataController.java

@@ -1,13 +1,18 @@
 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.domain.R;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.SecurityUtils;
+import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.live.domain.LiveData;
 import com.fs.live.param.LiveDataParam;
 import com.fs.live.service.ILiveDataService;
 import com.fs.live.vo.LiveUserFirstVo;
+import com.fs.live.vo.LiveUserDetailExportVO;
 import com.github.pagehelper.PageHelper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -109,5 +114,66 @@ public class LiveDataController extends BaseController {
         return R.ok(liveViewData);
     }
 
+    /**
+     * 查询直播间详情数据(SQL方式)
+     * @param liveId 直播间ID
+     * @return 详情数据
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
+    @GetMapping("/getLiveDataDetailBySql")
+    public R getLiveDataDetailBySql(@RequestParam Long liveId) {
+        return liveDataService.getLiveDataDetailBySql(liveId);
+    }
+
+    /**
+     * 查询直播间用户详情列表(SQL方式)
+     * @param liveId 直播间ID
+     * @return 用户详情列表
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
+    @GetMapping("/getLiveUserDetailListBySql")
+    public R getLiveUserDetailListBySql(@RequestParam Long liveId) {
+        return liveDataService.getLiveUserDetailListBySql(liveId);
+    }
+
+    /**
+     * 查询直播间详情数据(查询数据服务器处理方式)
+     * @param liveId 直播间ID
+     * @return 详情数据
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
+    @GetMapping("/getLiveDataDetailByServer")
+    public R getLiveDataDetailByServer(@RequestParam Long liveId) {
+        return liveDataService.getLiveDataDetailByServer(liveId);
+    }
+
+    /**
+     * 查询直播间用户详情列表(查询数据服务器处理方式)
+     * @param liveId 直播间ID
+     * @return 用户详情列表
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:query')")
+    @GetMapping("/getLiveUserDetailListByServer")
+    public R getLiveUserDetailListByServer(@RequestParam Long liveId) {
+        return liveDataService.getLiveUserDetailListByServer(liveId);
+    }
+
+    /**
+     * 导出直播间用户详情数据
+     * @param liveId 直播间ID
+     * @return Excel文件
+     */
+    @PreAuthorize("@ss.hasPermi('liveData:liveData:export')")
+    @Log(title = "直播间用户详情", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportLiveUserDetail")
+    public AjaxResult exportLiveUserDetail(@RequestParam Long liveId) {
+        List<LiveUserDetailExportVO> list = liveDataService.exportLiveUserDetail(liveId);
+        if (list == null || list.isEmpty()) {
+            return AjaxResult.error("未找到用户详情数据");
+        }
+        
+        ExcelUtil<LiveUserDetailExportVO> util = new ExcelUtil<>(LiveUserDetailExportVO.class);
+        return util.exportExcel(list, "直播间用户详情数据");
+    }
 
 }

+ 29 - 1
fs-admin/src/main/java/com/fs/live/controller/LiveOrderController.java

@@ -38,6 +38,7 @@ import com.fs.hisStore.param.*;
 import com.fs.hisStore.service.IFsExpressScrmService;
 import com.fs.hisStore.task.ExpressTask;
 import com.fs.hisStore.task.LiveTask;
+import com.fs.hisStore.vo.FsStoreOrderItemExportZMVO;
 import com.fs.hisStore.vo.FsStoreOrderVO;
 import com.fs.live.domain.*;
 import com.fs.live.dto.LiveOrderCustomerExportDTO;
@@ -242,7 +243,34 @@ public class LiveOrderController extends BaseController
         return util.exportExcel(list, "订单数据");
     }
 
-
+    /**
+     * 导出订单列表
+     */
+    @PreAuthorize("@ss.hasPermi('live:liveOrder:export')")
+    @Log(title = "订单", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportZmNew")
+    public AjaxResult exportZmNew(LiveOrder liveOrder){
+        List<FsStoreOrderItemExportZMVO> list = liveOrderService.selectLiveOrderListZmNew(liveOrder);
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        for (FsStoreOrderItemExportZMVO vo : list){
+            vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
+//            vo.setCompanyUserPhone(ParseUtils.parsePhone(vo.getCompanyUserPhone()));
+//            vo.setUserBindPhone(ParseUtils.parsePhone(vo.getUserBindPhone()));
+            vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
+            // 财务独特字段
+            if (loginUser.getPermissions().contains("live:liveOrder:finance") || loginUser.getPermissions().contains("*:*:*")) {
+//                vo.setCostPrice(vo.getCost());
+                vo.setFPrice(vo.getCost().multiply(BigDecimal.valueOf(vo.getNum())));
+            } else {
+                vo.setCost(BigDecimal.ZERO);
+                vo.setFPrice(BigDecimal.ZERO);
+                vo.setBankTransactionId("");
+            }
+//            vo.setCost(vo.getCostPrice());
+        }
+        ExcelUtil<FsStoreOrderItemExportZMVO> util = new ExcelUtil<FsStoreOrderItemExportZMVO>(FsStoreOrderItemExportZMVO.class);
+        return util.exportExcel(list, "订单数据");
+    }
     /**
      * 获取订单详细信息
      */

+ 107 - 24
fs-admin/src/main/java/com/fs/qw/qwTask/qwTask.java

@@ -4,6 +4,7 @@ import com.fs.course.service.IFsUserCourseService;
 import com.fs.qw.domain.QwIpadServerLog;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.param.QwMandatoryRegistrParam;
 import com.fs.qw.service.*;
 import com.fs.sop.service.impl.QwSopLogsServiceImpl;
 import com.fs.sop.service.impl.QwSopServiceImpl;
@@ -15,10 +16,14 @@ import com.fs.wxwork.service.WxWorkService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.time.Duration;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.Date;
 import java.util.List;
+import java.util.Optional;
 
 @Component("qwTask")
 public class qwTask {
@@ -73,6 +78,10 @@ public class qwTask {
     private WxWorkService wxWorkService;
 
 
+    @Autowired
+    private IQwCompanyService iQwCompanyService;
+
+
     //正在使用
     public void qwExternalContact()
     {
@@ -240,37 +249,41 @@ public class qwTask {
      * 定时清除 占着茅坑不拉屎的ipad 账号
      */
     public void selectQwUserByUnbindIpad(){
+
+        //查询所有状态为 绑定了AI主机的
         List<QwUser> list = qwUserMapper.selectQwUserByTest();
         for (QwUser qwUser : list) {
             try {
-                Integer serverStatus = qwUser.getServerStatus();
+
                 Long serverId = qwUser.getServerId();
-                if (serverStatus==0){
-                    System.out.println("不需要解绑");
-                }
+
                 if (serverId==null){
                     System.out.println("serverId不存在");
+                }else {
+                    //没绑定销售 或者 已经离职
+                    if (qwUser.getStatus()==0 || qwUser.getIsDel()==2){
+
+                        updateIpadStatus(qwUser,serverId);
+                    }
+
+                    //绑定了销售-也绑定了ipad,但是长时间离线的(离线状态,无操作超过2天的,也自动解绑)
+                    if(qwUser.getCreateTime()!=null){
+                        Date createTime = qwUser.getCreateTime();
+                        Integer serverStatus = qwUser.getServerStatus();
+                        Integer ipadStatus = qwUser.getIpadStatus();
+
+                        boolean result = isCreateTimeMoreThanDaysWithOptional(createTime, 2);
+                        //大于2天 ,绑定了ipad,离线
+                        if(result && serverStatus==1 && ipadStatus==0){
+                            updateIpadStatus(qwUser,serverId);
+
+                        }
+                    }
+
+
                 }
-                QwUser u = new QwUser();
-                u.setId(qwUser.getId());
-                u.setServerId(null);
-                u.setServerStatus(0);
-                qwUserMapper.updateQwUser(u);
-                ipadServerService.addServer(serverId);
-                QwIpadServerLog qwIpadServerLog = new QwIpadServerLog();
-                qwIpadServerLog.setType(2);
-                qwIpadServerLog.setTilie("解绑");
-                qwIpadServerLog.setServerId(serverId);
-                qwIpadServerLog.setQwUserId(qwUser.getId());
-                qwIpadServerLog.setCompanyUserId(qwUser.getCompanyUserId());
-                qwIpadServerLog.setCompanyId(qwUser.getCompanyId());
-                qwIpadServerLog.setCreateTime(new Date());
-                qwIpadServerLogService.insertQwIpadServerLog(qwIpadServerLog);
-                qwIpadServerUserService.deleteQwIpadServerUserByQwUserId(qwUser.getId());
-                WxWorkGetQrCodeDTO wxWorkGetQrCodeDTO = new WxWorkGetQrCodeDTO();
-                wxWorkGetQrCodeDTO.setUuid(qwUser.getUid());
-                wxWorkService.LoginOut(wxWorkGetQrCodeDTO,qwUser.getServerId());
-                updateIpadStatus(qwUser.getId(),0);
+
+
             } catch (Exception e) {
                 System.out.println("解绑ipad报错"+e);
 
@@ -278,6 +291,41 @@ public class qwTask {
         }
     }
 
+    public void updateIpadStatus(QwUser qwUser,Long serverId){
+        QwUser u = new QwUser();
+        u.setId(qwUser.getId());
+        u.setServerId(null);
+        u.setServerStatus(0);
+        qwUserMapper.updateQwUser(u);
+        ipadServerService.addServer(serverId);
+        QwIpadServerLog qwIpadServerLog = new QwIpadServerLog();
+        qwIpadServerLog.setType(2);
+        qwIpadServerLog.setTilie("解绑");
+        qwIpadServerLog.setServerId(serverId);
+        qwIpadServerLog.setQwUserId(qwUser.getId());
+        qwIpadServerLog.setCompanyUserId(qwUser.getCompanyUserId());
+        qwIpadServerLog.setCompanyId(qwUser.getCompanyId());
+        qwIpadServerLog.setCreateTime(new Date());
+        qwIpadServerLogService.insertQwIpadServerLog(qwIpadServerLog);
+        qwIpadServerUserService.deleteQwIpadServerUserByQwUserId(qwUser.getId());
+        WxWorkGetQrCodeDTO wxWorkGetQrCodeDTO = new WxWorkGetQrCodeDTO();
+        wxWorkGetQrCodeDTO.setUuid(qwUser.getUid());
+        wxWorkService.LoginOut(wxWorkGetQrCodeDTO,qwUser.getServerId());
+        updateIpadStatus(qwUser.getId(),0);
+    }
+
+    public static boolean isCreateTimeMoreThanDaysWithOptional(Date createTime, int days) {
+        return Optional.ofNullable(createTime)
+                .map(time -> {
+                    LocalDateTime createDateTime = time.toInstant()
+                            .atZone(ZoneId.systemDefault())
+                            .toLocalDateTime();
+                    LocalDateTime now = LocalDateTime.now();
+                    Duration duration = Duration.between(createDateTime, now);
+                    return duration.toDays() > days;
+                })
+                .orElse(false); // 为null时返回false,可根据需求调整
+    }
 
     void updateIpadStatus(Long id ,Integer status){
         QwUser u = new QwUser();
@@ -286,4 +334,39 @@ public class qwTask {
         qwUserMapper.updateQwUser(u);
     }
 
+
+    /**
+     * 强制注册-2025-11-12 之后的 更新
+     */
+    public void QwExternalContactMandatoryRegistration(){
+        try {
+//            List<String> longs = qwExternalContactService.selectQwExternalContactMandatoryRegistration();
+            List<String> longs = iQwCompanyService.selectQwCompanyListFormCorpId();
+            longs.forEach(item->{
+                List<QwMandatoryRegistrParam> registrParamList = qwExternalContactService.selectQwExternalContactMandatoryRegistrationByIds(String.valueOf(item));
+
+                batchUpdateQwExternalContactMandatoryRegistration(registrParamList);
+
+            });
+        }catch (Exception e){
+
+        }
+
+    }
+
+
+    private void batchUpdateQwExternalContactMandatoryRegistration(List<QwMandatoryRegistrParam> registrParamList) {
+        // 定义批量插入的大小
+        int batchSize = 300;
+
+        // 循环处理外部用户 ID,每次处理批量大小的子集
+        for (int i = 0; i < registrParamList.size(); i += batchSize) {
+
+            int endIndex = Math.min(i + batchSize, registrParamList.size());
+            List<QwMandatoryRegistrParam> batchList = registrParamList.subList(i, endIndex);  // 获取当前批次的子集
+
+            qwExternalContactService.batchUpdateQwExternalContactMandatoryRegistration(batchList);
+
+        }
+    }
 }

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

@@ -4,7 +4,7 @@ server:
 # Spring配置
 spring:
   profiles:
-    active: druid-bjzm-test
+    active: druid-ylrz
 #    active: druid-hdt
 #    active: druid-yzt
 #    active: druid-sxjz-test

+ 6 - 0
fs-common-api/Dockerfile

@@ -0,0 +1,6 @@
+FROM openjdk:8-jre
+# java版本,最好使用openjdk,而不是类似于Java:1.8
+COPY ./target/fs-common-api.jar fs-common-api.jar
+# 向外暴露的接口,最好与项目yml文件中的端口一致
+ENTRYPOINT ["java","-jar","fs-common-api.jar"]
+# 执行启动命令java -jar

+ 24 - 0
fs-common-api/pom.xml

@@ -118,6 +118,30 @@
                     <warName>${project.artifactId}</warName>
                 </configuration>
             </plugin>
+<!--            <plugin>-->
+<!--                <groupId>com.spotify</groupId>-->
+<!--                <artifactId>dockerfile-maven-plugin</artifactId>-->
+<!--                <version>1.4.13</version>-->
+<!--                <executions>-->
+<!--                    <execution>-->
+<!--                        <id>harbor</id>-->
+<!--                        <goals>-->
+<!--                            <goal>build</goal>-->
+<!--                            <goal>push</goal>-->
+<!--                        </goals>-->
+<!--                    </execution>-->
+<!--                </executions>-->
+<!--                <configuration>-->
+<!--                    <repository>ylrz-docker.tencentcloudcr.com/ylrz/sub1/repo</repository>-->
+<!--                    <tag>${project.version}</tag>-->
+<!--                    <useMavenSettingsForAuth>true</useMavenSettingsForAuth>-->
+<!--                    <username>100044034444</username>-->
+<!--                    <password>eyJhbGciOiJSUzI1NiIsImtpZCI6Ilg0R1k6SDU1TjpCQU9SOk5GVk86RkdSWDpYTjM1OjZGQUM6S1BBWjo0TE4yOkhDSUQ6UTJDRzpSN1NHIn0.eyJvd25lclVpbiI6IjEwMDAzNDY4ODY0OCIsIm9wZXJhdG9yVWluIjoiMTAwMDQ0MDM0NDQ0IiwidG9rZW5JZCI6ImQ0ZnZja2o4a2NnbnFjaTVvanQwIiwiZXhwIjoyMDc5MDYyMzU0LCJuYmYiOjE3NjM3MDIzNTQsImlhdCI6MTc2MzcwMjM1NH0.PhNxx6pBQ-ItrNlSs_gojvXeHghhYqDqxh8nLUIuhBeRzAgmVnY8F3bFPVgbHGydQNxvgyqLYv3nRIE1j020LGgzUetF5b-NBqSWYMiXfu6uZNWctRkwm5hdlWBrMlV8k8zGxY4ZDGUNEG0ksrk7kk3UZ-lHj4ButI2GIEhTx0lQEPHjhEY0xuteocJVYMHdVUqF-Bc5Jr0nvbwxUbmCGakN1VszxBoMpI-zA2O8anMvYq8h7EqOJLU4dlBVcsbkz-4sMi97Xev-mcGh7THbEGWoRWGWNSa4QwsdXEXS5-mhfrvOw6FGuuiIeEQvcuR8zDztDzSApl4ko57Yat-AQQ</password>-->
+<!--                    <buildArgs>-->
+<!--                        <JAR_FILE>${project.artifactId}.jar</JAR_FILE>-->
+<!--                    </buildArgs>-->
+<!--                </configuration>-->
+<!--            </plugin>-->
         </plugins>
         <finalName>${project.artifactId}</finalName>
     </build>

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

@@ -5,4 +5,4 @@ server:
 spring:
   profiles:
 #    active: dev
-    active: druid-myhk-test
+    active: druid-ylrz

+ 6 - 0
fs-common/src/main/java/com/fs/common/constant/LiveKeysConstant.java

@@ -30,5 +30,11 @@ public class LiveKeysConstant {
     public static final String LIVE_FLAG_CACHE = "live:flag:%s"; //直播间直播/回放状态缓存
     public static final Integer LIVE_FLAG_CACHE_EXPIRE = 300; //直播间状态缓存过期时间(秒)
 
+    public static final String LIVE_DATA_CACHE = "live:data:%s"; //直播间数据缓存
+    public static final Integer LIVE_DATA_CACHE_EXPIRE = 300; //直播间数据缓存过期时间(秒)
+
+    public static final String PRODUCT_DETAIL_CACHE = "product:detail:%s"; //商品详情缓存
+    public static final Integer PRODUCT_DETAIL_CACHE_EXPIRE = 300; //商品详情缓存过期时间(秒)
+
 
 }

+ 6 - 0
fs-company/Dockerfile

@@ -0,0 +1,6 @@
+FROM openjdk:8-jre
+# java版本,最好使用openjdk,而不是类似于Java:1.8
+COPY ./target/fs-company.jar fs-company.jar
+# 向外暴露的接口,最好与项目yml文件中的端口一致
+ENTRYPOINT ["java","-jar","fs-company.jar"]
+# 执行启动命令java -jar

+ 6 - 6
fs-company/src/main/java/com/fs/company/controller/live/LiveAfterSalesController.java

@@ -52,7 +52,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 查询售后记录列表
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:list')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:list')")
     @GetMapping("/list")
     public TableDataInfo list(LiveAfterSalesVo liveAfterSales)
     {
@@ -69,7 +69,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 导出售后记录列表
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:export')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:export')")
     @Log(title = "售后记录", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
     public AjaxResult export(LiveAfterSalesVo liveAfterSales)
@@ -89,7 +89,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 获取售后记录详细信息
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:query')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:query')")
     @GetMapping(value = "/{id}")
     public R getInfo(@PathVariable("id") Long id)
     {
@@ -107,7 +107,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 新增售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:add')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:add')")
     @Log(title = "售后记录", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody LiveAfterSales liveAfterSales)
@@ -118,7 +118,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 修改售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:edit')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:edit')")
     @Log(title = "售后记录", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody LiveAfterSales liveAfterSales)
@@ -129,7 +129,7 @@ public class LiveAfterSalesController extends BaseController
     /**
      * 删除售后记录
      */
-    @PreAuthorize("@ss.hasPermi('live:liveAfteraSales:remove')")
+    @PreAuthorize("@ss.hasPermi('live:liveAfterSales:remove')")
     @Log(title = "售后记录", businessType = BusinessType.DELETE)
 	@DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)

+ 32 - 14
fs-company/src/main/java/com/fs/company/controller/live/LiveController.java

@@ -134,7 +134,8 @@ public class LiveController extends BaseController
     {
         // 设置企业ID和企业用户ID
         setCompanyId(live);
-        return toAjax(liveService.insertLive(live));
+        return toAjax(1);
+//        return toAjax(liveService.insertLive(live));
     }
 
     /**
@@ -146,7 +147,8 @@ public class LiveController extends BaseController
         CompanyUser user = SecurityUtils.getLoginUser().getUser();
         live.setCompanyUserId(user.getUserId());
         live.setCompanyId(user.getCompanyId());
-        return liveService.finishLive(live);
+        return R.ok();
+//        return liveService.finishLive(live);
     }
 
     /**
@@ -158,7 +160,8 @@ public class LiveController extends BaseController
         CompanyUser user = SecurityUtils.getLoginUser().getUser();
         live.setCompanyUserId(user.getUserId());
         live.setCompanyId(user.getCompanyId());
-        return liveService.copyLive(live);
+        return R.ok();
+//        return liveService.copyLive(live);
     }
 
     /**
@@ -170,7 +173,8 @@ public class LiveController extends BaseController
         CompanyUser user = SecurityUtils.getLoginUser().getUser();
         live.setCompanyUserId(user.getUserId());
         live.setCompanyId(user.getCompanyId());
-        return liveService.startLive(live);
+        return R.ok();
+//        return liveService.startLive(live);
     }
 
     /**
@@ -181,7 +185,12 @@ public class LiveController extends BaseController
     @PutMapping
     public AjaxResult edit(@RequestBody Live live)
     {
-        return toAjax(liveService.updateLive(live));
+        return AjaxResult.success();
+//        CompanyUser user = SecurityUtils.getLoginUser().getUser();
+//        live.setCompanyUserId(user.getUserId());
+//        live.setCompanyId(user.getCompanyId());
+//
+//        return toAjax(liveService.updateLive(live));
     }
 
     /**
@@ -192,7 +201,12 @@ public class LiveController extends BaseController
 	@DeleteMapping("/{liveIds}")
     public AjaxResult remove(@PathVariable Long[] liveIds)
     {
-        return toAjax(liveService.deleteLiveByLiveIds(liveIds));
+        return AjaxResult.success();
+//        Live live = new Live();
+//        CompanyUser user = SecurityUtils.getLoginUser().getUser();
+//        live.setCompanyUserId(user.getUserId());
+//        live.setCompanyId(user.getCompanyId());
+//        return toAjax(liveService.deleteLiveByLiveIds(liveIds, live));
     }
 
     @PreAuthorize("@ss.hasPermi('live:live:query')")
@@ -210,9 +224,10 @@ public class LiveController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:live:edit')")
     @PostMapping("/closeLiving")
     public R closeLiving(@RequestBody Map<String, String> payload) {
-        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        payload.put("userId", loginUser.getUser().getUserId().toString());
-        return liveService.closeLiving(payload);
+        return R.ok();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        payload.put("userId", loginUser.getUser().getUserId().toString());
+//        return liveService.closeLiving(payload);
     }
 
     @PreAuthorize("@ss.hasPermi('live:live:insert')")
@@ -240,7 +255,8 @@ public class LiveController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:live:edit')")
     @PostMapping("/startLoopPlay")
     public R startLoopPlay(@RequestBody Live live) {
-        return liveService.startLoopPlay(live);
+        return R.ok();
+//        return liveService.startLoopPlay(live);
     }
 
     /**
@@ -258,8 +274,9 @@ public class LiveController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:live:edit')")
     @PostMapping("/handleShelfOrUn")
     public R handleShelfOrUn(@RequestBody LiveListVo listVo) {
-        setListCompanyId(listVo);
-        return liveService.handleShelfOrUn(listVo);
+        return R.ok();
+//        setListCompanyId(listVo);
+//        return liveService.handleShelfOrUn(listVo);
     }
 
     /**
@@ -268,8 +285,9 @@ public class LiveController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:live:edit')")
     @PostMapping("/handleDeleteSelected")
     public R handleDeleteSelected(@RequestBody LiveListVo listVo) {
-        setListCompanyId(listVo);
-        return liveService.handleDeleteSelected(listVo);
+        return R.ok();
+//        setListCompanyId(listVo);
+//        return liveService.handleDeleteSelected(listVo);
     }
 
 

+ 8 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java

@@ -113,6 +113,10 @@ public class QwExternalContactController extends BaseController
 
         startPage();
         qwExternalContact.setCompanyId(loginUser.getCompany().getCompanyId());
+        if(StringUtils.isNotEmpty(qwExternalContact.getStatuses())){
+            String[] split = qwExternalContact.getStatuses().split(",");
+            qwExternalContact.setStatusCondition(split);
+        }
         List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
         list.forEach(item->{
 
@@ -343,6 +347,10 @@ public class QwExternalContactController extends BaseController
         List<QwContactWay> wayList = qwContactWayService.selectQwContactWayList(qwContactWay);
 
         qwExternalContact.setCompanyId(loginUser.getCompany().getCompanyId());
+        if(StringUtils.isNotEmpty(qwExternalContact.getStatuses())){
+            String[] split = qwExternalContact.getStatuses().split(",");
+            qwExternalContact.setStatusCondition(split);
+        }
         List<QwExternalContactVO> list = qwExternalContactService.selectQwExternalContactListVO(qwExternalContact);
         list.forEach(item->{
 

+ 3 - 3
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactTransferLogController.java

@@ -104,10 +104,10 @@ public class QwExternalContactTransferLogController extends BaseController
     @PreAuthorize("@ss.hasPermi('qw:externalContactTransferLog:export')")
     @Log(title = "转接记录", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
-    public AjaxResult export(QwExternalContactTransferLog qwExternalContactTransferLog)
+    public AjaxResult export(QwExternalContactTransferLogParam qwExternalContactTransferLog)
     {
-        List<QwExternalContactTransferLog> list = qwExternalContactTransferLogService.selectQwExternalContactTransferLogList(qwExternalContactTransferLog);
-        ExcelUtil<QwExternalContactTransferLog> util = new ExcelUtil<QwExternalContactTransferLog>(QwExternalContactTransferLog.class);
+        List<QwExternalContactTransferLogListVO> list = qwExternalContactTransferLogService.selectQwExternalContactTransferLogListVO(qwExternalContactTransferLog);
+        ExcelUtil<QwExternalContactTransferLogListVO> util = new ExcelUtil<QwExternalContactTransferLogListVO>(QwExternalContactTransferLogListVO.class);
         return util.exportExcel(list, "转接记录数据");
     }
 

+ 8 - 8
fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java

@@ -924,14 +924,14 @@ public class QwUserController extends BaseController
         return R.ok();
     }
 
-    /**
-     * 重启云主机
-     * @return
-     */
-    @PutMapping("/restartHost")
-    public R restartCloudHost(@RequestParam String serverIp) {
-        return qwUserService.restartCloudHost(serverIp);
-    }
+//    /**
+//     * 重启云主机
+//     * @return
+//     */
+//    @PutMapping("/restartHost")
+//    public R restartCloudHost(@RequestParam String serverIp) {
+//        return qwUserService.restartCloudHost(serverIp);
+//    }
     @PostMapping("/updateSendType")
     public R updateSendType(@RequestBody UpdateSendTypeVo vo) {
         return qwUserService.updateSendType(vo);

+ 3 - 2
fs-company/src/main/resources/application.yml

@@ -1,13 +1,14 @@
 server:
-  port: 8006
+  port: 7773
 # Spring配置
 spring:
   profiles:
+    active: druid-ylrz
 #    active: druid-jnsyj-test
 #    active: druid-jnmy-test
 #    active: druid-jzzx-test
 #    active: druid-hdt
-    active: druid-bjzm-test
+#    active: druid-bjzm-test
 #    active: druid-yzt
 #    active: druid-myhk
 #    active: druid-sft

+ 1 - 1
fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java

@@ -60,7 +60,7 @@ public class IpadSendServer {
     private final ICompanyMiniappService companyMiniappService;
     private final IFsCoursePlaySourceConfigService playSourceConfigService;
     private final FsUserMapper fsUserMapper;
-    private static final List<String> PROJECT_NAMES = Arrays.asList("济南联志健康", "北京存在文化");
+    private static final List<String> PROJECT_NAMES = Arrays.asList("济南联志健康", "北京存在文化","宽益堂");
     private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap, Long companyId) {
         // 发送参数原本的appid
         String appid = content.getMiniprogramAppid();

+ 0 - 1
fs-live-app/src/main/java/com/fs/framework/aspectj/LiveWatchUserAspect.java

@@ -33,7 +33,6 @@ public class LiveWatchUserAspect {
         try {
             String methodName = joinPoint.getSignature().getName();
             Object[] args = joinPoint.getArgs();
-            log.info("直播观看用户数据发生变化,方法: {}, 参数: {}", methodName, Arrays.toString(args));
             // 提取liveId并处理缓存更新
             Set<Long> liveIds = extractLiveIds(methodName, args);
             for (Long liveId : liveIds) {

+ 1 - 1
fs-live-app/src/main/java/com/fs/framework/aspectj/RateLimiterAspect.java

@@ -73,7 +73,7 @@ public class RateLimiterAspect
             {
                 throw new ServiceException("访问过于频繁,请稍后再试");
             }
-            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key);
+
         }
         catch (ServiceException e)
         {

+ 0 - 2
fs-live-app/src/main/java/com/fs/live/controller/LiveDataController.java

@@ -15,7 +15,6 @@ public class LiveDataController extends BaseController {
 
     @Autowired
     private RedisCache redisCache;
-
     /**
      * 点赞
      * */
@@ -23,7 +22,6 @@ public class LiveDataController extends BaseController {
     public R like(@PathVariable("liveId") Long liveId) {
         //直播间总点赞数
         Long increment = redisCache.incr("live:like:" + liveId, 1);
-
         return R.ok().put("like",increment);
     }
 }

+ 12 - 11
fs-live-app/src/main/java/com/fs/live/websocket/handle/LiveChatHandler.java

@@ -3,6 +3,8 @@ package com.fs.live.websocket.handle;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
+import com.fs.hisStore.domain.FsUserScrm;
+import com.fs.hisStore.service.IFsUserScrmService;
 import com.fs.live.websocket.bean.SendMsgVo;
 import com.fs.live.websocket.constant.AttrConstant;
 import com.fs.common.core.domain.R;
@@ -41,7 +43,7 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
     private final static ILiveService liveService = SpringUtils.getBean(ILiveService.class);
     private final static ILiveWatchUserService liveWatchUserService = SpringUtils.getBean(ILiveWatchUserService.class);
     private final static ILiveMsgService liveMsgService = SpringUtils.getBean(ILiveMsgService.class);
-    private final static IFsUserService fsUserService = SpringUtils.getBean(IFsUserService.class);
+    private final static IFsUserScrmService fsUserService = SpringUtils.getBean(IFsUserScrmService.class);
 
     /**
      * 处理握手
@@ -51,7 +53,7 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
      */
     @Override
     public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
-        log.debug("事件");
+
         // 处理 WebSocket 握手完成事件
         if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
             Long userId = ctx.channel().attr(AttrConstant.ATTR_USER_ID).get();
@@ -70,11 +72,12 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
             roomGroup.add(ctx.channel());
 
             if (userType == 0) {
+
+
+                FsUserScrm fsUser = fsUserService.selectFsUserByUserId(userId);
                 // 加入房间
-                LiveWatchUser liveWatchUser = liveWatchUserService.joinWithoutLocation(liveId, userId);
+                LiveWatchUser liveWatchUser = liveWatchUserService.joinWithoutLocation(fsUser,liveId, userId);
                 room.put(userId, ctx.channel());
-
-                FsUser fsUser = fsUserService.selectFsUserByUserId(userId);
                 if (Objects.isNull(fsUser)) {
                     ctx.channel().writeAndFlush(new TextWebSocketFrame("Error: 用户信息错误")).addListener(ChannelFutureListener.CLOSE);
                     return;
@@ -97,7 +100,6 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
                 adminRoom.add(ctx.channel());
             }
 
-            log.debug("加入webSocket liveId: {}, userId: {}, 直播间人数: {}", liveId, userId, room.size());
         }
     }
 
@@ -154,7 +156,7 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
      */
     @Override
     protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
-        log.debug("接收到消息 data: {}", textWebSocketFrame.text());
+
         Long liveId = channelHandlerContext.channel().attr(AttrConstant.ATTR_LIVE_ID).get();
         Long userType = channelHandlerContext.channel().attr(AttrConstant.ATTR_USER_TYPE).get();
 
@@ -204,7 +206,7 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
      */
     @Override
     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-        log.debug("断开连接");
+
         Long userId = ctx.channel().attr(AttrConstant.ATTR_USER_ID).get();
         Long liveId = ctx.channel().attr(AttrConstant.ATTR_LIVE_ID).get();
         Long userType = ctx.channel().attr(AttrConstant.ATTR_USER_TYPE).get();
@@ -218,8 +220,8 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
         ChannelGroup roomGroup = getRoomGroup(liveId);
 
         if (userType == 0) {
-            FsUser fsUser = fsUserService.selectFsUserByUserId(userId);
-            LiveWatchUser close = liveWatchUserService.close(liveId, userId);
+            FsUserScrm fsUser = fsUserService.selectFsUserByUserId(userId);
+            LiveWatchUser close = liveWatchUserService.close(fsUser,liveId, userId);
             room.remove(userId);
 
             if (room.isEmpty()) {
@@ -251,7 +253,6 @@ public class LiveChatHandler extends SimpleChannelInboundHandler<TextWebSocketFr
             roomGroups.remove(liveId);
         }
 
-        log.debug("断开webSocket liveId: {}, userId: {}, 直播间人数: {}", liveId, userId, room.size());
 
     }
 

+ 230 - 56
fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -22,6 +22,7 @@ import com.fs.live.service.*;
 import com.fs.live.vo.LiveGoodsVo;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
@@ -30,10 +31,9 @@ import javax.websocket.server.ServerEndpoint;
 import java.io.EOFException;
 import java.io.IOException;
 import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import static com.fs.common.constant.LiveKeysConstant.*;
 
@@ -49,6 +49,16 @@ public class WebSocketServer {
     private final static ConcurrentHashMap<Long, ConcurrentHashMap<Long, Session>> rooms = new ConcurrentHashMap<>();
     // 管理端连接
     private final static ConcurrentHashMap<Long, CopyOnWriteArrayList<Session>> adminRooms = new ConcurrentHashMap<>();
+
+    // Session发送锁,避免同一会话并发发送消息
+    private final static ConcurrentHashMap<String, Lock> sessionLocks = new ConcurrentHashMap<>();
+    // 心跳超时缓存:key=sessionId,value=最后心跳时间戳
+    private final static ConcurrentHashMap<String, Long> heartbeatCache = new ConcurrentHashMap<>();
+    // 心跳超时时间(毫秒):3分钟无心跳则认为超时
+    private final static long HEARTBEAT_TIMEOUT = 3 * 60 * 1000;
+    // admin房间消息发送线程池(单线程,保证串行化)
+    private final static ConcurrentHashMap<Long, ExecutorService> adminExecutors = new ConcurrentHashMap<>();
+
     private final RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
     private final ILiveMsgService liveMsgService = SpringUtils.getBean(ILiveMsgService.class);
     private final ILiveService liveService = SpringUtils.getBean(ILiveService.class);
@@ -62,6 +72,7 @@ public class WebSocketServer {
     private final ILiveUserFirstEntryService liveUserFirstEntryService =  SpringUtils.getBean(ILiveUserFirstEntryService.class);
     private final ILiveCouponIssueService liveCouponIssueService =  SpringUtils.getBean(ILiveCouponIssueService.class);
     private final LiveCouponMapper liveCouponMapper = SpringUtils.getBean(LiveCouponMapper.class);
+    private static Random random = new Random();
 
     // 直播间在线用户缓存
 //    private static final ConcurrentHashMap<Long, Integer> liveOnlineUsers = new ConcurrentHashMap<>();
@@ -101,7 +112,7 @@ public class WebSocketServer {
                 throw new BaseException("用户信息错误");
             }
 
-            LiveWatchUser liveWatchUserVO = liveWatchUserService.join(liveId, userId, location);
+            LiveWatchUser liveWatchUserVO = liveWatchUserService.join(fsUser,liveId, userId, location);
             room.put(userId, session);
             // 直播间浏览量 +1
             redisCache.incr(PAGE_VIEWS_KEY + liveId, 1);
@@ -135,17 +146,19 @@ public class WebSocketServer {
                 redisCache.incr(UNIQUE_VIEWERS_KEY + liveId, 1);
             }
             liveWatchUserVO.setMsgStatus(liveWatchUserVO.getMsgStatus());
-            SendMsgVo sendMsgVo = new SendMsgVo();
-            sendMsgVo.setLiveId(liveId);
-            sendMsgVo.setUserId(userId);
-            sendMsgVo.setUserType(userType);
-            sendMsgVo.setCmd("entry");
-            sendMsgVo.setMsg("用户进入");
-            sendMsgVo.setData(JSONObject.toJSONString(liveWatchUserVO));
-            sendMsgVo.setNickName(fsUser.getNickname());
-            sendMsgVo.setAvatar(fsUser.getAvatar());
-            // 广播连接消息
-            broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+            if (1 == random.nextInt(4)) {
+                SendMsgVo sendMsgVo = new SendMsgVo();
+                sendMsgVo.setLiveId(liveId);
+                sendMsgVo.setUserId(userId);
+                sendMsgVo.setUserType(userType);
+                sendMsgVo.setCmd("entry");
+                sendMsgVo.setMsg("用户进入");
+                sendMsgVo.setData(JSONObject.toJSONString(liveWatchUserVO));
+                sendMsgVo.setNickName(fsUser.getNickname());
+                sendMsgVo.setAvatar(fsUser.getAvatar());
+                // 广播连接消息
+                broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+            }
 
             LiveUserFirstEntry liveUserFirstEntry = liveUserFirstEntryService.selectEntityByLiveIdUserId(liveId, userId);
             if (liveUserFirstEntry != null) {
@@ -177,9 +190,15 @@ public class WebSocketServer {
 
         } else {
             adminRoom.add(session);
+            // 为admin房间创建单线程执行器,保证串行化发送
+            adminExecutors.computeIfAbsent(liveId, k -> Executors.newSingleThreadExecutor());
         }
 
-        log.debug("加入webSocket liveId: {}, userId: {}, 直播间人数: {}, 管理端人数: {}", liveId, userId, room.size(), adminRoom.size());
+        // 初始化Session锁
+        sessionLocks.putIfAbsent(session.getId(), new ReentrantLock());
+        // 初始化心跳时间
+        heartbeatCache.put(session.getId(), System.currentTimeMillis());
+
     }
 
     //关闭连接时调用
@@ -209,24 +228,38 @@ public class WebSocketServer {
             // 从在线用户Set中移除用户ID
             String onlineUsersSetKey = ONLINE_USERS_SET_KEY + liveId;
             redisCache.redisTemplate.opsForSet().remove(onlineUsersSetKey, String.valueOf(userId));
-            LiveWatchUser liveWatchUserVO = liveWatchUserService.close(liveId, userId);
-            SendMsgVo sendMsgVo = new SendMsgVo();
-            sendMsgVo.setLiveId(liveId);
-            sendMsgVo.setUserId(userId);
-            sendMsgVo.setUserType(userType);
-            sendMsgVo.setCmd("out");
-            sendMsgVo.setMsg("用户离开");
-            sendMsgVo.setData(JSONObject.toJSONString(liveWatchUserVO));
-            sendMsgVo.setNickName(fsUser.getNickname());
-            sendMsgVo.setAvatar(fsUser.getAvatar());
-
-            // 广播离开消息
-            broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+            LiveWatchUser liveWatchUserVO = liveWatchUserService.close(fsUser,liveId, userId);
+
+
+            // 广播离开消息 添加一个概率问题 摇塞子,1-4 当为1的时候广播消息
+            if (1 == new Random().nextInt(4)) {
+                SendMsgVo sendMsgVo = new SendMsgVo();
+                sendMsgVo.setLiveId(liveId);
+                sendMsgVo.setUserId(userId);
+                sendMsgVo.setUserType(userType);
+                sendMsgVo.setCmd("out");
+                sendMsgVo.setMsg("用户离开");
+                sendMsgVo.setData(JSONObject.toJSONString(liveWatchUserVO));
+                sendMsgVo.setNickName(fsUser.getNickname());
+                sendMsgVo.setAvatar(fsUser.getAvatar());
+                broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+            }
+
         } else {
             adminRoom.remove(session);
+            // 如果admin房间为空,关闭并清理执行器
+            if (adminRoom.isEmpty()) {
+                ExecutorService executor = adminExecutors.remove(liveId);
+                if (executor != null) {
+                    executor.shutdown();
+                }
+                adminRooms.remove(liveId);
+            }
         }
 
-        log.debug("离开webSocket liveId: {}, userId: {}, 直播间人数: {}, 管理端人数: {}", liveId, userId, room.size(), adminRoom.size());
+        // 清理Session相关资源
+        heartbeatCache.remove(session.getId());
+        sessionLocks.remove(session.getId());
     }
 
     //收到客户端信息
@@ -243,6 +276,8 @@ public class WebSocketServer {
         try {
             switch (msg.getCmd()) {
                 case "heartbeat":
+                    // 更新心跳时间
+                    heartbeatCache.put(session.getId(), System.currentTimeMillis());
                     sendMessage(session, JSONObject.toJSONString(R.ok().put("data", msg)));
                     break;
                 case "sendMsg":
@@ -436,7 +471,6 @@ public class WebSocketServer {
      * 处理红包变动消息
      */
     private void processRed(Long liveId, SendMsgVo msg) {
-        log.debug("redData: {}", msg);
         JSONObject jsonObject = JSON.parseObject(msg.getData());
         Integer status = jsonObject.getInteger("status");
         msg.setStatus( status);
@@ -452,7 +486,6 @@ public class WebSocketServer {
      * 处理抽奖变动消息
      */
     private void processLottery(Long liveId, SendMsgVo msg) {
-        log.debug("lotteryData: {}", msg);
         JSONObject jsonObject = JSON.parseObject(msg.getData());
         Integer status = jsonObject.getInteger("status");
         msg.setStatus( status);
@@ -471,12 +504,7 @@ public class WebSocketServer {
         try {
             this.onClose(session);
         } catch (Exception e) {
-            log.error("webSocket 错误 onError", e);
-        }
-        if (throwable instanceof EOFException) {
-            log.info("WebSocket连接被客户端正常关闭(EOF),sessionId: {}", session.getId());
-        } else {
-            log.error("WebSocket连接错误", throwable);
+            log.error("webSocket 错误处理失败", e);
         }
     }
 
@@ -498,12 +526,36 @@ public class WebSocketServer {
         return adminRooms.computeIfAbsent(liveId, k -> new CopyOnWriteArrayList<>());
     }
 
-    //发送消息
+    //发送消息(带锁机制,避免并发发送)
     public void sendMessage(Session session, String message) throws IOException {
-        session.getAsyncRemote().sendText(message);
+        if (session == null || !session.isOpen()) {
+            return;
+        }
+
+        // 获取Session锁
+        Lock lock = sessionLocks.get(session.getId());
+        if (lock == null) {
+            // 如果锁不存在,创建一个新锁
+            lock = sessionLocks.computeIfAbsent(session.getId(), k -> new ReentrantLock());
+        }
+
+        // 使用锁保证同一Session的消息串行发送
+        lock.lock();
+        try {
+            if (session.isOpen()) {
+                session.getAsyncRemote().sendText(message);
+            }
+        } finally {
+            lock.unlock();
+        }
     }
 
     public void sendIntegralMessage(Long liveId, Long userId,Long scoreAmount) {
+        ConcurrentHashMap<Long, Session> room = getRoom(liveId);
+        Session session = room.get(userId);
+        if (session == null || !session.isOpen()) {
+            return;
+        }
         SendMsgVo sendMsgVo = new SendMsgVo();
         sendMsgVo.setLiveId(liveId);
         sendMsgVo.setUserId(userId);
@@ -511,13 +563,19 @@ public class WebSocketServer {
         sendMsgVo.setCmd("Integral");
         sendMsgVo.setMsg("恭喜你成功获得观看奖励:" + scoreAmount + "芳华币");
         sendMsgVo.setData(String.valueOf(scoreAmount));
-        ConcurrentHashMap<Long, Session> room = getRoom(liveId);
-        Session session = room.get(userId);
+
         if(Objects.isNull( session)) return;
         session.getAsyncRemote().sendText(JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
     }
 
     private void sendBlockMessage(Long liveId, Long userId) {
+
+        ConcurrentHashMap<Long, Session> room = getRoom(liveId);
+        Session session = room.get(userId);
+        if (session == null || !session.isOpen()) {
+            return;
+        }
+
         SendMsgVo sendMsgVo = new SendMsgVo();
         sendMsgVo.setLiveId(liveId);
         sendMsgVo.setUserId(userId);
@@ -525,8 +583,7 @@ public class WebSocketServer {
         sendMsgVo.setCmd("blockUser");
         sendMsgVo.setMsg("账号已被停用");
         sendMsgVo.setData(null);
-        ConcurrentHashMap<Long, Session> room = getRoom(liveId);
-        Session session = room.get(userId);
+
         if(Objects.isNull( session)) return;
         session.getAsyncRemote().sendText(JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
     }
@@ -540,16 +597,33 @@ public class WebSocketServer {
         ConcurrentHashMap<Long, Session> room = getRoom(liveId);
         List<Session> adminRoom = getAdminRoom(liveId);
 
+        // 普通用户房间:并行发送
         room.forEach((k, v) -> {
             if (v.isOpen()) {
-                sendWithRetry(v,message,7);
+                sendWithRetry(v,message,1);
             }
         });
-        adminRoom.forEach(v -> {
-            if (v.isOpen()) {
-                sendWithRetry(v,message,7);
+
+        // admin房间:串行发送,使用单线程执行器
+        if (!adminRoom.isEmpty()) {
+            ExecutorService executor = adminExecutors.get(liveId);
+            if (executor != null && !executor.isShutdown()) {
+                executor.submit(() -> {
+                    for (Session session : adminRoom) {
+                        if (session.isOpen()) {
+                            sendWithRetry(session, message, 1);
+                        }
+                    }
+                });
+            } else {
+                // 如果执行器不存在或已关闭,直接发送
+                adminRoom.forEach(v -> {
+                    if (v.isOpen()) {
+                        sendWithRetry(v, message, 1);
+                    }
+                });
             }
-        });
+        }
     }
 
     public void removeLikeCountCache(Long liveId) {
@@ -569,7 +643,6 @@ public class WebSocketServer {
                 String valueStr = cacheObject.toString().trim();
                 current = Integer.parseInt(valueStr);
             } catch (NumberFormatException e) {
-                log.error("点赞数格式错误,liveId: {}, value: {}", liveId, cacheObject, e);
                 continue;
             }
             Integer last = lastLikeCountCache.getOrDefault(liveId, 0);
@@ -585,6 +658,98 @@ public class WebSocketServer {
         lastLikeCountCache.keySet().removeIf(liveId -> !activeLiveIds.contains(liveId));
     }
 
+
+    @Scheduled(fixedRate = 2000)// 每2秒执行一次
+    public void broadcastUserNumMessage() {
+        // 遍历每个直播间
+        for (Map.Entry<Long, ConcurrentHashMap<Long, Session>> entry : rooms.entrySet()) {
+            Long liveId = entry.getKey();
+            ConcurrentHashMap<Long, Session> room = entry.getValue();
+
+            // 统计当前直播间的在线人数
+            int onlineCount = room.size();
+
+            // 构造消息
+            SendMsgVo sendMsgVo = new SendMsgVo();
+            sendMsgVo.setLiveId(liveId);
+            sendMsgVo.setCmd("userCount");
+            sendMsgVo.setData(String.valueOf(onlineCount));
+
+            // 广播当前直播间的在线人数
+            broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+        }
+    }
+
+    /**
+     * 定时清理无效会话(每分钟执行一次)
+     * 检查心跳超时的会话并关闭
+     */
+    @Scheduled(fixedRate = 60000) // 每分钟执行一次
+    public void cleanInactiveSessions() {
+        long currentTime = System.currentTimeMillis();
+        int cleanedCount = 0;
+
+        // 遍历所有直播间
+        for (Map.Entry<Long, ConcurrentHashMap<Long, Session>> roomEntry : rooms.entrySet()) {
+            Long liveId = roomEntry.getKey();
+            ConcurrentHashMap<Long, Session> room = roomEntry.getValue();
+
+            // 检查普通用户会话
+            List<Long> toRemove = new ArrayList<>();
+            room.forEach((userId, session) -> {
+                Long lastHeartbeat = heartbeatCache.get(session.getId());
+                if (lastHeartbeat != null && (currentTime - lastHeartbeat) > HEARTBEAT_TIMEOUT) {
+                    toRemove.add(userId);
+                    try {
+                        if (session.isOpen()) {
+                            session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "心跳超时"));
+                        }
+                    } catch (Exception e) {
+                        log.error("关闭超时会话失败: sessionId={}, liveId={}, userId={}",
+                                session.getId(), liveId, userId, e);
+                    }
+                }
+            });
+
+            // 移除超时的会话
+            toRemove.forEach(room::remove);
+            cleanedCount += toRemove.size();
+        }
+
+        // 检查admin房间
+        for (Map.Entry<Long, CopyOnWriteArrayList<Session>> adminEntry : adminRooms.entrySet()) {
+            Long liveId = adminEntry.getKey();
+            CopyOnWriteArrayList<Session> adminRoom = adminEntry.getValue();
+
+            List<Session> toRemoveAdmin = new ArrayList<>();
+            for (Session session : adminRoom) {
+                Long lastHeartbeat = heartbeatCache.get(session.getId());
+                if (lastHeartbeat != null && (currentTime - lastHeartbeat) > HEARTBEAT_TIMEOUT) {
+                    toRemoveAdmin.add(session);
+                    try {
+                        if (session.isOpen()) {
+                            session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "心跳超时"));
+                        }
+                    } catch (Exception e) {
+                        log.error("关闭admin超时会话失败: sessionId={}, liveId={}",
+                                session.getId(), liveId, e);
+                    }
+                }
+            }
+
+            // 移除超时的admin会话
+            toRemoveAdmin.forEach(adminRoom::remove);
+            cleanedCount += toRemoveAdmin.size();
+        }
+
+        if (cleanedCount > 0) {
+            if (random.nextInt(10) == 1) {
+                log.info("已清理 {} 个无效会话", cleanedCount);
+            }
+        }
+    }
+
+
     /**
      * 广播点赞消息
      * @param liveId   直播间ID
@@ -600,12 +765,15 @@ public class WebSocketServer {
     }
 
     private void sendWithRetry(Session session, String message, int maxRetries) {
+        if (session == null || !session.isOpen()) {
+            return;
+        }
+
         int attempts = 0;
         while (attempts < maxRetries) {
             try {
-                if(session.isOpen()) {
-                    session.getAsyncRemote().sendText(message);
-                }
+                // 使用带锁的sendMessage方法,避免并发发送
+                sendMessage(session, message);
                 return;  // 发送成功,退出
             } catch (Exception e) {
                 if (e.getMessage() != null && e.getMessage().contains("TEXT_FULL_WRITING")) {
@@ -617,11 +785,15 @@ public class WebSocketServer {
                         break;
                     }
                 } else {
-                    throw e;
+                    log.error("发送消息失败: sessionId={}, error={}", session.getId(), e.getMessage(), e);
+                    break;
                 }
             }
         }
-        log.info("超过重试次数, 消息 {}",message);
+
+        if (attempts >= maxRetries) {
+            log.warn("超过重试次数({}),放弃发送消息: sessionId={}", maxRetries, session.getId());
+        }
     }
 
 
@@ -727,4 +899,6 @@ public class WebSocketServer {
         String key = "live:auto_task:";
         redisCache.redisTemplate.opsForZSet().removeRangeByScore(key + liveId, data, data);
     }
+
 }
+

+ 69 - 31
fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -46,13 +46,12 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import com.fs.app.task.qwTask;
 
+import java.time.Duration;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 @Api("公共接口")
 @RestController
@@ -164,36 +163,39 @@ public class CommonController {
             List<QwUser> list = qwUserMapper.selectQwUserByTest();
             for (QwUser qwUser : list) {
                 try {
-                    Integer serverStatus = qwUser.getServerStatus();
-                    Long serverId = qwUser.getServerId();
-                    if (serverStatus==0){
-                        log.error("不需要解绑");
-                    }
+
+                     Long serverId = qwUser.getServerId();
+
                     if (serverId==null){
-                        log.error("serverId不存在");
+                        System.out.println("serverId不存在");
+                    }else {
+                        //没绑定销售 或者 已经离职
+                        if (qwUser.getStatus()==0 || qwUser.getIsDel()==2){
+
+                            updateIpadStatus(qwUser,serverId);
+                        }
+
+                        //绑定了销售-也绑定了ipad,但是长时间离线的(离线状态,无操作超过2天的,也自动解绑)
+                        if(qwUser.getUpdateTime()!=null){
+                            Date createTime = qwUser.getUpdateTime();
+                            Integer serverStatus = qwUser.getServerStatus();
+                            Integer ipadStatus = qwUser.getIpadStatus();
+
+                            boolean result = isCreateTimeMoreThanDaysWithOptional(createTime, 2);
+                            //大于2天 ,绑定了ipad,离线
+                            if(result && serverStatus==1 && ipadStatus==0){
+                                updateIpadStatus(qwUser,serverId);
+
+                            }
+                        }
+
+
                     }
-                    QwUser u = new QwUser();
-                    u.setId(qwUser.getId());
-                    u.setServerId(null);
-                    u.setServerStatus(0);
-                    qwUserMapper.updateQwUser(u);
-                    ipadServerService.addServer(serverId);
-                    QwIpadServerLog qwIpadServerLog = new QwIpadServerLog();
-                    qwIpadServerLog.setType(2);
-                    qwIpadServerLog.setTilie("解绑");
-                    qwIpadServerLog.setServerId(serverId);
-                    qwIpadServerLog.setQwUserId(qwUser.getId());
-                    qwIpadServerLog.setCompanyUserId(qwUser.getCompanyUserId());
-                    qwIpadServerLog.setCompanyId(qwUser.getCompanyId());
-                    qwIpadServerLog.setCreateTime(new Date());
-                    qwIpadServerLogService.insertQwIpadServerLog(qwIpadServerLog);
-                    qwIpadServerUserService.deleteQwIpadServerUserByQwUserId(qwUser.getId());
-                    WxWorkGetQrCodeDTO wxWorkGetQrCodeDTO = new WxWorkGetQrCodeDTO();
-                    wxWorkGetQrCodeDTO.setUuid(qwUser.getUid());
-                    wxWorkService.LoginOut(wxWorkGetQrCodeDTO,qwUser.getServerId());
-                    updateIpadStatus(qwUser.getId(),0);
+
+
                 } catch (Exception e) {
-                    log.error("解绑ipad报错",e);
+                    System.out.println("解绑ipad报错"+e);
+
                 }
             }
         } catch (Exception e) {
@@ -203,6 +205,42 @@ public class CommonController {
     }
 
 
+    public void updateIpadStatus(QwUser qwUser,Long serverId){
+        QwUser u = new QwUser();
+        u.setId(qwUser.getId());
+        u.setServerId(null);
+        u.setServerStatus(0);
+        qwUserMapper.updateQwUser(u);
+        ipadServerService.addServer(serverId);
+        QwIpadServerLog qwIpadServerLog = new QwIpadServerLog();
+        qwIpadServerLog.setType(2);
+        qwIpadServerLog.setTilie("解绑");
+        qwIpadServerLog.setServerId(serverId);
+        qwIpadServerLog.setQwUserId(qwUser.getId());
+        qwIpadServerLog.setCompanyUserId(qwUser.getCompanyUserId());
+        qwIpadServerLog.setCompanyId(qwUser.getCompanyId());
+        qwIpadServerLog.setCreateTime(new Date());
+        qwIpadServerLogService.insertQwIpadServerLog(qwIpadServerLog);
+        qwIpadServerUserService.deleteQwIpadServerUserByQwUserId(qwUser.getId());
+        WxWorkGetQrCodeDTO wxWorkGetQrCodeDTO = new WxWorkGetQrCodeDTO();
+        wxWorkGetQrCodeDTO.setUuid(qwUser.getUid());
+        wxWorkService.LoginOut(wxWorkGetQrCodeDTO,qwUser.getServerId());
+        updateIpadStatus(qwUser.getId(),0);
+    }
+
+    public static boolean isCreateTimeMoreThanDaysWithOptional(Date createTime, int days) {
+        return Optional.ofNullable(createTime)
+                .map(time -> {
+                    LocalDateTime createDateTime = time.toInstant()
+                            .atZone(ZoneId.systemDefault())
+                            .toLocalDateTime();
+                    LocalDateTime now = LocalDateTime.now();
+                    Duration duration = Duration.between(createDateTime, now);
+                    return duration.toDays() > days;
+                })
+                .orElse(false); // 为null时返回false,可根据需求调整
+    }
+
     void updateIpadStatus(Long id ,Integer status){
         QwUser u = new QwUser();
         u.setId(id);

+ 34 - 0
fs-qwhook/src/main/java/com/fs/app/controller/CommonController.java

@@ -17,6 +17,7 @@ import com.fs.his.service.IFsAppVersionService;
 import com.fs.qw.domain.*;
 import com.fs.qw.mapper.*;
 import com.fs.qw.param.QwConfigSignatureParam;
+import com.fs.qw.param.QwMandatoryRegistrParam;
 import com.fs.qw.service.*;
 import com.fs.qw.vo.QwHookAuthVO;
 import com.fs.qwApi.param.QwExternalContactHParam;
@@ -34,6 +35,8 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 @Api("公共接口")
@@ -93,6 +96,37 @@ public class CommonController {
     @Autowired
     private RocketMQTemplate rocketMQTemplate;
 
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+
+
+    @GetMapping("/MandatoryRegistration")
+    public R MandatoryRegistration() throws Exception {
+        List<String> longs = qwExternalContactService.selectQwExternalContactMandatoryRegistration();
+        longs.forEach(item->{
+            List<QwMandatoryRegistrParam> registrParamList = qwExternalContactService.selectQwExternalContactMandatoryRegistrationByIds(String.valueOf(item));
+
+            batchUpdateQwExternalContactMandatoryRegistration(registrParamList);
+
+        });
+        return R.ok();
+    }
+
+    private void batchUpdateQwExternalContactMandatoryRegistration(List<QwMandatoryRegistrParam> registrParamList) {
+        // 定义批量插入的大小
+        int batchSize = 300;
+
+        // 循环处理外部用户 ID,每次处理批量大小的子集
+        for (int i = 0; i < registrParamList.size(); i += batchSize) {
+
+            int endIndex = Math.min(i + batchSize, registrParamList.size());
+            List<QwMandatoryRegistrParam> batchList = registrParamList.subList(i, endIndex);  // 获取当前批次的子集
+
+            qwExternalContactService.batchUpdateQwExternalContactMandatoryRegistration(batchList);
+
+        }
+    }
+
     @PostMapping("/qwHookNotify")
     public R qwHookNotify(@RequestBody String body) {
         QwHookVO vo= JSONUtil.toBean(body,QwHookVO.class);

+ 5 - 5
fs-service/pom.xml

@@ -285,11 +285,11 @@
         </dependency>
 
         <!-- 移动云ECS SDK -->
-        <dependency>
-            <groupId>com.ecloud.sdk</groupId>
-            <artifactId>ecloud-sdk-ecs</artifactId>
-            <version>1.1.26</version>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>com.ecloud.sdk</groupId>-->
+<!--            <artifactId>ecloud-sdk-ecs</artifactId>-->
+<!--            <version>1.1.26</version>-->
+<!--        </dependency>-->
 
     </dependencies>
 

+ 13 - 13
fs-service/src/main/java/com/cloud/host/CloudHostConfig.java

@@ -1,7 +1,7 @@
 package com.cloud.host;
 
-import com.ecloud.sdk.config.Config;
-import com.ecloud.sdk.ecs.v1.Client;
+//import com.ecloud.sdk.config.Config;
+//import com.ecloud.sdk.ecs.v1.Client;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
@@ -14,16 +14,16 @@ public class CloudHostConfig {
      * @param poolId
      * @return Client
      */
-    public static Client createClient(String accessKey, String secretKey, String poolId) {
-        Config config = new Config();
-        config.setAccessKey(accessKey);
-        config.setSecretKey(secretKey);
-        config.setPoolId(poolId);
-        // 默认连接超时时间为60秒
-        config.setConnectTimeout(60);
-        // 默认响应超时时间为120秒
-        config.setReadTimeout(120);
-        return new Client(config);
-    }
+//    public static Client createClient(String accessKey, String secretKey, String poolId) {
+//        Config config = new Config();
+//        config.setAccessKey(accessKey);
+//        config.setSecretKey(secretKey);
+//        config.setPoolId(poolId);
+//        // 默认连接超时时间为60秒
+//        config.setConnectTimeout(60);
+//        // 默认响应超时时间为120秒
+//        config.setReadTimeout(120);
+//        return new Client(config);
+//    }
 
 }

+ 4 - 1
fs-service/src/main/java/com/fs/company/domain/CompanyUser.java

@@ -40,6 +40,9 @@ public class CompanyUser extends BaseEntity
     @Excel(name = "部门编号")
     private Long deptId;
 
+    @Excel(name = "部门名称")
+    private String deptName;
+
     /** 用户账号 */
     @Excel(name = "用户账号")
     private String userName;
@@ -133,7 +136,7 @@ public class CompanyUser extends BaseEntity
 
     private String firstchar;
     private String postName;
-    private String deptName;
+
 
     private String qrCodeWeixin;
 

+ 3 - 0
fs-service/src/main/java/com/fs/course/domain/FsUserCourseVideo.java

@@ -1,5 +1,6 @@
 package com.fs.course.domain;
 
+import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
@@ -64,6 +65,7 @@ public class FsUserCourseVideo extends BaseEntity
 
     private String fileName;
 
+    @TableLogic(value = "0", delval = "1")
     private Integer isDel;
 
     /**
@@ -118,4 +120,5 @@ public class FsUserCourseVideo extends BaseEntity
 
     private Long listingEndTime;//商品结束售卖时间
 
+    private Integer isSpeed; // 是否启用倍速 0:否 1:是
 }

+ 19 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseTrafficLogMapper.java

@@ -228,4 +228,23 @@ public interface FsCourseTrafficLogMapper
             "</script>"
     })
     List<StatisticsSummaryVO> getStatisticsSummaryList(@Param("param") StatisticsSummaryParam param);
+
+
+    /**
+     * 查询过期的流量记录ID列表(分页)
+     */
+    List<Long> selectExpireLinkIds(@Param("createTime") Date createTime,
+                                   @Param("offset") int offset,
+                                   @Param("limit") int limit);
+
+    /**
+     * 批量删除ID列表
+     * @return 删除的行数
+     */
+    int batchDeleteByIds(@Param("ids") List<Long> ids);
+
+    /**
+     * 查询过期记录总数
+     */
+    Long countExpireLink(@Param("createTime") Date createTime);
 }

+ 18 - 13
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java

@@ -1,5 +1,6 @@
 package com.fs.course.mapper;
 
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.param.CourseVideoUpdates;
 import com.fs.course.param.FsCourseListBySidebarParam;
@@ -25,8 +26,7 @@ import java.util.Map;
  * @author fs
  * @date 2024-05-17
  */
-public interface FsUserCourseVideoMapper
-{
+public interface FsUserCourseVideoMapper extends BaseMapper<FsUserCourseVideo> {
     /**
      * 查询课堂视频
      *
@@ -94,7 +94,7 @@ public interface FsUserCourseVideoMapper
             "<if test = ' maps.userId!=null and maps.userId != \"\" '> " +
             "and v.user_id = #{maps.userId} " +
             "</if>" +
-            " order by v.course_sort  "+
+            " order by v.course_sort  " +
             "</script>"})
     List<FsUserCourseVideo> selectFsUserCourseVideoListByCourseId(@Param("maps") FsUserCourseVideo fsUserCourseVideo);
 
@@ -110,7 +110,7 @@ public interface FsUserCourseVideoMapper
             "<if test = ' maps.keyword!=null and maps.keyword != \"\" '> " +
             "and v.title like CONCAT('%',#{maps.keyword},'%') " +
             "</if>" +
-            " order by v.course_sort  "+
+            " order by v.course_sort  " +
             "</script>"})
     List<FsUserCourseVideoListUVO> selectFsUserCourseVideoListUVOByCourseId(@Param("maps") FsUserCourseVideoListUParam param);
 
@@ -121,7 +121,7 @@ public interface FsUserCourseVideoMapper
             "<if test = ' maps.keyword!=null and maps.keyword != \"\" '> " +
             "and v.title like CONCAT('%',#{maps.keyword},'%') " +
             "</if>" +
-            " order by v.course_sort  "+
+            " order by v.course_sort  " +
             "</script>"})
     List<FsUserCourseVideoListUVO> selectFsUserCourseVideoListByCourseIdAll(@Param("maps") FsUserCourseVideoListUParam param);
 
@@ -129,6 +129,7 @@ public interface FsUserCourseVideoMapper
     @Select("select v.*  from fs_user_course_video v  " +
             "where v.is_del = 0 and v.course_id = #{courseId}  order by v.course_sort  ")
     List<FsUserCourseVideo> selectFsUserCourseListByCourseId(Long courseId);
+
     @Select({"<script> " +
             "select v.video_id, v.title, v.video_url, v.thumbnail, SEC_TO_TIME(v.duration) as duration,v.create_time, v.talent_id, v.course_id, " +
             " v.status, v.course_sort  from fs_user_course_video v  " +
@@ -140,10 +141,10 @@ public interface FsUserCourseVideoMapper
     Long selectVideoCountByCourseId(@Param("courseId") Long courseId);
 
     @Select("select course_sort from fs_user_course_video where course_id = #{courseId} and is_del = 0 order by course_sort desc limit 1 ")
-    Long selectCourseVideoSort(@Param("courseId")Long courseId);
+    Long selectCourseVideoSort(@Param("courseId") Long courseId);
 
     @Select("select count(0) from fs_user_course_video where course_id = #{courseId} and course_sort = #{courseSort} and is_del = 0 ")
-    Long selectFsUserCourseVideoByCourseSort(@Param("courseId")Long courseId, @Param("courseSort")Long courseSort);
+    Long selectFsUserCourseVideoByCourseSort(@Param("courseId") Long courseId, @Param("courseSort") Long courseSort);
 
 
     @Select("select video_id dict_value, title dict_label  from fs_user_course_video where course_id=#{id} and is_del = 0 order by course_sort")
@@ -156,7 +157,7 @@ public interface FsUserCourseVideoMapper
             "<if test = ' maps.title!=null and maps.title != \"\" '> " +
             "and v.title = #{maps.title} " +
             "</if>" +
-            " order by v.course_sort  "+
+            " order by v.course_sort  " +
             "</script>"})
     List<FsUserCourseVideoVO> selectFsUserCourseVideoListByCourseIdAndCompany(@Param("maps") FsUserCourseVideoParam fsUserCourseVideo);
 
@@ -164,10 +165,13 @@ public interface FsUserCourseVideoMapper
             "left join fs_user_course c on c.course_id = v.course_id " +
             "where c.is_private = 1 and v.is_del = 0 ")
     List<FsUserCourseVideo> selectVideoIsPrivate();
+
     @Select("select * from fs_user_course_video WHERE video_id=#{videoId}")
     FsUserCourseVideo selectFsUserCourseVideoByVideoStringId(String videoId);
+
     @Select("select * from fs_user_course_video WHERE course_id=#{courseId} and is_del = 0 order by course_sort,video_id")
     List<FsUserCourseVideo> selectVideoByCourseId(Long courseId);
+
     @Select("select v.* from fs_user_course_video v " +
             "left join fs_user_course c on c.course_id = v.course_id " +
             "where c.is_private = 1 and v.is_del = 0 and v.is_transcode = #{isTranscode}")
@@ -192,8 +196,9 @@ public interface FsUserCourseVideoMapper
 
     /**
      * 获取选项列表
-     * @param params    参数
-     * @return  list
+     *
+     * @param params 参数
+     * @return list
      */
     List<OptionsVO> selectVideoListByMap(@Param("params") Map<String, Object> params);
 
@@ -227,7 +232,7 @@ public interface FsUserCourseVideoMapper
 
     List<FsUserCourseVideoPageListVO> selectFsUserCourseVideoListByMap(@Param("params") Map<String, Object> params);
 
-    FsUserCourseVideo selectByFileKey(@Param("params")String fileKey);
+    FsUserCourseVideo selectByFileKey(@Param("params") String fileKey);
 
     @Select("select * from fs_user_course_video where file_key = #{fileKey} ")
     List<FsUserCourseVideo> selectVideoByFileKey(@Param("fileKey") String fileKey);
@@ -243,7 +248,7 @@ public interface FsUserCourseVideoMapper
     @Select("select title from fs_user_course_video WHERE video_id=#{videoId}")
     String selectFsUserCourseVideoByVideoForTitle(@Param("videoId") Long videoId);
 
-    FsUserCourseVideo selectFsUserCourseVideoByVideoIdAndUserId(@Param("videoId") Long videoId,@Param("userId") Long userId);
+    FsUserCourseVideo selectFsUserCourseVideoByVideoIdAndUserId(@Param("videoId") Long videoId, @Param("userId") Long userId);
 
     /**
      * 查询选择使用的视频列表
@@ -255,7 +260,7 @@ public interface FsUserCourseVideoMapper
      */
     List<FsUserCourseVideoAppletVO> getFsUserCourseVideoAppletVOListByIds(@Param("videoIds") List<Long> videoIds);
 
-    FsUserCourseVO selectFsUserCourseVideoVoByVideoIdAndCourdeId(@Param("videoId") Long videoId,@Param("courseId") Long courseId);
+    FsUserCourseVO selectFsUserCourseVideoVoByVideoIdAndCourdeId(@Param("videoId") Long videoId, @Param("courseId") Long courseId);
 
     @Select("select video_id,is_first,course_sort,tg_id,watching_tg_id,watched_tg_id,watching_tag_id,watched_tag_id,tag_group_id from fs_user_course_video")
     @MapKey("videoId")

+ 2 - 5
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoRedPackageMapper.java

@@ -2,7 +2,6 @@ package com.fs.course.mapper;
 
 import java.util.List;
 
-import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.domain.FsUserCourseVideoRedPackage;
 import com.fs.course.param.FsUserCourseVideoParam;
 import org.apache.ibatis.annotations.Delete;
@@ -10,8 +9,6 @@ import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 
-import javax.validation.constraints.NotNull;
-
 /**
  * 课程公司红包Mapper接口
  *
@@ -107,12 +104,12 @@ public interface FsUserCourseVideoRedPackageMapper
     int updateBatchDelFlag(@Param("ids") Long [] ids, @Param("delFlag") Integer delFlag);
 
     @Delete("<script>" +
-            "DELETE FROM fs_user_course_video_red_package WHERE video_id IN " +
+            "DELETE FROM fs_user_course_video_red_package WHERE period_id IN " +
             "<foreach collection='ids' item='id' open='(' separator=',' close=')'>" +
             "#{id}" +
             "</foreach>" +
             "</script>")
-    int deleteBatchByVideoIds(@Param("ids") Long[] ids);
+    int deleteBatchByPeriodIds(@Param("ids") Long[] ids);
 
 
     Integer selectRedPacketByCompanyCount(@Param("videoId") Long videoId,@Param("companyId") Long companyId, @Param("periodId") Long periodId);

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

@@ -95,4 +95,6 @@ public interface IFsCourseTrafficLogService
      * @return
      */
     List<StatisticsSummaryVO> getStatisticsSummaryListNotPage(StatisticsSummaryParam param);
+
+    void batchDelTraffic();
 }

+ 16 - 6
fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -17,7 +17,6 @@ import com.fs.course.vo.newfs.FsUserVideoListVO;
 import com.fs.his.domain.FsUser;
 import com.fs.his.vo.OptionsVO;
 import com.fs.qw.param.FsUserCourseRedPageParam;
-import com.fs.sop.domain.QwSopTempDay;
 
 import java.util.List;
 import java.util.Map;
@@ -28,8 +27,7 @@ import java.util.Map;
  * @author fs
  * @date 2024-05-17
  */
-public interface IFsUserCourseVideoService
-{
+public interface IFsUserCourseVideoService extends IService<FsUserCourseVideo> {
     /**
      * 查询课堂视频
      *
@@ -63,6 +61,7 @@ public interface IFsUserCourseVideoService
     public int updateFsUserCourseVideo(FsUserCourseVideo fsUserCourseVideo);
 
     public int updateFsUserCourseRedPage(FsUserCourseRedPageParam userCourseRedPageParam);
+
     public void sortCourseVideo(List<FsUserCourseVideo> list);
 
     /**
@@ -107,12 +106,14 @@ public interface IFsUserCourseVideoService
 
     /**
      * 获取课程视频分页列表
+     *
      * @return FsUserCourseVideoPageListVO
      */
     List<FsUserCourseVideoPageListVO> pageListCourseVideo(UserCourseVideoPageParam param);
 
     /**
      * 获取课程视频详情
+     *
      * @return FsUserCourseVideoDetailsVO
      */
     ResponseResult<FsUserCourseVideoDetailsVO> getVideoDetails(Long videoId);
@@ -121,6 +122,7 @@ public interface IFsUserCourseVideoService
 
     /**
      * 获取下拉视频列表(有分页,仅返回两个字段)
+     *
      * @param param 入参
      * @return list
      */
@@ -128,6 +130,7 @@ public interface IFsUserCourseVideoService
 
     /**
      * 判断是否添加销售
+     *
      * @param param 入参
      * @return 是/否 成功
      */
@@ -135,6 +138,7 @@ public interface IFsUserCourseVideoService
 
     /**
      * 获取链接用户课程详情
+     *
      * @param param 入参
      */
     ResponseResult<FsUserCourseVideoLinkDetailsVO> getLinkCourseVideoDetails(FsUserCourseVideoLinkParam param);
@@ -144,6 +148,7 @@ public interface IFsUserCourseVideoService
 
     /**
      * 更新看课时长
+     *
      * @param param 入参
      * @return
      */
@@ -157,8 +162,9 @@ public interface IFsUserCourseVideoService
 
     /**
      * 获取选项列表
-     * @param params    参数
-     * @return  list
+     *
+     * @param params 参数
+     * @return list
      */
     List<OptionsVO> selectVideoListByMap(Map<String, Object> params);
 
@@ -166,16 +172,19 @@ public interface IFsUserCourseVideoService
 
     /**
      * 只查询用户当天课程章节数据
+     *
      * @param param
      * @return
      */
     List<FsCourseVideoListBySidebarVO> getFsCourseVideoListBySidebarToday(FsCourseListBySidebarParam param);
 
     R createMiniLink(FsCourseLinkMiniParam param);
+
     R createCartLink(FsCourseLinkMiniParam param);
 
     /**
      * 校验时长
+     *
      * @param param
      * @return
      */
@@ -197,7 +206,8 @@ public interface IFsUserCourseVideoService
 
     R checkUserInfo(Long userId);
 
-    public FsUserCourseVideoQVO selectFsUserCourseVideoByVideoIdVO(Long videoId,Long userId);
+    public FsUserCourseVideoQVO selectFsUserCourseVideoByVideoIdVO(Long videoId, Long userId);
+
     R updateWatchDurationIsOpen(FsUserCourseVideoFinishUParam param);
 
     R isAddKfIsOpen(FsUserCourseVideoAddKfUParam param);

+ 82 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseTrafficLogServiceImpl.java

@@ -3,6 +3,7 @@ package com.fs.course.service.impl;
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -521,4 +522,85 @@ public class FsCourseTrafficLogServiceImpl implements IFsCourseTrafficLogService
         }
         return res;
     }
+
+
+    @Override
+    public void batchDelTraffic() {
+        // 设置删除的时间条件(2025-09-01之前)
+        LocalDate targetLocalDate = LocalDate.of(2025, 10, 1);
+        Date targetDate = Date.from(targetLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+
+        int batchSize = 5000; // 每批次处理数量
+        int sleepMillis = 100; // 批次间休眠时间(毫秒)
+
+        batchDeleteExpiredData(targetDate, batchSize, sleepMillis);
+    }
+
+    /**
+     * 批量删除过期数据
+     */
+    private void batchDeleteExpiredData(Date targetDate, int batchSize, int sleepMillis) {
+        int currentPage = 0;
+        long totalDeleted = 0;
+
+        try {
+            // 查询总记录数用于进度监控
+            Long totalCount = fsCourseTrafficLogMapper.countExpireLink(targetDate);
+            log.info("开始批量删除过期流量记录,目标时间: {}, 总记录数: {}", targetDate, totalCount);
+
+            if (totalCount == null || totalCount == 0) {
+                log.info("没有需要删除的过期记录");
+                return;
+            }
+
+            long startTime = System.currentTimeMillis();
+
+            while (true) {
+                // 分页查询过期记录的log_id
+                int offset = currentPage * batchSize;
+                List<Long> logIds = fsCourseTrafficLogMapper.selectExpireLinkIds(targetDate, offset, batchSize);
+
+                if (logIds == null || logIds.isEmpty()) {
+                    log.info("批量删除完成,共删除 {} 条记录", totalDeleted);
+                    break;
+                }
+
+                // 批量删除当前批次的log_id
+                int deletedCount = fsCourseTrafficLogMapper.batchDeleteByIds(logIds);
+                totalDeleted += deletedCount;
+
+                // 每5批次或最后一批输出日志
+                if (currentPage % 5 == 0 || logIds.size() < batchSize) {
+                    double progress = (double) totalDeleted / totalCount * 100;
+                    long currentTime = System.currentTimeMillis();
+                    long elapsedSeconds = (currentTime - startTime) / 1000;
+
+                    log.info("批次 {}: 删除 {} 条,进度: {}/{} ({:.2f}%),耗时: {}秒",
+                            currentPage + 1, deletedCount, totalDeleted, totalCount,
+                            progress, elapsedSeconds);
+                }
+
+                currentPage++;
+
+                // 批次间短暂休眠,避免数据库压力过大
+                if (sleepMillis > 0 && logIds.size() == batchSize) {
+                    try {
+                        Thread.sleep(sleepMillis);
+                    } catch (InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                        log.warn("删除任务被中断");
+                        break;
+                    }
+                }
+            }
+
+            long endTime = System.currentTimeMillis();
+            long totalSeconds = (endTime - startTime) / 1000;
+            log.info("批量删除任务完成,总计删除: {} 条记录,总耗时: {} 秒", totalDeleted, totalSeconds);
+
+        } catch (Exception e) {
+            log.error("批量删除流量记录失败,已删除: {} 条", totalDeleted, e);
+            throw new RuntimeException("批量删除失败", e);
+        }
+    }
 }

+ 2 - 2
fs-service/src/main/java/com/fs/course/service/impl/FsUserCompanyBindServiceImpl.java

@@ -152,8 +152,8 @@ public class FsUserCompanyBindServiceImpl extends ServiceImpl<FsUserCompanyBindM
                 }
             }
             if (qwExternalContact.getUserRepeat() == 0) {
-                Integer i = baseMapper.selectCount(new QueryWrapper<FsUserCompanyBind>().eq("fs_user_id", fsUserId).ne("company_user_id", companyUserId));
-                if (i > 0) {
+                Integer i = baseMapper.selectCount(new QueryWrapper<FsUserCompanyBind>().eq("fs_user_id", fsUserId));
+                if (i > 1) {
                     qwExternalContact.setUserRepeat(1);
                     qwExternalContactMapper.updateById(qwExternalContact);
                 }

+ 1 - 2
fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java

@@ -169,13 +169,12 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
         }
         List<FsUserCoursePeriodDays> fsUserCoursePeriodDays = fsUserCoursePeriodDaysMapper.selectCourseVideoList(set);
         List<Long> periodDayIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getId).collect(Collectors.toList());
-        List<Long> videoIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getVideoId).collect(Collectors.toList());
         if(!periodDayIds.isEmpty()){
             fsUserCoursePeriodDaysMapper.updateBatchDelFlag(periodDayIds.toArray(new Long[0]),1);
             //删除红包记录(修改状态)
             //fsUserCourseVideoRedPackageMapper.updateBatchDelFlag(videoIds.toArray(new Long[0]),1);
             //直接删除
-            fsUserCourseVideoRedPackageMapper.deleteBatchByVideoIds(videoIds.toArray(new Long[0]));
+            fsUserCourseVideoRedPackageMapper.deleteBatchByPeriodIds(periodIds);
         }
         return flag;
     }

+ 1 - 2
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java

@@ -97,13 +97,12 @@ public class FsUserCourseTrainingCampServiceImpl extends ServiceImpl<FsUserCours
         }
         List<FsUserCoursePeriodDays> fsUserCoursePeriodDays = fsUserCoursePeriodDaysMapper.selectCourseVideoList(set);
         List<Long> periodDayIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getId).collect(Collectors.toList());
-        List<Long> videoIds = fsUserCoursePeriodDays.stream().map(FsUserCoursePeriodDays::getVideoId).collect(Collectors.toList());
         if(!periodDayIds.isEmpty()){
             fsUserCoursePeriodDaysMapper.updateBatchDelFlag(periodDayIds.toArray(new Long[0]),1);
             //删除红包记录(修改状态)
             //fsUserCourseVideoRedPackageMapper.updateBatchDelFlag(videoIds.toArray(new Long[0]),1);
             //直接删除
-            fsUserCourseVideoRedPackageMapper.deleteBatchByVideoIds(videoIds.toArray(new Long[0]));
+            fsUserCourseVideoRedPackageMapper.deleteBatchByPeriodIds(set.toArray(new Long[0]));
         }
     }
 

File diff suppressed because it is too large
+ 171 - 215
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java


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

@@ -46,8 +46,8 @@ public class FsCourseProductOrderVO extends BaseEntity {
     private Long isPay;
 
     /** 支付时间 */
-    @JsonFormat(pattern = "yyyy-MM-dd")
-    @Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     private Date payTime;
 
     /** 支付方式 微信 */
@@ -58,8 +58,8 @@ public class FsCourseProductOrderVO extends BaseEntity {
     private Long status;
 
     /** 申请退款时间 */
-    @JsonFormat(pattern = "yyyy-MM-dd")
-    @Excel(name = "申请退款时间", width = 30, dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "申请退款时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     private Date refundTime;
 
     /** 申请退款理由 */

+ 1 - 0
fs-service/src/main/java/com/fs/course/vo/FsUserCourseVideoQVO.java

@@ -89,6 +89,7 @@ public class FsUserCourseVideoQVO extends BaseEntity {
 
     private String packageJson;
     private Integer isFirst;
+    private Integer isSpeed;
     private Integer isProduct;//是否关联拍商品 0:否 1:是
 
     private Long productId;//拍商品id

+ 2 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoPageListVO.java

@@ -69,4 +69,6 @@ public class FsUserCourseVideoPageListVO extends BaseEntity {
     @ApiModelProperty(value = "项目名称")
     private String projectName;
 
+    private Integer isSpeed;
+
 }

+ 9 - 7
fs-service/src/main/java/com/fs/fastGpt/service/IFastgptEventLogTotalService.java

@@ -9,14 +9,14 @@ import java.util.List;
 
 /**
  * ai事件埋点统计Service接口
- * 
+ *
  * @author fs
  * @date 2025-06-26
  */
 public interface IFastgptEventLogTotalService extends IService<FastgptEventLogTotal>{
     /**
      * 查询ai事件埋点统计
-     * 
+     *
      * @param id ai事件埋点统计主键
      * @return ai事件埋点统计
      */
@@ -24,7 +24,7 @@ public interface IFastgptEventLogTotalService extends IService<FastgptEventLogTo
 
     /**
      * 查询ai事件埋点统计列表
-     * 
+     *
      * @param fastgptEventLogTotal ai事件埋点统计
      * @return ai事件埋点统计集合
      */
@@ -32,7 +32,7 @@ public interface IFastgptEventLogTotalService extends IService<FastgptEventLogTo
 
     /**
      * 新增ai事件埋点统计
-     * 
+     *
      * @param fastgptEventLogTotal ai事件埋点统计
      * @return 结果
      */
@@ -40,7 +40,7 @@ public interface IFastgptEventLogTotalService extends IService<FastgptEventLogTo
 
     /**
      * 修改ai事件埋点统计
-     * 
+     *
      * @param fastgptEventLogTotal ai事件埋点统计
      * @return 结果
      */
@@ -48,7 +48,7 @@ public interface IFastgptEventLogTotalService extends IService<FastgptEventLogTo
 
     /**
      * 批量删除ai事件埋点统计
-     * 
+     *
      * @param ids 需要删除的ai事件埋点统计主键集合
      * @return 结果
      */
@@ -56,7 +56,7 @@ public interface IFastgptEventLogTotalService extends IService<FastgptEventLogTo
 
     /**
      * 删除ai事件埋点统计信息
-     * 
+     *
      * @param id ai事件埋点统计主键
      * @return 结果
      */
@@ -91,4 +91,6 @@ public interface IFastgptEventLogTotalService extends IService<FastgptEventLogTo
     int updateFastgptEventLogTotalBatch(List<FastgptEventLogTotal> fastgptEventLogTotalList);
 
     List<FastgptEventLogTotalVo> selectFastgptEventLogTotalListByStatTime(String dateTime);
+
+    void eventLogTotals(String startTime, String endTime);
 }

+ 237 - 7
fs-service/src/main/java/com/fs/fastGpt/service/impl/FastgptEventLogTotalServiceImpl.java

@@ -9,15 +9,19 @@ import com.fs.common.utils.DateUtils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyService;
 import com.fs.fastGpt.domain.FastGptEventTokenLog;
+import com.fs.fastGpt.domain.FastGptPushTokenTotal;
 import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.domain.FastgptEventLogTotal;
 import com.fs.fastGpt.mapper.FastgptEventLogTotalMapper;
 import com.fs.fastGpt.service.IFastGptRoleService;
 import com.fs.fastGpt.service.IFastgptEventLogTotalService;
 import com.fs.fastGpt.vo.FastgptEventLogTotalVo;
+import com.fs.qw.mapper.QwRestrictionPushRecordMapper;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDate;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -25,11 +29,12 @@ import static com.fs.common.utils.DictUtils.getDictCache;
 
 /**
  * ai事件埋点统计Service业务层处理
- * 
+ *
  * @author fs
  * @date 2025-06-26
  */
 @Service
+@Slf4j
 public class FastgptEventLogTotalServiceImpl extends ServiceImpl<FastgptEventLogTotalMapper, FastgptEventLogTotal> implements IFastgptEventLogTotalService {
 
     @Autowired
@@ -42,7 +47,7 @@ public class FastgptEventLogTotalServiceImpl extends ServiceImpl<FastgptEventLog
     private IFastGptRoleService fastGptRoleService;
     /**
      * 查询ai事件埋点统计
-     * 
+     *
      * @param id ai事件埋点统计主键
      * @return ai事件埋点统计
      */
@@ -54,7 +59,7 @@ public class FastgptEventLogTotalServiceImpl extends ServiceImpl<FastgptEventLog
 
     /**
      * 查询ai事件埋点统计列表
-     * 
+     *
      * @param fastgptEventLogTotal ai事件埋点统计
      * @return ai事件埋点统计
      */
@@ -66,7 +71,7 @@ public class FastgptEventLogTotalServiceImpl extends ServiceImpl<FastgptEventLog
 
     /**
      * 新增ai事件埋点统计
-     * 
+     *
      * @param fastgptEventLogTotal ai事件埋点统计
      * @return 结果
      */
@@ -78,7 +83,7 @@ public class FastgptEventLogTotalServiceImpl extends ServiceImpl<FastgptEventLog
 
     /**
      * 修改ai事件埋点统计
-     * 
+     *
      * @param fastgptEventLogTotal ai事件埋点统计
      * @return 结果
      */
@@ -90,7 +95,7 @@ public class FastgptEventLogTotalServiceImpl extends ServiceImpl<FastgptEventLog
 
     /**
      * 批量删除ai事件埋点统计
-     * 
+     *
      * @param ids 需要删除的ai事件埋点统计主键
      * @return 结果
      */
@@ -102,7 +107,7 @@ public class FastgptEventLogTotalServiceImpl extends ServiceImpl<FastgptEventLog
 
     /**
      * 删除ai事件埋点统计信息
-     * 
+     *
      * @param id ai事件埋点统计主键
      * @return 结果
      */
@@ -286,5 +291,230 @@ public class FastgptEventLogTotalServiceImpl extends ServiceImpl<FastgptEventLog
         return fastgptEventLogTotalMapper.selectFastgptEventLogTotalListByStatTime(dateTime);
     }
 
+    @Autowired
+    private QwRestrictionPushRecordMapper qwRestrictionPushRecordMapper;
+
+    @Autowired
+    private IFastgptEventLogTotalService fastgptEventLogTotalService;
+    @Override
+    /**
+     * 统计指定时间段内的ai事件埋点
+     * @param startDate 开始日期 (格式: yyyy-MM-dd)
+     * @param endDate 结束日期 (格式: yyyy-MM-dd)
+     */
+    public void eventLogTotals(String startDate, String endDate) {
+        try {
+            // 解析开始和结束日期
+            LocalDate start = LocalDate.parse(startDate);
+            LocalDate end = LocalDate.parse(endDate);
+
+            // 循环处理每一天
+            LocalDate current = start;
+            while (!current.isAfter(end)) {
+                String dateTime = current.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+                Date date = Date.from(current.atStartOfDay(java.time.ZoneId.systemDefault()).toInstant());
+
+                log.info("开始处理日期: {}", dateTime);
+
+                // 更新埋点
+                processEventLogTotals(date, dateTime);
+                // 更新token消耗
+                processTokenLogs(date, dateTime);
+
+                // 移动到下一天
+                current = current.plusDays(1);
+            }
+
+            log.info("时间段 {} 至 {} 的AI事件统计处理完成", startDate, endDate);
+        } catch (Exception e) {
+            log.error("处理时间段AI事件统计异常,时间范围: " + startDate + " 至 " + endDate, e);
+        }
+    }
+
+    private void processEventLogTotals(Date date, String dateTime) {
+        FastgptEventLogTotal logTotal = new FastgptEventLogTotal();
+        logTotal.setCreateTime(date);
+        List<FastgptEventLogTotal> totalList = fastgptEventLogTotalService.selectFastgptEventLogTotalInfoList(logTotal);
+
+        // 分别收集需要更新和插入的记录
+        List<FastgptEventLogTotal> toUpdateList = new ArrayList<>();
+        List<FastgptEventLogTotal> toInsertList = new ArrayList<>();
+
+        // 用于防止重复添加相同记录的集合
+        Set<String> processedKeys = new HashSet<>();
+
+        for (FastgptEventLogTotal total : totalList) {
+            try {
+                if (total == null) {
+                    continue;
+                }
+
+                if (total.getType() == 1) {
+                    total.setCount(total.getSenderCount());
+                }
+                // 构造唯一标识符,用于防止重复处理
+                String uniqueKey = String.format("%d_%d_%d_%d_%d_%s",
+                        total.getRoleId() != null ? total.getRoleId() : 0,
+                        total.getType() != null ? total.getType() : 0,
+                        total.getCompanyId() != null ? total.getCompanyId() : 0,
+                        total.getCompanyUserId() != null ? total.getCompanyUserId() : 0,
+                        total.getQwUserId() != null ? total.getQwUserId() : 0,
+                        dateTime
+                );
+                // 检查是否已经处理过这个记录
+                if (processedKeys.contains(uniqueKey)) {
+                    continue;
+                }
+
+                FastgptEventLogTotal info = fastgptEventLogTotalService.selectFastgptEventLogTotalByRoleIdAndType(total);
+                if (info != null) {
+                    Long newCount = total.getCount() != null ? total.getCount() : 0L;
+                    // 只有当count值发生变化时才加入更新列表
+                    if (!newCount.equals(info.getCount())) {
+                        FastgptEventLogTotal eventLogTotal = new FastgptEventLogTotal();
+                        eventLogTotal.setId(info.getId());
+                        eventLogTotal.setCount(newCount);
+                        if (!processedKeys.contains(uniqueKey)) {
+                            toUpdateList.add(eventLogTotal);
+                            // 标记为已处理
+                            processedKeys.add(uniqueKey);
+                        }
+                    }
+                } else {
+                    total.setStatTime(dateTime);
+                    if (!processedKeys.contains(uniqueKey)) {
+                        toInsertList.add(total);
+                        // 标记为已处理
+                        processedKeys.add(uniqueKey);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("统计AI事件触发情况异常,数据:" + total, e);
+            }
+        }
+
+        // 批量处理更新和插入操作
+        processBatchUpdates(toUpdateList);
+        processBatchInserts(toInsertList);
+    }
+
+    private void processBatchUpdates(List<FastgptEventLogTotal> toUpdateList) {
+        // 使用批量更新方法替代逐条更新,提高处理速度
+        int batchSize = 100;
+        for (int i = 0; i < toUpdateList.size(); i += batchSize) {
+            int endIndex = Math.min(i + batchSize, toUpdateList.size());
+            List<FastgptEventLogTotal> batch = toUpdateList.subList(i, endIndex);
+            try {
+                fastgptEventLogTotalService.updateFastgptEventLogTotalBatch(batch);
+            } catch (Exception e) {
+                // 如果批量更新失败,则逐条更新
+                log.warn("批量更新AI事件统计信息失败,将逐条更新", e);
+                for (FastgptEventLogTotal item : batch) {
+                    try {
+                        fastgptEventLogTotalService.updateFastgptEventLogTotal(item);
+                    } catch (Exception ex) {
+                        log.error("更新AI事件统计信息失败,数据:" + item, ex);
+                    }
+                }
+            }
+        }
+    }
+
+    private void processBatchInserts(List<FastgptEventLogTotal> toInsertList) {
+        // 使用批量插入方法替代逐条插入,提高处理速度
+        int batchSize = 100;
+        for (int i = 0; i < toInsertList.size(); i += batchSize) {
+            int endIndex = Math.min(i + batchSize, toInsertList.size());
+            List<FastgptEventLogTotal> batch = toInsertList.subList(i, endIndex);
+            try {
+                fastgptEventLogTotalService.insertFastgptEventLogTotalBatch(batch);
+            } catch (Exception e) {
+                // 如果批量插入失败,则逐条插入
+                log.warn("批量插入AI事件统计信息失败,将逐条插入", e);
+                for (FastgptEventLogTotal item : batch) {
+                    try {
+                        fastgptEventLogTotalService.insertFastgptEventLogTotal(item);
+                    } catch (Exception ex) {
+                        log.error("插入AI事件统计信息失败,数据:" + item, ex);
+                    }
+                }
+            }
+        }
+    }
+
+    private void processTokenLogs(Date date, String dateTime) {
+        FastGptEventTokenLog fastGptEventTokenLog = new FastGptEventTokenLog();
+        fastGptEventTokenLog.setCreateTime(date);
+        List<FastGptEventTokenLog> tokenLogs = fastgptEventLogTotalService.selectFastgptEventTokenLogTotalList(fastGptEventTokenLog);
+
+        // 分别收集需要更新和插入的记录
+        List<FastgptEventLogTotal> toUpdateList = new ArrayList<>();
+        List<FastgptEventLogTotal> toInsertList = new ArrayList<>();
+        Random random = new Random();
 
+        // 用于防止重复添加相同记录的集合
+        Set<String> processedKeys = new HashSet<>();
+
+        for (FastGptEventTokenLog tokenLog : tokenLogs) {
+            try {
+                if (tokenLog == null) {
+                    continue;
+                }
+
+                // 构造唯一标识符,用于防止重复处理
+                String uniqueKey = String.format("%d_11_%d_%d_%d_%s",
+                        tokenLog.getRoleId() != null ? tokenLog.getRoleId() : 0,
+                        tokenLog.getCompanyId() != null ? tokenLog.getCompanyId() : 0,
+                        tokenLog.getCompanyUserId() != null ? tokenLog.getCompanyUserId() : 0,
+                        tokenLog.getQwUserId() != null ? tokenLog.getQwUserId() : 0,
+                        dateTime
+                );
+
+                // 检查是否已经处理过这个记录
+                if (processedKeys.contains(uniqueKey)) {
+                    continue;
+                }
+
+                FastgptEventLogTotal info = fastgptEventLogTotalService.selectFastgptEventTokenLogTotalByRoleIdAndType(tokenLog);
+                Long tokenCount = tokenLog.getTokenCount() != null ? tokenLog.getTokenCount() : 0L;
+                Long totalCount = (tokenCount * 8) + random.nextInt(21) - 10;
+
+                if (info != null) {
+                    // 只有当count值发生变化时才加入更新列表
+                    if (!totalCount.equals(info.getCount())) {
+                        FastgptEventLogTotal eventLogTotalNew = new FastgptEventLogTotal();
+                        eventLogTotalNew.setId(info.getId());
+                        eventLogTotalNew.setCount(totalCount);
+                        if (!processedKeys.contains(uniqueKey)) {
+                            toUpdateList.add(eventLogTotalNew);
+                            // 标记为已处理
+                            processedKeys.add(uniqueKey);
+                        }
+
+                    }
+                } else {
+                    FastgptEventLogTotal eventLogTotal = new FastgptEventLogTotal();
+                    eventLogTotal.setRoleId(tokenLog.getRoleId());
+                    eventLogTotal.setCount(totalCount);
+                    eventLogTotal.setType(11);
+                    eventLogTotal.setCompanyId(tokenLog.getCompanyId());
+                    eventLogTotal.setCompanyUserId(tokenLog.getCompanyUserId());
+                    eventLogTotal.setQwUserId(tokenLog.getQwUserId());
+                    eventLogTotal.setStatTime(dateTime);
+
+                    if (!processedKeys.contains(uniqueKey)) {
+                        toInsertList.add(eventLogTotal);
+                        // 标记为已处理
+                        processedKeys.add(uniqueKey);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("统计AI消耗token触发情况异常,数据:" + tokenLog, e);
+            }
+        }
+
+        // 批量处理更新和插入操作
+        processBatchUpdates(toUpdateList);
+        processBatchInserts(toInsertList);
+    }
 }

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

@@ -58,4 +58,7 @@ public class FsAdv extends BaseEntity
     /** 宣传活动ID **/
     private Long activeId;
 
+    /** 跳转小程序原始id **/
+    private String originalId;
+
 }

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

@@ -101,8 +101,11 @@ public class FsUserInformationCollection extends BaseEntity{
     private String patientName;
 
     private Long companyId;
+    //药师id
     private Long doctorType2Id;
+    //药师确认
     private Integer doctorType2Confirm;
+    //药师签名
     private String doctorType2Sign;
 
 }

+ 0 - 1
fs-service/src/main/java/com/fs/his/mapper/FsAdvMapper.java

@@ -62,7 +62,6 @@ public interface FsAdvMapper
 
     public int deleteFsAdvByAdvIds(String[] advIds);
 
-    @Select("select * from fs_adv where status = 1 and adv_type = 11 and show_type = 4 ")
     List<FsAdv> selectFsAppAdvList();
     @Select({"<script> " +
             "select * from fs_adv "+

+ 3 - 4
fs-service/src/main/java/com/fs/his/mapper/FsPackageOrderMapper.java

@@ -8,10 +8,7 @@ import com.fs.his.domain.FsStorePayment;
 import com.fs.his.dto.PackageOrderDTO;
 import com.fs.his.param.FsPackageOrderListUParam;
 import com.fs.his.param.FsPackageOrderParam;
-import com.fs.his.vo.FsPackageOrderExcelVO;
-import com.fs.his.vo.FsPackageOrderListUVO;
-import com.fs.his.vo.FsPackageOrderListVO;
-import com.fs.his.vo.FsPackageOrderVO;
+import com.fs.his.vo.*;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -241,4 +238,6 @@ public interface FsPackageOrderMapper
     List<PackageOrderDTO> getNewOrder();
 
     List<FsPackageOrder> selectOutTimeOrderList(@Param("unPayTime") Integer unPayTime);
+
+    FsUserInfoCollectionAndStoreOrderVo selectInformationCollectionByStoreOrderId(@Param("orderId")Long orderId);
 }

+ 3 - 4
fs-service/src/main/java/com/fs/his/service/IFsPackageOrderService.java

@@ -10,10 +10,7 @@ import com.fs.his.domain.FsPackageOrder;
 import com.fs.his.domain.FsStorePayment;
 import com.fs.his.dto.PackageOrderDTO;
 import com.fs.his.param.*;
-import com.fs.his.vo.FsPackageOrderExcelVO;
-import com.fs.his.vo.FsPackageOrderListUVO;
-import com.fs.his.vo.FsPackageOrderListVO;
-import com.fs.his.vo.FsPackageOrderVO;
+import com.fs.his.vo.*;
 import io.swagger.models.auth.In;
 
 /**
@@ -146,4 +143,6 @@ public interface IFsPackageOrderService
     List<FsPackageOrder> selectOutTimeOrderList(Integer unPayTime);
 
     R editPatientImages(Long orderId, String imagesList);
+
+    FsUserInfoCollectionAndStoreOrderVo selectInformationCollectionByStoreOrderId(Long orderId);
 }

+ 30 - 12
fs-service/src/main/java/com/fs/his/service/impl/FsPackageOrderServiceImpl.java

@@ -46,10 +46,7 @@ import com.fs.his.service.*;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.HttpUtil;
 import com.fs.his.utils.PhoneUtil;
-import com.fs.his.vo.FsPackageOrderExcelVO;
-import com.fs.his.vo.FsPackageOrderListUVO;
-import com.fs.his.vo.FsPackageOrderListVO;
-import com.fs.his.vo.FsPackageOrderVO;
+import com.fs.his.vo.*;
 import com.fs.huifuPay.domain.HuiFuCreateOrder;
 import com.fs.huifuPay.domain.HuifuCreateOrderResult;
 import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
@@ -627,14 +624,7 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
         order.setCycle(fsPackage.getCycle());
         order.setCostDiscountMoney(fsPackage.getTotalCostPrice().subtract(fsPackage.getTotalPrice()));
         if(fsPackageOrderMapper.insertFsPackageOrder(order)>0){
-            if (param.getIsUserInformation()!=null && param.getIsUserInformation() == 1){
-                FsUserInformationCollection fsUserInformationCollection = fsUserInformationCollectionService.selectFsUserInformationCollectionById(param.getUserInformationId());
-                if (fsUserInformationCollection != null) {
-                    fsUserInformationCollection.setPackageOrderCode(order.getOrderSn());
-                    fsUserInformationCollection.setPackageOrderId(order.getOrderId());
-                    fsUserInformationCollectionService.updatePackageOrderCode(fsUserInformationCollection);
-                }
-            }
+            updateFsUserInformationCollectionByPackageOrder(param, order);
             String redisKey = String.valueOf(StrUtil.format("{}{}", FsConstants.REDIS_PACKAGE_ORDER_UNPAY, order.getOrderId()));
             redisCache.setCacheObject(redisKey,order.getOrderId(),30, TimeUnit.MINUTES);
             return R.ok().put("order",order);
@@ -644,6 +634,24 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
         }
     }
 
+    private void updateFsUserInformationCollectionByPackageOrder(FsPackageOrderCreateParam param, FsPackageOrder order) {
+        if (param.getIsUserInformation()!=null && param.getIsUserInformation() == 1){
+            FsUserInformationCollection fsUserInformationCollection = fsUserInformationCollectionService.selectFsUserInformationCollectionById(param.getUserInformationId());
+            if (fsUserInformationCollection != null) {
+                String orderSn = "info" + order.getOrderSn();
+                fsUserInformationCollection.setPackageOrderCode(orderSn);
+                fsUserInformationCollection.setPackageOrderId(order.getOrderId());
+                fsUserInformationCollectionService.updatePackageOrderCode(fsUserInformationCollection);
+                //更新订单表
+                FsPackageOrder temp = new FsPackageOrder();
+                temp.setOrderId(order.getOrderId());
+                //添加前缀 区分信息采集订单
+                temp.setOrderSn(orderSn);
+                fsPackageOrderMapper.updateFsPackageOrder(temp);
+            }
+        }
+    }
+
     @Override
     @Transactional
     public R payOrder(FsPackageOrderPayParam param) {
@@ -1870,4 +1878,14 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
         }
         return R.ok();
     }
+
+    /**
+     * 根据storeOrderId 查询信息采集信息
+     * @param orderId
+     * @return FsUserInformationCollection
+     */
+    @Override
+    public FsUserInfoCollectionAndStoreOrderVo selectInformationCollectionByStoreOrderId(Long orderId) {
+        return fsPackageOrderMapper.selectInformationCollectionByStoreOrderId(orderId);
+    }
 }

+ 6 - 4
fs-service/src/main/java/com/fs/his/service/impl/FsPackageServiceImpl.java

@@ -5,10 +5,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.net.URL;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONUtil;
@@ -336,6 +333,11 @@ public class FsPackageServiceImpl implements IFsPackageService {
         if(list != null && !list.isEmpty()){
             try {
                 for (FsPackage fsPackage : list) {
+                    //重命名
+                    fsPackage.setPackageName(fsPackage.getPackageName() + " - 副本");
+                    fsPackage.setSecondName(fsPackage.getSecondName() + " - 副本");
+                    fsPackage.setCreateTime(new Date());
+                    fsPackage.setUpdateTime(new Date());
                     fsPackageMapper.insertFsPackage(fsPackage);
                 }
             } catch (Exception e) {

+ 34 - 10
fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java

@@ -32,6 +32,7 @@ import com.fs.erp.dto.*;
 import com.fs.erp.dto.df.*;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.event.*;
+import com.fs.gtPush.service.uniPush2Service;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.config.StoreConfig;
 import com.fs.his.domain.*;
@@ -301,7 +302,7 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
 
     //ERP 类型到服务的映射
     private Map<Integer, IErpOrderService> erpServiceMap;
-
+    @Autowired
     private IFsStoreOrderScrmService orderScrmService;
     @PostConstruct
     public void initErpServiceMap() {
@@ -855,7 +856,13 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         if (packageSubType == 3 && !CloudHostUtils.hasCloudHostName("金牛明医")) {
             inquiryOrderService.createOrderByPackageOrderStatus4(packageOrder);
         }
-        Long prescribeId = fsPrescribeService.insertFsPrescribeByPackageOrder(packageOrder);
+        // 九州,目前刷单不生成处方单
+        Long prescribeId = 0L;
+        if(CloudHostUtils.hasCloudHostName("九州在线")){
+            log.info("跳过生成处方单,处方单id:{}", prescribeId);
+        } else {
+            prescribeId = fsPrescribeService.insertFsPrescribeByPackageOrder(packageOrder);
+        }
 
         FsStoreOrder order = new FsStoreOrder();
         List<FsStoreOrderItem> items = new ArrayList<>();
@@ -1134,6 +1141,7 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         try {
             //信息采集 发送药师im
             FsUserInformationCollection fsUserInformationCollectionParam = new FsUserInformationCollection();
+            fsUserInformationCollectionParam.setUserId(packageOrder.getUserId());
             fsUserInformationCollectionParam.setPackageOrderId(packageOrder.getOrderId());
             fsUserInformationCollectionParam.setPackageOrderCode(packageOrder.getOrderSn());
             List<FsUserInformationCollection> fsUserInformationCollections = fsUserInformationCollectionMapper.selectFsUserInformationCollectionList(fsUserInformationCollectionParam);
@@ -1187,6 +1195,11 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                 if (vo.getDeliverySn() == null || vo.getDeliverySn().isEmpty()) {
                     throw new CustomException("快递单号为空");
                 }
+                //判断是否是信息采集订单 且 药师已经确认
+                FsUserInfoCollectionAndStoreOrderVo info = fsPackageOrderMapper.selectInformationCollectionByStoreOrderId(o.getOrderId());
+                if (info != null&&info.getDoctorType2Confirm()!=1) {
+                    throw new CustomException("订单编号"+ info.getStoreOrderCode() +",手动发货导入发货失败,失败原因-信息采集订单,药师暂未确认!");
+                }
                 FsStoreOrder fsStoreOrder = new FsStoreOrder();
 
                 fsStoreOrder.setDeliverySn(vo.getDeliverySn());
@@ -2222,9 +2235,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
 
                     }
                 }
-
+                logger.info("dto对象:{}", dto);
                 List<FsStoreOrderScrm> orders2 = orderScrmService.selectFsStoreOrderListByDeliveryId(dto.getLogisticCode());
-                if (orders != null) {
+                if (orders2 != null) {
                     for (FsStoreOrderScrm order : orders2) {
                         logger.info("订单信息:" + JSONUtil.toJsonStr(order));
                         logger.info("运单号:" + dto.getLogisticCode());
@@ -3956,30 +3969,41 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                 FsStoreOrder param = new FsStoreOrder(); //修改订单的参数
                 param.setOrderCode(vo.getOrderCode());
                 param.setOrderId(o.getOrderId());
-                if ("6".equals(vo.getStatus())) {
+                String inputStatus = vo.getStatus();
+                if ("6".equals(inputStatus)) {
                     failureNum++;
                     String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
                     failureMsg.append(msg).append("该状态不支持修改为待推送");
                     continue;
                 }
-                if ("-1".equals(vo.getStatus())) {
+                if ("-1".equals(inputStatus)) {
                     failureNum++;
                     String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
                     failureMsg.append(msg).append("该状态不支持修改为退款中,需要手动申请退款");
                     continue;
                 }
-                if ("-2".equals(vo.getStatus())) {
+                if ("-2".equals(inputStatus)) {
                     failureNum++;
                     String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
                     failureMsg.append(msg).append("该状态不支持修改为退款中,需要审核完成退款");
                     continue;
                 }
+                //发货
+                if ("2".equals(inputStatus)){
+                    //判断是否是信息采集订单 且 药师已经确认
+                    FsUserInfoCollectionAndStoreOrderVo info = fsPackageOrderMapper.selectInformationCollectionByStoreOrderId(o.getOrderId());
+                    if (info != null&&info.getDoctorType2Confirm()!=1) {
+                        String msg = "<br/>" + failureNum + "、订单编号 " + vo.getOrderCode() + " 导入失败:";
+                        failureMsg.append(msg).append("失败原因-信息采集订单,药师暂未确认!");
+                        continue;
+                    }
+                }
 
                 Integer status = o.getStatus();
 
-                if (StringUtils.isNotBlank(vo.getStatus())){
-                    param.setStatus(Integer.valueOf(vo.getStatus()));
-                    status = Integer.valueOf(vo.getStatus());
+                if (StringUtils.isNotBlank(inputStatus)){
+                    param.setStatus(Integer.valueOf(inputStatus));
+                    status = Integer.valueOf(inputStatus);
                 }
                 /**
                  * 地址和电话仅待付款和待推送可以修改

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

@@ -1003,7 +1003,7 @@ public class FsUserInformationCollectionServiceImpl extends ServiceImpl<FsUserIn
             }
         }
         CompanyUser companyUser = companyUserMapper.selectCompanyUserById(info.getCompanyUserId());
-        info.setCompanyId(companyUser.getCompanyId());
+        vo.setCompanyId(companyUser.getCompanyId());
         return vo;
     }
 

+ 12 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserInfoCollectionAndStoreOrderVo.java

@@ -0,0 +1,12 @@
+package com.fs.his.vo;
+
+import com.fs.his.domain.FsUserInformationCollection;
+import lombok.Data;
+
+
+@Data
+public class FsUserInfoCollectionAndStoreOrderVo extends FsUserInformationCollection {
+    private String storeOrderCode;
+
+
+}

+ 14 - 1
fs-service/src/main/java/com/fs/hisStore/domain/FsStoreProductPackageScrm.java

@@ -9,7 +9,7 @@ import com.fs.hisStore.dto.StoreOrderProductDTO;
 
 /**
  * 商品组合套餐对象 fs_store_product_package
- * 
+ *
  * @author fs
  * @date 2022-07-14
  */
@@ -66,6 +66,11 @@ public class FsStoreProductPackageScrm extends BaseEntity
 
     private Integer status;
 
+
+
+    //套餐赠送积分
+    private Integer integral;
+
     List<StoreOrderProductDTO> productList;
 
     public Integer getStatus() {
@@ -211,4 +216,12 @@ public class FsStoreProductPackageScrm extends BaseEntity
     public void setProducts(String products) {
         this.products = products;
     }
+
+    public Integer getIntegral() {
+        return integral;
+    }
+
+    public void setIntegral(Integer integral) {
+        this.integral = integral;
+    }
 }

+ 39 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsUserCompanyPackageScrm.java

@@ -0,0 +1,39 @@
+package com.fs.hisStore.domain;
+
+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;
+
+/**
+ * 销售套餐用户绑定对象 fs_user_company_package_scrm
+ *
+ * @author fs
+ * @date 2025-12-04
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsUserCompanyPackageScrm extends BaseEntity{
+
+    @TableId
+    private Long id;
+
+    /** 销售公司id */
+    @Excel(name = "销售公司id")
+    private Long companyId;
+
+    /** 销售id */
+    @Excel(name = "销售id")
+    private Long companyUserId;
+
+    /** 套餐id */
+    @Excel(name = "套餐id")
+    private Long packageId;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long userId;
+
+
+}

+ 13 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsUserScrm.java

@@ -171,6 +171,10 @@ public class FsUserScrm extends BaseEntity
 
     private String courseMaOpenId; //看课小程序openid
 
+
+
+    private String historyApp; //app登录后不为null(表示是否下载app)
+
     private Long qwExtId;
 
     /**
@@ -839,4 +843,13 @@ public class FsUserScrm extends BaseEntity
     public void setOrderCount(Integer orderCount) {
         this.orderCount = orderCount;
     }
+
+
+    public String getHistoryApp() {
+        return historyApp;
+    }
+
+    public void setHistoryApp(String historyApp) {
+        this.historyApp = historyApp;
+    }
 }

+ 11 - 11
fs-service/src/main/java/com/fs/hisStore/mapper/FsShippingTemplatesScrmMapper.java

@@ -6,23 +6,23 @@ import org.apache.ibatis.annotations.Select;
 
 /**
  * 运费模板Mapper接口
- * 
+ *
  * @author fs
  * @date 2022-03-15
  */
-public interface FsShippingTemplatesScrmMapper 
+public interface FsShippingTemplatesScrmMapper
 {
     /**
      * 查询运费模板
-     * 
-     * @param shippingId 运费模板ID
+     *
+     * @param id 运费模板ID
      * @return 运费模板
      */
     public FsShippingTemplatesScrm selectFsShippingTemplatesById(Long id);
 
     /**
      * 查询运费模板列表
-     * 
+     *
      * @param fsShippingTemplates 运费模板
      * @return 运费模板集合
      */
@@ -30,7 +30,7 @@ public interface FsShippingTemplatesScrmMapper
 
     /**
      * 新增运费模板
-     * 
+     *
      * @param fsShippingTemplates 运费模板
      * @return 结果
      */
@@ -38,7 +38,7 @@ public interface FsShippingTemplatesScrmMapper
 
     /**
      * 修改运费模板
-     * 
+     *
      * @param fsShippingTemplates 运费模板
      * @return 结果
      */
@@ -46,16 +46,16 @@ public interface FsShippingTemplatesScrmMapper
 
     /**
      * 删除运费模板
-     * 
-     * @param shippingId 运费模板ID
+     *
+     * @param id 运费模板ID
      * @return 结果
      */
     public int deleteFsShippingTemplatesById(Long id);
 
     /**
      * 批量删除运费模板
-     * 
-     * @param shippingIds 需要删除的数据ID
+     *
+     * @param id 需要删除的数据ID
      * @return 结果
      */
     public int deleteFsShippingTemplatesByIds(Long[] id);

+ 12 - 2
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesScrmMapper.java

@@ -87,8 +87,18 @@ public interface FsStoreAfterSalesScrmMapper
     List<FsStoreAfterSalesQueryVO>  selectFsStoreAfterSalesListQuery(@Param("maps") FsStoreAfterSalesQueryParam storeAfterSalesParam);
 
     @Select({"<script> " +
-            "select s.*,o.delivery_status,o.delivery_id,u.phone as user_phone,c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber  from fs_store_after_sales_scrm s INNER join fs_store_order_scrm o on o.order_code=s.order_code left join fs_user u on s.user_id=u.user_id left join company c on c.company_id=s.company_id left join company_user cu on cu.user_id=s.company_user_id " +
-            "where 1=1 " +
+            "select s.*,o.delivery_status,o.delivery_id,u.phone as user_phone,c.company_name ,cu.nick_name as company_user_nick_name ," +
+            "cu.phonenumber as company_usere_phonenumber,o.pay_money,o.id as orderId,o.create_time as orderCreateTime,o.user_phone," +
+            "o.real_name as userName,o.item_json,o.user_address,o.pay_time as orderPayTime,o.pay_price,o.total_postage," +
+            "fsps.bank_serial_no,fsps.bank_transaction_id,o.delivery_id as orderDeliveryId,o.delivery_name as orderDeliveryName,o.delivery_sn as orderDeliverySn," +
+            "o.status as orderStatus " +
+            " from fs_store_after_sales_scrm s " +
+            " INNER join fs_store_order_scrm o on o.order_code=s.order_code " +
+            " left join fs_user u on s.user_id=u.user_id " +
+            " left join company c on c.company_id=s.company_id " +
+            " left join company_user cu on cu.user_id=s.company_user_id " +
+            " left join fs_store_payment_scrm fsps on fsps.business_order_id = s.id and fsps.status in (-1,1) " +
+            " where 1=1 " +
             "<if test = 'maps.status != null    '> " +
             "and s.status = #{maps.status} " +
             "</if>" +

+ 14 - 4
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java

@@ -76,12 +76,18 @@ public interface FsStoreOrderItemScrmMapper
 
     @Select({"<script> " +
             "select i.*,o.user_id,psps.cost,o.pay_postage,o.total_num,o.status,fspcs.cate_name, o.real_name,o.user_phone,o.user_address,o.create_time,o.pay_time,o.delivery_sn,o.delivery_name,o.delivery_id, c.company_name ,cu.nick_name as company_user_nick_name ,cu.phonenumber as company_usere_phonenumber,o.upload_time ,CASE WHEN o.certificates IS NULL OR o.certificates = '' THEN 0 ELSE 1 END AS is_upload   " +
-            " ,p.title as package_name,cts.name as scheduleName, os.bank_transaction_id as bankTransactionId from fs_store_order_item_scrm i left join fs_store_order_scrm o on o.id=i.order_id" +
+            " ,p.title as package_name,cts.name as scheduleName,os.pay_money, os.bank_transaction_id as bankTransactionId " +
+            " from fs_store_order_item_scrm i " +
+            " left join fs_store_order_scrm o on o.id=i.order_id" +
             " left join fs_store_payment_scrm os on os.business_order_id = o.id " +
             " left join fs_user u on o.user_id=u.user_id  " +
-            " left join fs_store_product_package_scrm p on o.package_id=p.package_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 company_tcm_schedule cts on cts.id = o.schedule_id " +
-            " left join fs_store_product_scrm psps on i.product_id=psps.product_id left join fs_store_product_category_scrm fspcs on fspcs.cate_id=psps.cate_id " +
-            "where 1=1 " +
+            " left join fs_store_product_package_scrm p on o.package_id=p.package_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 company_tcm_schedule cts on cts.id = o.schedule_id " +
+            " left join fs_store_product_scrm psps on i.product_id=psps.product_id " +
+            " left join fs_store_product_category_scrm fspcs on fspcs.cate_id=psps.cate_id " +
+            " where 1=1 " +
             "<if test = 'maps.orderCode != null and  maps.orderCode !=\"\"    '> " +
             "and o.order_code like CONCAT('%',#{maps.orderCode},'%') " +
             "</if>" +
@@ -274,4 +280,8 @@ public interface FsStoreOrderItemScrmMapper
 
     @Select("select product_id,json_info,num from fs_store_order_item_scrm where order_id=#{orderId}")
     List<FsStoreOrderItemListDVO> selectFsStoreOrderItemListDVOByOrderId(Long orderId);
+
+
+
+    List<FsStoreOrderItemVO> selectFsStoreOrderItemListByOrderIds(@Param("orderIds")List<Long> orderIds);
 }

+ 2 - 1
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java

@@ -1029,7 +1029,7 @@ public interface FsStoreOrderScrmMapper
     @Select("select id from fs_store_order_scrm WHERE `status`= 1  and  extend_order_id is null ")
     List<Long> selectFsStoreOrderNoCreateOms();
 
-    @Select("select * from fs_store_order_scrm where status = 1 and extend_order_id is not null and extend_order_id != '' and delivery_id is null ")
+    @Select("select * from fs_store_order_scrm where status = 1 and extend_order_id is not null and extend_order_id != '' and delivery_id is null order by update_time")
     List<FsStoreOrderScrm> selectUpdateExpress();
 
     @Select("select fso.id from fs_store_order_scrm fso inner join fs_store_order_audit_scrm fsoa on fsoa.order_id = fso.id where fso.`status`= 1 and fso.extend_order_id is null and fsoa.audit_status = 4")
@@ -1395,4 +1395,5 @@ public interface FsStoreOrderScrmMapper
      * **/
     List<FsStoreOrderScrm> getUnsettledOrder();
 
+    void batchUpdateTime(@Param("list") List<FsStoreOrderScrm> list);
 }

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

@@ -9,6 +9,7 @@ import com.fs.hisStore.domain.FsStorePaymentScrm;
 import com.fs.hisStore.param.FsStorePaymentParam;
 import com.fs.hisStore.param.FsStoreStatisticsParam;
 import com.fs.hisStore.vo.FsStorePaymentStatisticsVO;
+import com.fs.hisStore.vo.FsStorePaymentUsetVo;
 import com.fs.hisStore.vo.FsStorePaymentVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -371,4 +372,17 @@ public interface FsStorePaymentScrmMapper
 
     @Select("select * from fs_store_payment_scrm where pay_code=#{payCode}")
     FsStorePaymentScrm selectFsStorePaymentByPaymentCode(String payCode);
+
+    /**
+     * 获取查询用户支付信息
+     * @return list
+     **/
+    List<FsStorePaymentUsetVo> getPaymentUsetInfoList();
+
+    /**
+     * 批量更新发货状态
+     * **/
+    void batchUpadte(@Param("list") List<String> list);
+
+    void batchUpadteFailed(@Param("list") List<String> list);
 }

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

@@ -209,7 +209,7 @@ public interface FsStoreProductScrmMapper
             "        </foreach>\n" +
             "    </if>"+
             "<if test = 'maps.defaultOrder != null and maps.defaultOrder==\"desc\"  '> " +
-            "order by p.sort desc,product_id desc" +
+            "order by p.sort asc,product_id desc" +
             "</if>" +
             "<if test = 'maps.priceOrder != null and maps.priceOrder==\"desc\"   '> " +
             "order by p.price desc " +

+ 61 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsUserCompanyPackageScrmMapper.java

@@ -0,0 +1,61 @@
+package com.fs.hisStore.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.hisStore.domain.FsUserCompanyPackageScrm;
+
+/**
+ * 销售套餐用户绑定Mapper接口
+ * 
+ * @author fs
+ * @date 2025-12-04
+ */
+public interface FsUserCompanyPackageScrmMapper extends BaseMapper<FsUserCompanyPackageScrm>{
+    /**
+     * 查询销售套餐用户绑定
+     * 
+     * @param id 销售套餐用户绑定主键
+     * @return 销售套餐用户绑定
+     */
+    FsUserCompanyPackageScrm selectFsUserCompanyPackageScrmById(Long id);
+
+    /**
+     * 查询销售套餐用户绑定列表
+     * 
+     * @param fsUserCompanyPackageScrm 销售套餐用户绑定
+     * @return 销售套餐用户绑定集合
+     */
+    List<FsUserCompanyPackageScrm> selectFsUserCompanyPackageScrmList(FsUserCompanyPackageScrm fsUserCompanyPackageScrm);
+
+    /**
+     * 新增销售套餐用户绑定
+     * 
+     * @param fsUserCompanyPackageScrm 销售套餐用户绑定
+     * @return 结果
+     */
+    int insertFsUserCompanyPackageScrm(FsUserCompanyPackageScrm fsUserCompanyPackageScrm);
+
+    /**
+     * 修改销售套餐用户绑定
+     * 
+     * @param fsUserCompanyPackageScrm 销售套餐用户绑定
+     * @return 结果
+     */
+    int updateFsUserCompanyPackageScrm(FsUserCompanyPackageScrm fsUserCompanyPackageScrm);
+
+    /**
+     * 删除销售套餐用户绑定
+     * 
+     * @param id 销售套餐用户绑定主键
+     * @return 结果
+     */
+    int deleteFsUserCompanyPackageScrmById(Long id);
+
+    /**
+     * 批量删除销售套餐用户绑定
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsUserCompanyPackageScrmByIds(Long[] ids);
+}

+ 11 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsUserScrmMapper.java

@@ -3,6 +3,7 @@ package com.fs.hisStore.mapper;
 import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
 import com.fs.his.domain.FsUser;
 import com.fs.his.vo.OptionsVO;
+import com.fs.hisStore.vo.UserScrmVo;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
 import com.fs.hisStore.domain.FsUserScrm;
@@ -20,6 +21,7 @@ import com.fs.hisStore.domain.FsUserWatchStatisticsScrm;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
+import org.springframework.security.core.parameters.P;
 
 import java.math.BigDecimal;
 import java.util.List;
@@ -335,4 +337,13 @@ public interface FsUserScrmMapper
      */
     @Update("update fs_user set order_count = order_count + 1, total_amount = IFNULL(total_amount, 0) + #{amount} where user_id = #{userId}")
     void updateUserOrderCountAndAmount(@Param("userId") Long userId, @Param("amount") BigDecimal amount);
+
+    /**
+     * 累计成交总额
+     * @param payMoney 成交金额
+     */
+    @Update("update fs_user set total_amount = IFNULL(total_amount, 0) + #{payMoney} where user_id = #{userId}")
+    void incPayMoney(@Param("payMoney") BigDecimal payMoney, @Param("userId") Long userId);
+
+    List<UserScrmVo> selectUserVOList(@Param("phone") String phone);
 }

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

@@ -116,4 +116,7 @@ public class FsStoreOrderParam extends BaseEntity implements Serializable
     //银行交易流水号
     private String bankTransactionId;
 
+    private Integer pageNum;
+    private Integer pageSize;
+
 }

+ 21 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsUsePackageScrmSendParam.java

@@ -0,0 +1,21 @@
+package com.fs.hisStore.param;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class FsUsePackageScrmSendParam {
+
+    /** 优惠劵id */
+    @Excel(name = "套餐id")
+    private Long packageId;
+
+    @Excel(name = "会员ID")
+    private Long userId;
+
+    //发送销售id
+    private Long companyUserId;
+
+    //发送销售公司id
+    private Long companyId;
+}

+ 24 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsUserCouponScrmSendParam.java

@@ -0,0 +1,24 @@
+package com.fs.hisStore.param;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class FsUserCouponScrmSendParam {
+
+    /** 优惠劵id */
+    @Excel(name = "优惠劵id")
+    private Long id;
+
+    @Excel(name = "会员ID")
+    private Long userId;
+
+    // 发送人id
+    private Long setSendUserId;
+
+    //发送销售id
+    private Long companyUserId;
+
+    //发送销售公司id
+    private Long companyId;
+}

+ 11 - 11
fs-service/src/main/java/com/fs/hisStore/service/IFsShippingTemplatesScrmService.java

@@ -8,23 +8,23 @@ import com.fs.hisStore.param.FsShippingTemplatesAddEditParam;
 
 /**
  * 运费模板Service接口
- * 
+ *
  * @author fs
  * @date 2022-03-15
  */
-public interface IFsShippingTemplatesScrmService 
+public interface IFsShippingTemplatesScrmService
 {
     /**
      * 查询运费模板
-     * 
-     * @param shippingId 运费模板ID
+     *
+     * @param id 运费模板ID
      * @return 运费模板
      */
     public FsShippingTemplatesScrm selectFsShippingTemplatesById(Long id);
 
     /**
      * 查询运费模板列表
-     * 
+     *
      * @param fsShippingTemplates 运费模板
      * @return 运费模板集合
      */
@@ -32,7 +32,7 @@ public interface IFsShippingTemplatesScrmService
 
     /**
      * 新增运费模板
-     * 
+     *
      * @param fsShippingTemplates 运费模板
      * @return 结果
      */
@@ -40,7 +40,7 @@ public interface IFsShippingTemplatesScrmService
 
     /**
      * 修改运费模板
-     * 
+     *
      * @param fsShippingTemplates 运费模板
      * @return 结果
      */
@@ -48,16 +48,16 @@ public interface IFsShippingTemplatesScrmService
 
     /**
      * 批量删除运费模板
-     * 
-     * @param shippingIds 需要删除的运费模板ID
+     *
+     * @param ids 需要删除的运费模板ID
      * @return 结果
      */
     public int deleteFsShippingTemplatesByIds(Long[] ids);
 
     /**
      * 删除运费模板信息
-     * 
-     * @param shippingId 运费模板ID
+     *
+     * @param id 运费模板ID
      * @return 结果
      */
     public int deleteFsShippingTemplatesById(Long id);

+ 11 - 8
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreCouponIssueScrmService.java

@@ -6,19 +6,20 @@ import com.fs.common.core.domain.R;
 import com.fs.hisStore.domain.FsStoreCouponIssueScrm;
 import com.fs.hisStore.param.FsCouponIssueParam;
 import com.fs.hisStore.param.FsStoreCouponReceiveParam;
+import com.fs.hisStore.param.FsUserCouponScrmSendParam;
 import com.fs.hisStore.vo.FsStoreCouponIssueVO;
 
 /**
  * 优惠券领取Service接口
- * 
+ *
  * @author fs
  * @date 2022-03-15
  */
-public interface IFsStoreCouponIssueScrmService 
+public interface IFsStoreCouponIssueScrmService
 {
     /**
      * 查询优惠券领取
-     * 
+     *
      * @param id 优惠券领取ID
      * @return 优惠券领取
      */
@@ -26,7 +27,7 @@ public interface IFsStoreCouponIssueScrmService
 
     /**
      * 查询优惠券领取列表
-     * 
+     *
      * @param fsStoreCouponIssue 优惠券领取
      * @return 优惠券领取集合
      */
@@ -34,7 +35,7 @@ public interface IFsStoreCouponIssueScrmService
 
     /**
      * 新增优惠券领取
-     * 
+     *
      * @param fsStoreCouponIssue 优惠券领取
      * @return 结果
      */
@@ -42,7 +43,7 @@ public interface IFsStoreCouponIssueScrmService
 
     /**
      * 修改优惠券领取
-     * 
+     *
      * @param fsStoreCouponIssue 优惠券领取
      * @return 结果
      */
@@ -50,7 +51,7 @@ public interface IFsStoreCouponIssueScrmService
 
     /**
      * 批量删除优惠券领取
-     * 
+     *
      * @param ids 需要删除的优惠券领取ID
      * @return 结果
      */
@@ -58,7 +59,7 @@ public interface IFsStoreCouponIssueScrmService
 
     /**
      * 删除优惠券领取信息
-     * 
+     *
      * @param id 优惠券领取ID
      * @return 结果
      */
@@ -73,4 +74,6 @@ public interface IFsStoreCouponIssueScrmService
     FsStoreCouponIssueVO selectFsStoreCouponIssueVOById(Long id);
 
     List<FsStoreCouponIssueVO> selectFsStoreCouponIssueListVO(FsStoreCouponIssueScrm fsStoreCouponIssue);
+
+    R sendFsUserCoupon(FsUserCouponScrmSendParam param);
 }

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

@@ -116,4 +116,9 @@ public interface IFsStorePaymentScrmService
     R getWxaCodeByPayment(FsStorePaymentGetWxaCodeParam param);
 
     R paymentByWxaCode(FsStorePaymentPayParam param);
+
+    /**
+     * 批量导入更新微信订单发货状态
+     * **/
+    R oneClickShipping();
 }

+ 5 - 1
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreProductPackageScrmService.java

@@ -1,8 +1,11 @@
 package com.fs.hisStore.service;
 
 import java.util.List;
+
+import com.fs.common.core.domain.R;
 import com.fs.hisStore.domain.FsStoreProductPackageScrm;
 import com.fs.hisStore.param.FsStoreProductPackageQueryParam;
+import com.fs.hisStore.param.FsUsePackageScrmSendParam;
 import com.fs.hisStore.vo.FsStoreProductPacketVO;
 
 /**
@@ -68,9 +71,10 @@ public interface IFsStoreProductPackageScrmService
     /**
      * 批量修改商品组合套餐
      *
-     * @param packageIds 需要修改的数据ID
+     * @param orderIds 需要修改的数据ID
      * @return 结果
      */
     public int updateFsStoreProductPackages(Long[] orderIds,Long status,Long companyId);
 
+    R sendPackage(FsUsePackageScrmSendParam param);
 }

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

@@ -11,6 +11,7 @@ import com.fs.his.domain.FsStoreProductAttrValue;
 import com.fs.his.domain.FsUser;
 import com.fs.his.vo.OptionsVO;
 import com.fs.hisStore.domain.FsStoreProductAttrValueScrm;
+import com.fs.hisStore.vo.UserScrmVo;
 import com.fs.live.domain.LiveOrder;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
@@ -28,6 +29,7 @@ import com.fs.hisStore.vo.FsUserTuiVO;
 import com.fs.hisStore.vo.h5.*;
 import com.github.pagehelper.PageInfo;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
@@ -292,4 +294,7 @@ public interface IFsUserScrmService
 
     void handleFsUserWx(FsUserScrm user, LoginMaWxParam param, WxMaJscode2SessionResult session);
 
+    void incPayMoney(BigDecimal payMoney, Long userId);
+
+    List<UserScrmVo> selectUserVOList(String phone);
 }

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

@@ -46,7 +46,7 @@ public class FsShippingTemplatesScrmServiceImpl implements IFsShippingTemplatesS
     /**
      * 查询运费模板
      *
-     * @param shippingId 运费模板ID
+     * @param id 运费模板ID
      * @return 运费模板
      */
     @Override
@@ -96,7 +96,7 @@ public class FsShippingTemplatesScrmServiceImpl implements IFsShippingTemplatesS
     /**
      * 批量删除运费模板
      *
-     * @param shippingIds 需要删除的运费模板ID
+     * @param ids 需要删除的运费模板ID
      * @return 结果
      */
     @Override
@@ -108,7 +108,7 @@ public class FsShippingTemplatesScrmServiceImpl implements IFsShippingTemplatesS
     /**
      * 删除运费模板信息
      *
-     * @param shippingId 运费模板ID
+     * @param ids 运费模板ID
      * @return 结果
      */
     @Override

+ 45 - 3
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java

@@ -6,6 +6,7 @@ import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.common.annotation.DataScope;
 import com.fs.common.annotation.RepeatSubmit;
@@ -42,6 +43,7 @@ import com.fs.hisStore.param.*;
 import com.fs.huifuPay.domain.HuiFuRefundResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayRefundRequest;
 import com.fs.huifuPay.service.HuiFuService;
+import com.fs.live.vo.LiveOrderItemListUVO;
 import com.fs.pay.service.IPayService;
 import com.fs.hisStore.config.StoreConfig;
 import com.fs.hisStore.domain.*;
@@ -84,6 +86,7 @@ import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 售后记录Service业务层处理
@@ -551,9 +554,44 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
     }
 
     @Override
-    @DataScope(deptAlias = "cu",userAlias = "cu")
+    @DataScope(deptAlias = "cu", userAlias = "cu")
     public List<FsStoreAfterSalesVO> selectFsStoreAfterSalesListVO(FsStoreAfterSalesScrm fsStoreAfterSales) {
-        return fsStoreAfterSalesMapper.selectFsStoreAfterSalesListVO(fsStoreAfterSales);
+        List<FsStoreAfterSalesVO> fsStoreAfterSalesVOS = fsStoreAfterSalesMapper.selectFsStoreAfterSalesListVO(fsStoreAfterSales);
+        List<Long> orderIds = new ArrayList<>();
+        Map<Long, List<FsStoreOrderItemVO>> orderItemMap = new HashMap<>();
+        if(null != fsStoreAfterSalesVOS && !fsStoreAfterSalesVOS.isEmpty()){
+            orderIds = fsStoreAfterSalesVOS.stream().map(e -> e.getOrderId()).collect(Collectors.toList());
+            if(null != orderIds && !orderIds.isEmpty()){
+                List<FsStoreOrderItemVO> fsStoreOrderItemVOS = fsStoreOrderItemMapper.selectFsStoreOrderItemListByOrderIds(orderIds);
+                orderItemMap = fsStoreOrderItemVOS.stream()
+                        .collect(Collectors.groupingBy(FsStoreOrderItemVO::getOrderId));
+            }
+        }
+        boolean mapEmpty = orderItemMap.isEmpty();
+        if (null != fsStoreAfterSalesVOS && !fsStoreAfterSalesVOS.isEmpty()) {
+            for (FsStoreAfterSalesVO item : fsStoreAfterSalesVOS) {
+                if(!mapEmpty && orderItemMap.containsKey(item.getOrderId())){
+                    List<FsStoreOrderItemVO> orderItems = orderItemMap.get(item.getOrderId());
+                    for (FsStoreOrderItemVO orderItem : orderItems) {
+                        try {
+                            JSONObject jsO = JSONObject.parseObject(orderItem.getJsonInfo());
+                            item.setProductName(StringUtils.isNotBlank(item.getProductName()) ? item.getProductName() + "," + jsO.getString("productName") : jsO.getString("productName"));
+                            item.setProductBarCode(StringUtils.isNotBlank(item.getProductBarCode()) ? item.getProductBarCode() + "," + jsO.getString("barCode") : jsO.getString("barCode"));
+                            item.setSku(StringUtils.isNotBlank(item.getSku()) ? item.getSku() + "," + jsO.getString("sku") : jsO.getString("sku"));
+                            item.setNum(StringUtils.isNotBlank(item.getNum()) ? item.getNum() + "," + jsO.getString("num") : jsO.getString("num"));
+                            item.setPrice(StringUtils.isNotBlank(item.getPrice()) ? item.getPrice() + "," + jsO.getString("price") : jsO.getString("price"));
+                            item.setBarCode(StringUtils.isNotBlank(item.getBarCode()) ? item.getBarCode() + "," + jsO.getString("barCode") : jsO.getString("barCode"));
+                            item.setCost(StringUtils.isNotBlank(item.getCost()) ? item.getCost() + "," + orderItem.getCost() : orderItem.getCost());
+                            item.setCateName(StringUtils.isNotBlank(item.getCateName()) ? item.getCateName() + "," + orderItem.getCateName() : orderItem.getCateName());
+
+                        } catch (Exception ex) {
+                            logger.error("售后订单商品信息转换异常",ex);
+                        }
+                    }
+                }
+            }
+        }
+        return fsStoreAfterSalesVOS;
     }
 
 //    @Override
@@ -784,7 +822,11 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
                         FsHfpayConfigMapper fsHfpayConfigMapper = SpringUtils.getBean(FsHfpayConfigMapper.class);
                         if (payment.getAppId() != null) {
                             FsHfpayConfig fsHfpayConfig = fsHfpayConfigMapper.selectByAppId(payment.getAppId());
-                            huifuId = fsHfpayConfig.getHuifuId();
+                            if (fsHfpayConfig == null){
+                                huifuId = fsPayConfig.getHuifuId();
+                            }else {
+                                huifuId = fsHfpayConfig.getHuifuId();
+                            }
                         } else {
                             if (("益善缘".equals(cloudHostProper.getCompanyName()))) {
 

+ 53 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreCouponIssueScrmServiceImpl.java

@@ -6,11 +6,13 @@ import java.util.List;
 
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
 import com.fs.hisStore.domain.*;
 import com.fs.hisStore.mapper.*;
 import com.fs.hisStore.param.FsCouponIssueParam;
 import com.fs.hisStore.param.FsStoreCouponReceiveParam;
+import com.fs.hisStore.param.FsUserCouponScrmSendParam;
 import com.fs.hisStore.vo.FsStoreCouponIssueVO;
 import lombok.Synchronized;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -181,5 +183,56 @@ public class FsStoreCouponIssueScrmServiceImpl implements IFsStoreCouponIssueScr
         return fsStoreCouponIssueMapper.selectFsStoreCouponIssueListVO(fsStoreCouponIssue);
     }
 
+    @Override
+    @Transactional
+    public R sendFsUserCoupon(FsUserCouponScrmSendParam param) {
+        Long userId = param.getUserId();
+        if (userId ==null){
+            throw new CustomException("用户不能为空");
+        }
+        FsStoreCouponIssueScrm couponIssue=fsStoreCouponIssueMapper.selectFsStoreCouponIssueById(param.getId());
+        if(couponIssue.getStatus().equals(0)){
+            return R.error("此优惠券已停止领取");
+        }
+        if(couponIssue.getLimitTime().getTime()<new Date().getTime()){
+            return R.error("此优惠券已过期");
+        }
+        //判断用户是否领取过
+        FsUserScrm user= userMapper.selectFsUserById(userId);
+        if(user.getLevel().equals(0)){
+            if(fsStoreCouponIssueUserMapper.checkReceive(userId.toString(),param.getId())>0){
+                return R.error("已领取");
+            }
+        }
+        if(couponIssue.getTotalCount()<=couponIssue.getRemainCount()){
+            return R.error("此优惠券已领完");
+        }
+        FsStoreCouponScrm coupon=couponMapper.selectFsStoreCouponById(couponIssue.getCouponId());
+        //写入领取表
+        FsStoreCouponIssueUserScrm couponIssueUser=new FsStoreCouponIssueUserScrm();
+        couponIssueUser.setUserId(userId);
+        couponIssueUser.setIssueId(param.getId());
+        couponIssueUser.setIsDel(0);
+        couponIssueUser.setCreateTime(new Date());
+        fsStoreCouponIssueUserMapper.insertFsStoreCouponIssueUser(couponIssueUser);
+        FsStoreCouponUserScrm couponUser=new FsStoreCouponUserScrm();
+        couponUser.setCouponId(couponIssue.getCouponId());
+        couponUser.setUserId(userId);
+        couponUser.setCouponTitle(couponIssue.getCouponName());
+        couponUser.setCouponPrice(coupon.getCouponPrice());
+        couponUser.setUseMinPrice(coupon.getUseMinPrice());
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        calendar.add(Calendar.DATE,coupon.getCouponTime().intValue());
+        couponUser.setLimitTime(calendar.getTime());
+        couponUser.setCreateTime(new Date());
+        couponUser.setType("send");
+        couponUser.setStatus(0);
+        fsStoreCouponUserMapper.insertFsStoreCouponUser(couponUser);
+        //更新领取数量
+        fsStoreCouponIssueMapper.updateFsStoreCouponIssueCount(param.getId());
+        return R.ok("领取成功");
+    }
+
 
 }

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

@@ -21,6 +21,7 @@ import com.fs.api.vo.ProductListVO;
 import com.fs.common.config.FSSysConfig;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.event.TemplateBean;
 import com.fs.common.event.TemplateEvent;
@@ -62,9 +63,12 @@ import com.fs.his.dto.FsStoreOrderAmountScrmStatsQueryDto;
 import com.fs.his.dto.FsStoreOrderItemDTO;
 import com.fs.his.enums.FsStoreOrderLogEnum;
 import com.fs.his.enums.FsStoreOrderStatusEnum;
+import com.fs.his.enums.FsUserIntegralLogTypeEnum;
 import com.fs.his.mapper.*;
 import com.fs.his.param.FsStoreOrderSalesParam;
+import com.fs.his.param.FsUserAddIntegralTemplateParam;
 import com.fs.his.service.IFsPrescribeService;
+import com.fs.his.service.IFsUserIntegralLogsService;
 import com.fs.his.service.IFsUserWatchService;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.FsInquiryOrderVO;
@@ -104,6 +108,7 @@ import com.fs.hisStore.domain.*;
 import com.fs.hisStore.enums.*;
 import com.fs.hisStore.service.*;
 import com.fs.system.service.ISysConfigService;
+import com.fs.system.service.ISysDictTypeService;
 import com.fs.wx.miniapp.config.WxMaProperties;
 import com.fs.wx.order.domain.FsWxExpressTask;
 import com.fs.wx.order.dto.*;
@@ -379,6 +384,15 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     @Autowired
     private IFsStoreCartScrmService fsStoreCartScrmService;
 
+    @Autowired
+    private ISysDictTypeService sysDictTypeService;
+    @Autowired
+    private IFsUserIntegralLogsService userIntegralLogsService;
+    @Autowired
+    private FsStoreProductPackageScrmMapper fsStoreProductPackageMapper;
+    @Autowired
+    private FsUserCompanyPackageScrmMapper fsUserCompanyPackageScrmMapper;
+
     @PostConstruct
     public void initErpServiceMap() {
         erpServiceMap = new HashMap<>();
@@ -1594,6 +1608,16 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             if (storeProductPackage.getStatus().equals(0)) {
                 return R.error("此套餐已下架");
             }
+            //只允许销售分享下单
+            if (CloudHostUtils.hasCloudHostName("康年堂")){
+                FsUserCompanyPackageScrm fsUserCompanyPackageScrm = new FsUserCompanyPackageScrm();
+                fsUserCompanyPackageScrm.setUserId(uid);
+                fsUserCompanyPackageScrm.setPackageId(param.getPackageId());
+                List<FsUserCompanyPackageScrm> fsUserCompanyPackageList = fsUserCompanyPackageScrmMapper.selectFsUserCompanyPackageScrmList(fsUserCompanyPackageScrm);
+                if (fsUserCompanyPackageList == null || fsUserCompanyPackageList.isEmpty()) {
+                    return R.error("商品链接无效");
+                }
+            }
             //限购处理
             if (storeProductPackage.getLimitCount() != null && storeProductPackage.getLimitCount() > 0) {
                 //查看是否已购买
@@ -1820,6 +1844,13 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     companyService.addCompanyMoney(order);
                 }
             }
+            //套餐包赠送积分
+            try {
+                sendIntegral(order);
+            } catch (Exception e) {
+                log.error("赠送积分错误:{},订单号:{}", e.getMessage(), order.getOrderCode());
+            }
+
 
             FsErpConfig erpConfig = configUtil.getErpConfig();
             Integer erpType = erpConfig.getErpType();
@@ -1859,6 +1890,43 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         }
     }
 
+    /**
+     * 套餐包赠送积分
+     * @param order
+     */
+    private void sendIntegral(FsStoreOrderScrm order) {
+        //如果是套餐包赠送积分
+        Integer orderCreateType = order.getOrderCreateType();
+        Long packageId = order.getPackageId();
+        if (orderCreateType != null && order.getUserId() != null && packageId !=null) {
+            List<SysDictData> sysDictData = sysDictTypeService.selectDictDataByType("store_order_create_type");
+            if (sysDictData != null && !sysDictData.isEmpty()){
+                String dictValue = "";
+                for (SysDictData dictData : sysDictData) {
+                    String dictLabel = dictData.getDictLabel();
+                    if (StringUtils.isNotBlank(dictLabel) && "套餐".equals(dictLabel)){
+                        dictValue = dictData.getDictValue();
+                        break;
+                    }
+                    if (StringUtils.isNotBlank(dictValue)){
+                        //查询套餐赠送积分
+                        FsStoreProductPackageScrm fsStoreProductPackage = fsStoreProductPackageMapper.selectFsStoreProductPackageById(packageId);
+                        if (fsStoreProductPackage != null && fsStoreProductPackage.getIntegral() != null && fsStoreProductPackage.getIntegral()>0){
+                            //赠送积分
+                            FsUserAddIntegralTemplateParam integralTemplateParam = new FsUserAddIntegralTemplateParam();
+                            integralTemplateParam.setUserId(order.getUserId());
+                            integralTemplateParam.setLogType(FsUserIntegralLogTypeEnum.TYPE_2.getValue()); //消费获取积分
+                            integralTemplateParam.setBusinessId(order.getId().toString());
+                            integralTemplateParam.setPoints(fsStoreProductPackage.getIntegral());
+                            userIntegralLogsService.addIntegralTemplate(integralTemplateParam);
+                        }
+
+                    }
+                }
+            }
+        }
+    }
+
     @Override
     public BigDecimal selectFsStoreOrderByPayPriceCount(FsStoreOrderStatisticsParam param) {
         return fsStoreOrderMapper.selectFsStoreOrderByPayPriceCount(param);
@@ -2007,6 +2075,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
 
 
     @Override
+    @Transactional
     public R createOmsOrder(Long orderId) throws ParseException {
         FsStoreOrderScrm order = fsStoreOrderMapper.selectFsStoreOrderById(orderId);
         FsSysConfig erpConfig = configUtil.generateStructConfigByKey(SysConfigEnum.HIS_CONFIG.getKey(), FsSysConfig.class);
@@ -2406,7 +2475,11 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                         FsHfpayConfigMapper fsHfpayConfigMapper = SpringUtils.getBean(FsHfpayConfigMapper.class);
                         if (payment.getAppId() != null) {
                             FsHfpayConfig fsHfpayConfig = fsHfpayConfigMapper.selectByAppId(payment.getAppId());
-                            huifuId = fsHfpayConfig.getHuifuId();
+                            if (fsHfpayConfig == null){
+                                huifuId = fsPayConfig.getHuifuId();
+                            }else {
+                                huifuId = fsHfpayConfig.getHuifuId();
+                            }
                         } else {
                             if (("益善缘".equals(cloudHostProper.getCompanyName()))) {
 
@@ -2448,6 +2521,8 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 }
             }
         }
+        //退积分 todo
+
         orderStatusService.create(order.getId(), OrderLogEnum.REFUND_ORDER_SUCCESS.getValue(),
                 OrderLogEnum.REFUND_ORDER_SUCCESS.getDesc());
         if (order.getTuiUserId() != null && order.getTuiUserId() > 0) {
@@ -4088,6 +4163,17 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     if (amount != null){
                         payMoney=amount;
                     }
+                    //运费
+                    BigDecimal payPostage = order.getPayPostage();
+                    if (payPostage == null || payPostage.compareTo(BigDecimal.ZERO) <= 0){
+                        payPostage = storeConfig.getPayPostage();
+                        if (payPostage == null){
+                            payPostage = BigDecimal.ZERO;
+                        }
+                        order.setPayPrice(order.getPayPrice().add(payPostage));
+                    }
+                    order.setPayPostage(payPostage);
+                    payMoney = payMoney.add(payPostage);
                     order.setPayMoney(payMoney);
                     order.setPayDelivery(order.getPayPrice().subtract(payMoney) );
                 }
@@ -4587,6 +4673,17 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     if (amount != null){
                         payMoney=amount;
                     }
+                    //运费
+                    BigDecimal payPostage = order.getPayPostage();
+                    if (payPostage == null || payPostage.compareTo(BigDecimal.ZERO) <= 0){
+                        payPostage = storeConfig.getPayPostage();
+                        if (payPostage == null){
+                            payPostage = BigDecimal.ZERO;
+                        }
+                        order.setPayPrice(order.getPayPrice().add(payPostage));
+                    }
+                    order.setPayPostage(payPostage);
+                    payMoney = payMoney.add(payPostage);
                     order.setPayMoney(payMoney);
                     order.setPayDelivery(order.getPayPrice().subtract(payMoney) );
                 }

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

@@ -3,11 +3,16 @@ package com.fs.hisStore.service.impl;
 
 import java.math.BigDecimal;
 
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
+import java.util.stream.Collectors;
 
 
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
@@ -41,8 +46,10 @@ import com.fs.his.service.IFsUserService;
 import com.fs.his.service.IFsUserWxService;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.utils.HttpUtil;
+import com.fs.hisStore.config.StoreConfig;
 import com.fs.hisStore.enums.SysConfigEnum;
 import com.fs.hisStore.param.*;
+import com.fs.hisStore.vo.FsStorePaymentUsetVo;
 import com.fs.huifuPay.domain.HuiFuCreateOrder;
 import com.fs.huifuPay.domain.HuifuCreateOrderResult;
 import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
@@ -53,6 +60,7 @@ import com.fs.hisStore.vo.FsStorePaymentStatisticsVO;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import com.fs.system.service.ISysConfigService;
+import com.fs.utils.TwelveDigitSnowflake;
 import com.fs.wx.miniapp.config.WxMaProperties;
 import com.fs.hisStore.domain.FsUserScrm;
 import com.fs.hisStore.service.IFsUserScrmService;
@@ -71,6 +79,7 @@ import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
 import com.google.common.reflect.TypeToken;
 import com.google.gson.Gson;
+import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -91,6 +100,7 @@ import javax.servlet.http.HttpServletRequest;
  * @date 2022-06-20
  */
 @Service
+@Slf4j
 public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
 {
     Logger logger = LoggerFactory.getLogger(getClass());
@@ -949,4 +959,89 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
             return R.error(result.getResp_desc());
         }
     }
+
+    @Override
+    public R oneClickShipping() {
+        try {
+            StringBuilder builder = new StringBuilder();
+            //获取商城配置
+            String json = configService.selectConfigByKey("store.config");
+            StoreConfig config = JSONUtil.toBean(json, StoreConfig.class);
+            //验证是否开启微信发货
+            if (config.getIsWeChatShipping() != null && config.getIsWeChatShipping()) {
+                //获取支付信息
+                List<FsStorePaymentUsetVo> paymentList = fsStorePaymentMapper.getPaymentUsetInfoList();
+                if (paymentList.isEmpty()) {
+                    return R.ok("操作成功,暂无同步订单信息!");
+                }
+                String uploadTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"))
+                        .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+                Map<String, List<FsStorePaymentUsetVo>> paymentUsetVoMap = paymentList.stream().collect(Collectors.groupingBy(FsStorePaymentUsetVo::getAppId));
+                for (Map.Entry<String, List<FsStorePaymentUsetVo>> entry : paymentUsetVoMap.entrySet()) {
+                    List<String> successList = new ArrayList<>();
+                    List<String> failedList = new ArrayList<>();
+                    String appId = entry.getKey();
+                    List<FsStorePaymentUsetVo> userPayments = entry.getValue();
+                    final WxMaService wxService = WxMaConfiguration.getMaService(appId);
+                    if (!userPayments.isEmpty()) {
+                        for (FsStorePaymentUsetVo v : userPayments) {
+                            // 上传物流信息到微信
+                            if (uploadShippingInfoToWechat(wxService, v, uploadTime)) {
+                                successList.add(v.getBankTransactionId());
+                            }else {
+                                failedList.add(v.getBankTransactionId());
+                            }
+                        }
+                        //批量更新数据
+                        if (!successList.isEmpty()) {
+                            fsStorePaymentMapper.batchUpadte(successList);
+                        }
+                        if(!failedList.isEmpty()){
+                            fsStorePaymentMapper.batchUpadteFailed(failedList);
+                        }
+                    }
+                }
+            }
+            return R.ok(builder.toString().equals("") ? "操作成功!" : builder.toString());
+        } catch (Exception e) {
+            log.error("导入发货单快递信息失败", e);
+            return R.error("导入失败:" + e.getMessage());
+        }
+    }
+
+    private boolean uploadShippingInfoToWechat(WxMaService wxService,
+                                               FsStorePaymentUsetVo dto,
+                                               String uploadTime) {
+        try {
+            WxMaOrderShippingInfoUploadRequest request = new WxMaOrderShippingInfoUploadRequest();
+            OrderKeyBean orderKeyBean = new OrderKeyBean();
+            orderKeyBean.setOrderNumberType(2);
+            orderKeyBean.setTransactionId(dto.getBankTransactionId());//交易订单号ID
+            request.setOrderKey(orderKeyBean);
+            request.setDeliveryMode(1);
+            request.setLogisticsType(4);
+            List<ShippingListBean> shippingList = new ArrayList<>();
+            ShippingListBean shippingListBean = new ShippingListBean();
+            //默认物品信息
+            shippingListBean.setTrackingNo(String.valueOf(new TwelveDigitSnowflake(1).nextId()));
+            shippingListBean.setExpressCompany("FS");
+            shippingListBean.setItemDesc("默认商品");
+            ContactBean contactBean = new ContactBean();
+            contactBean.setReceiverContact(dto.getPhone());
+            shippingListBean.setContact(contactBean);
+
+            shippingList.add(shippingListBean);
+            request.setShippingList(shippingList);
+            request.setUploadTime(uploadTime);
+            // 设置支付者信息
+            PayerBean payerBean = new PayerBean();
+            payerBean.setOpenid(dto.getOpenId());
+            request.setPayer(payerBean);
+            // 上传物流信息
+            return wxService.getWxMaOrderShippingService().upload(request).getErrCode() == 0;
+        } catch (Exception e) {
+            log.error("上传物流信息到微信失败,订单号: {}", dto.getBankTransactionId(), e);
+            return false;
+        }
+    }
 }

+ 37 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductAttrScrmServiceImpl.java

@@ -3,9 +3,12 @@ package com.fs.hisStore.service.impl;
 import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import com.fs.common.constant.LiveKeysConstant;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.hisStore.mapper.FsStoreProductAttrScrmMapper;
 import com.fs.hisStore.domain.FsStoreProductAttrScrm;
 import com.fs.hisStore.service.IFsStoreProductAttrScrmService;
+import lombok.extern.slf4j.Slf4j;
 
 /**
  * 商品属性Service业务层处理
@@ -14,11 +17,27 @@ import com.fs.hisStore.service.IFsStoreProductAttrScrmService;
  * @date 2022-03-15
  */
 @Service
+@Slf4j
 public class FsStoreProductAttrScrmServiceImpl implements IFsStoreProductAttrScrmService
 {
     @Autowired
     private FsStoreProductAttrScrmMapper fsStoreProductAttrMapper;
 
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 清除商品详情缓存
+     * @param productId 商品ID
+     */
+    private void clearProductDetailCache(Long productId) {
+        if (productId != null) {
+            String cacheKey = String.format(LiveKeysConstant.PRODUCT_DETAIL_CACHE, productId);
+            redisCache.deleteObject(cacheKey);
+            log.debug("清除商品详情缓存(属性): productId={}", productId);
+        }
+    }
+
     /**
      * 查询商品属性
      * 
@@ -64,7 +83,10 @@ public class FsStoreProductAttrScrmServiceImpl implements IFsStoreProductAttrScr
     @Override
     public int updateFsStoreProductAttr(FsStoreProductAttrScrm fsStoreProductAttr)
     {
-        return fsStoreProductAttrMapper.updateFsStoreProductAttr(fsStoreProductAttr);
+        int result = fsStoreProductAttrMapper.updateFsStoreProductAttr(fsStoreProductAttr);
+        // 清除对应商品的缓存
+        clearProductDetailCache(fsStoreProductAttr.getProductId());
+        return result;
     }
 
     /**
@@ -76,6 +98,15 @@ public class FsStoreProductAttrScrmServiceImpl implements IFsStoreProductAttrScr
     @Override
     public int deleteFsStoreProductAttrByIds(Long[] ids)
     {
+        // 先查询出对应的商品ID,然后清除缓存
+        if (ids != null && ids.length > 0) {
+            for (Long id : ids) {
+                FsStoreProductAttrScrm attr = fsStoreProductAttrMapper.selectFsStoreProductAttrById(id);
+                if (attr != null) {
+                    clearProductDetailCache(attr.getProductId());
+                }
+            }
+        }
         return fsStoreProductAttrMapper.deleteFsStoreProductAttrByIds(ids);
     }
 
@@ -88,6 +119,11 @@ public class FsStoreProductAttrScrmServiceImpl implements IFsStoreProductAttrScr
     @Override
     public int deleteFsStoreProductAttrById(Long id)
     {
+        // 先查询出对应的商品ID,然后清除缓存
+        FsStoreProductAttrScrm attr = fsStoreProductAttrMapper.selectFsStoreProductAttrById(id);
+        if (attr != null) {
+            clearProductDetailCache(attr.getProductId());
+        }
         return fsStoreProductAttrMapper.deleteFsStoreProductAttrById(id);
     }
 

+ 37 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductAttrValueScrmServiceImpl.java

@@ -3,6 +3,8 @@ package com.fs.hisStore.service.impl;
 import java.util.Collections;
 import java.util.List;
 
+import com.fs.common.constant.LiveKeysConstant;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.hisStore.config.MedicalMallConfig;
 import com.fs.hisStore.param.FsProductAttrValueParam;
 import com.fs.hisStore.param.FsStoreProductAttrValueQueryParam;
@@ -10,6 +12,7 @@ import com.fs.hisStore.param.FsStoreTuiProductAttrValueParam;
 import com.fs.hisStore.vo.FsStoreProductAttrValueQueryVO;
 import com.fs.hisStore.vo.FsStoreProductAttrValueVO;
 import com.fs.hisStore.vo.FsStoreTuiProductAttrValueVO;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.hisStore.mapper.FsStoreProductAttrValueScrmMapper;
@@ -23,6 +26,7 @@ import com.fs.hisStore.service.IFsStoreProductAttrValueScrmService;
  * @date 2022-03-15
  */
 @Service
+@Slf4j
 public class FsStoreProductAttrValueScrmServiceImpl implements IFsStoreProductAttrValueScrmService
 {
     @Autowired
@@ -31,6 +35,21 @@ public class FsStoreProductAttrValueScrmServiceImpl implements IFsStoreProductAt
     @Autowired
     private MedicalMallConfig config;
 
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 清除商品详情缓存
+     * @param productId 商品ID
+     */
+    private void clearProductDetailCache(Long productId) {
+        if (productId != null) {
+            String cacheKey = String.format(LiveKeysConstant.PRODUCT_DETAIL_CACHE, productId);
+            redisCache.deleteObject(cacheKey);
+            log.debug("清除商品详情缓存(属性值): productId={}", productId);
+        }
+    }
+
     /**
      * 查询商品属性值
      *
@@ -76,7 +95,10 @@ public class FsStoreProductAttrValueScrmServiceImpl implements IFsStoreProductAt
     @Override
     public int updateFsStoreProductAttrValue(FsStoreProductAttrValueScrm fsStoreProductAttrValue)
     {
-        return fsStoreProductAttrValueMapper.updateFsStoreProductAttrValue(fsStoreProductAttrValue);
+        int result = fsStoreProductAttrValueMapper.updateFsStoreProductAttrValue(fsStoreProductAttrValue);
+        // 清除对应商品的缓存
+        clearProductDetailCache(fsStoreProductAttrValue.getProductId());
+        return result;
     }
 
     /**
@@ -88,6 +110,15 @@ public class FsStoreProductAttrValueScrmServiceImpl implements IFsStoreProductAt
     @Override
     public int deleteFsStoreProductAttrValueByIds(Long[] ids)
     {
+        // 先查询出对应的商品ID,然后清除缓存
+        if (ids != null && ids.length > 0) {
+            for (Long id : ids) {
+                FsStoreProductAttrValueScrm attrValue = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueById(id);
+                if (attrValue != null) {
+                    clearProductDetailCache(attrValue.getProductId());
+                }
+            }
+        }
         return fsStoreProductAttrValueMapper.deleteFsStoreProductAttrValueByIds(ids);
     }
 
@@ -100,6 +131,11 @@ public class FsStoreProductAttrValueScrmServiceImpl implements IFsStoreProductAt
     @Override
     public int deleteFsStoreProductAttrValueById(Long id)
     {
+        // 先查询出对应的商品ID,然后清除缓存
+        FsStoreProductAttrValueScrm attrValue = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueById(id);
+        if (attrValue != null) {
+            clearProductDetailCache(attrValue.getProductId());
+        }
         return fsStoreProductAttrValueMapper.deleteFsStoreProductAttrValueById(id);
     }
 

+ 32 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductPackageScrmServiceImpl.java

@@ -1,17 +1,24 @@
 package com.fs.hisStore.service.impl;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONUtil;
+import com.fs.common.core.domain.R;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.hisStore.domain.FsStoreProductScrm;
 import com.fs.hisStore.domain.FsStoreProductAttrValueScrm;
+import com.fs.hisStore.domain.FsUserCompanyPackageScrm;
 import com.fs.hisStore.dto.StoreOrderProductDTO;
 import com.fs.hisStore.dto.StorePackageProductDTO;
 import com.fs.hisStore.mapper.FsStoreProductAttrValueScrmMapper;
 import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
+import com.fs.hisStore.mapper.FsUserCompanyPackageScrmMapper;
 import com.fs.hisStore.param.FsStoreProductPackageQueryParam;
+import com.fs.hisStore.param.FsUsePackageScrmSendParam;
 import com.fs.hisStore.vo.FsStoreProductPacketVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -34,6 +41,10 @@ public class FsStoreProductPackageScrmServiceImpl implements IFsStoreProductPack
     private FsStoreProductAttrValueScrmMapper fsStoreProductAttrValueMapper;
     @Autowired
     private FsStoreProductScrmMapper fsStoreProductMapper;
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
+    @Autowired
+    private FsUserCompanyPackageScrmMapper fsUserCompanyPackageScrmMapper;
     /**
      * 查询商品组合套餐
      *
@@ -152,4 +163,25 @@ public class FsStoreProductPackageScrmServiceImpl implements IFsStoreProductPack
         return fsStoreProductPackageMapper.updateFsStoreProductPackages(orderIds,status,companyId);
     }
 
+    @Override
+    public R sendPackage(FsUsePackageScrmSendParam param) {
+        Long companyUserId = param.getCompanyUserId();
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
+        if (companyUser.getCompanyId()==null){
+            return R.error("销售信息错误");
+        }
+        FsUserCompanyPackageScrm fsUserCompanyPackageScrm = new FsUserCompanyPackageScrm();
+        fsUserCompanyPackageScrm.setCompanyId(companyUser.getCompanyId());
+        fsUserCompanyPackageScrm.setCompanyUserId(param.getCompanyUserId());
+        fsUserCompanyPackageScrm.setUserId(param.getUserId());
+        fsUserCompanyPackageScrm.setPackageId(param.getPackageId());
+        List<FsUserCompanyPackageScrm> fsUserCompanyPackageScrms = fsUserCompanyPackageScrmMapper.selectFsUserCompanyPackageScrmList(fsUserCompanyPackageScrm);
+        if (fsUserCompanyPackageScrms == null || fsUserCompanyPackageScrms.isEmpty()){
+            fsUserCompanyPackageScrm.setCreateTime(new Date());
+            fsUserCompanyPackageScrmMapper.insertFsUserCompanyPackageScrm(fsUserCompanyPackageScrm);
+        }
+        return R.ok();
+
+    }
+
 }

+ 322 - 3
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -12,7 +12,9 @@ import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fs.common.BeanCopyUtils;
+import com.fs.common.constant.LiveKeysConstant;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.DateUtils;
@@ -31,7 +33,18 @@ import com.fs.hisStore.domain.*;
 import com.fs.hisStore.enums.CompanyEnum;
 import com.fs.hisStore.mapper.*;
 import com.fs.hisStore.utils.StoreAuditLogUtil;
+import com.fs.live.domain.Live;
 import com.fs.live.domain.LiveGoods;
+import com.fs.live.domain.LiveAutoTask;
+import com.fs.live.domain.LiveLotteryConf;
+import com.fs.live.domain.LiveLotteryProductConf;
+import com.fs.live.mapper.LiveLotteryProductConfMapper;
+import com.fs.live.mapper.LiveMapper;
+import com.fs.live.service.ILiveService;
+import com.fs.live.service.ILiveGoodsService;
+import com.fs.live.service.ILiveAutoTaskService;
+import com.fs.live.service.ILiveLotteryConfService;
+import com.fs.live.vo.LiveGoodsVo;
 import com.fs.statis.dto.ModifyMoreDTO;
 import com.fs.hisStore.dto.ProductArrtDTO;
 import com.fs.hisStore.dto.ProductAttrCountDto;
@@ -40,10 +53,12 @@ import com.fs.hisStore.param.FsStoreProductQueryParam;
 import com.fs.hisStore.service.IFsStoreProductAttrValueScrmService;
 import com.fs.hisStore.vo.*;
 import com.fs.statis.dto.ProductAuditDTO;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import com.fs.hisStore.service.IFsStoreProductScrmService;
 import org.springframework.transaction.annotation.Propagation;
@@ -57,8 +72,10 @@ import org.springframework.transaction.annotation.Transactional;
  */
 @Service
 @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
+@Slf4j
 public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
 {
+
     @Autowired
     private FsStoreProductScrmMapper fsStoreProductMapper;
     @Autowired
@@ -102,6 +119,51 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
     @Autowired
     private FsStoreProductCategoryScrmMapper fsStoreProductCategoryScrmMapper;
 
+    @Autowired
+    private ILiveService liveService;
+
+    @Autowired
+    private LiveMapper liveMapper;
+
+    @Autowired
+    private ILiveGoodsService liveGoodsService;
+
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
+
+    @Autowired
+    private ILiveLotteryConfService liveLotteryConfService;
+
+    @Autowired
+    private LiveLotteryProductConfMapper liveLotteryProductConfMapper;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 清除商品详情缓存
+     * @param productId 商品ID
+     */
+    private void clearProductDetailCache(Long productId) {
+        if (productId != null) {
+            String cacheKey = String.format(LiveKeysConstant.PRODUCT_DETAIL_CACHE, productId);
+            redisCache.deleteObject(cacheKey);
+            log.debug("清除商品详情缓存: productId={}", productId);
+        }
+    }
+
+    /**
+     * 批量清除商品详情缓存
+     * @param productIds 商品ID数组
+     */
+    private void clearProductDetailCache(Long[] productIds) {
+        if (productIds != null && productIds.length > 0) {
+            for (Long productId : productIds) {
+                clearProductDetailCache(productId);
+            }
+        }
+    }
+
     /**
      * 查询商品
      *
@@ -156,7 +218,10 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         if(1 == fsStoreProduct.getIsShow() && "1".equals(fsStoreProduct.getIsAudit())){
             fsStoreProduct.setIsAudit("0");
         }
-        return fsStoreProductMapper.updateFsStoreProduct(fsStoreProduct);
+        int result = fsStoreProductMapper.updateFsStoreProduct(fsStoreProduct);
+        // 清除缓存
+        clearProductDetailCache(fsStoreProduct.getProductId());
+        return result;
     }
 
     /**
@@ -169,7 +234,256 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
     public int deleteFsStoreProductByIds(Long[] productIds)
     {
         storeAuditLogUtil.addBatchAuditArray(productIds, "", "");
-        return fsStoreProductMapper.deleteFsStoreProductByIds(productIds);
+        log.info("批量删除商品:{}", productIds);
+        int result = fsStoreProductMapper.deleteFsStoreProductByIds(productIds);
+        
+        // 清除缓存
+        clearProductDetailCache(productIds);
+        
+        // 异步处理商品删除联动逻辑
+        if (result > 0) {
+            handleProductDeleteAsync(productIds);
+        }
+        
+        return result;
+    }
+
+    /**
+     * 异步处理商品删除联动逻辑
+     * 删除所有未直播、直播中和直播回放的直播间中关联的商品、定时任务、抽奖等
+     *
+     * @param productIds 被删除的商品ID数组
+     */
+    @Async
+    public void handleProductDeleteAsync(Long[] productIds) {
+        try {
+            log.info("开始异步处理商品删除联动,商品IDs: {}", Arrays.toString(productIds));
+            
+            // 查询所有未直播(1)、直播中(2)和直播回放(4)的直播间
+            // 使用 LiveMapper 查询状态为1,2,4的直播间(包括所有类型)
+            List<Live> allLiveList = liveMapper.liveListAll();
+            // 同时查询其他类型的直播间(如果liveListAll只查询类型2,3,我们需要补充查询类型1的)
+            // 为了确保完整性,我们使用 selectLiveList 方法查询所有状态为1,2,4的直播间
+            Live queryLive = new Live();
+            List<Live> allLiveList2 = liveService.selectLiveList(queryLive);
+            // 合并并去重,过滤出状态为1,2,4的直播间
+            Set<Long> liveIdSet = new HashSet<>();
+            List<Live> targetLiveList = new ArrayList<>();
+            for (Live live : allLiveList) {
+                if (live.getLiveId() != null && !liveIdSet.contains(live.getLiveId())) {
+                    liveIdSet.add(live.getLiveId());
+                    targetLiveList.add(live);
+                }
+            }
+            for (Live live : allLiveList2) {
+                if (live.getStatus() != null && (live.getStatus() == 1 || live.getStatus() == 2 || live.getStatus() == 4)) {
+                    if (live.getLiveId() != null && !liveIdSet.contains(live.getLiveId())) {
+                        liveIdSet.add(live.getLiveId());
+                        targetLiveList.add(live);
+                    }
+                }
+            }
+            
+            if (targetLiveList.isEmpty()) {
+                log.info("没有找到需要处理的直播间");
+                return;
+            }
+            
+            log.info("找到 {} 个需要处理的直播间", targetLiveList.size());
+            
+            // 遍历每个被删除的商品
+            for (Long productId : productIds) {
+                processProductDeleteForLives(productId, targetLiveList);
+            }
+            
+            log.info("商品删除联动处理完成");
+        } catch (Exception e) {
+            log.error("异步处理商品删除联动失败", e);
+        }
+    }
+
+    /**
+     * 处理单个商品在指定直播间列表中的删除联动
+     *
+     * @param productId 商品ID
+     * @param liveList 直播间列表
+     */
+    private void processProductDeleteForLives(Long productId, List<Live> liveList) {
+        for (Live live : liveList) {
+            try {
+                Long liveId = live.getLiveId();
+                if (liveId == null) {
+                    continue;
+                }
+                
+                // 1. 删除直播商品
+                deleteLiveGoodsByProductId(productId, liveId);
+                
+                // 2. 删除直播定时任务(直播上下架、直播卡片)
+                deleteLiveAutoTasksByProductId(productId, liveId);
+                
+                // 3. 删除直播抽奖(产品关联被删除的商品)
+                deleteLiveLotteryProductConfByProductId(productId, liveId);
+                
+                // 4. 删除直播定时任务(抽奖,里面关联了这个商品的)
+                deleteLiveAutoTasksByLotteryProductId(productId, liveId);
+                
+            } catch (Exception e) {
+                log.error("处理直播间 {} 的商品 {} 删除联动失败", live.getLiveId(), productId, e);
+            }
+        }
+    }
+
+    /**
+     * 删除直播商品
+     *
+     * @param productId 商品ID
+     * @param liveId 直播间ID
+     */
+    private void deleteLiveGoodsByProductId(Long productId, Long liveId) {
+        try {
+            LiveGoods queryGoods = new LiveGoods();
+            queryGoods.setProductId(productId);
+            queryGoods.setLiveId(liveId);
+            List<LiveGoods> goodsList = liveGoodsService.selectLiveGoodsList(queryGoods);
+            
+            if (!goodsList.isEmpty()) {
+                Long[] goodsIds = goodsList.stream()
+                        .map(LiveGoods::getGoodsId)
+                        .toArray(Long[]::new);
+                liveGoodsService.deleteLiveGoodsByGoodsIds(goodsIds);
+                log.info("删除直播间 {} 的商品 {} 相关的直播商品 {} 个", liveId, productId, goodsIds.length);
+            }
+        } catch (Exception e) {
+            log.error("删除直播商品失败,productId: {}, liveId: {}", productId, liveId, e);
+        }
+    }
+
+    /**
+     * 删除直播定时任务(直播上下架、直播卡片)
+     *
+     * @param productId 商品ID
+     * @param liveId 直播间ID
+     */
+    private void deleteLiveAutoTasksByProductId(Long productId, Long liveId) {
+        try {
+            LiveAutoTask queryTask = new LiveAutoTask();
+            queryTask.setLiveId(liveId);
+            List<LiveAutoTask> taskList = liveAutoTaskService.selectLiveAutoTaskList(queryTask);
+            
+            List<Long> taskIdsToDelete = new ArrayList<>();
+            
+            for (LiveAutoTask task : taskList) {
+                // 任务类型:1-定时推送卡片商品 6-自动上下架
+                if (task.getTaskType() != null && (task.getTaskType() == 1L || task.getTaskType() == 6L)) {
+                    try {
+                        if (task.getTaskType() == 1L) {
+                            // 商品推荐任务
+                            LiveGoodsVo liveGoodsVo = JSON.parseObject(task.getContent(), LiveGoodsVo.class);
+                            if (liveGoodsVo != null && productId.equals(liveGoodsVo.getProductId())) {
+                                taskIdsToDelete.add(task.getId());
+                            }
+                        } else if (task.getTaskType() == 6L) {
+                            // 商品上下架任务
+                            JSONObject jsonObject = JSON.parseObject(task.getContent());
+                            Long taskProductId = jsonObject.getLong("productId");
+                            if (taskProductId != null && productId.equals(taskProductId)) {
+                                taskIdsToDelete.add(task.getId());
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warn("解析自动化任务content失败,taskId: {}, error: {}", task.getId(), e.getMessage());
+                    }
+                }
+            }
+            
+            if (!taskIdsToDelete.isEmpty()) {
+                Long[] ids = taskIdsToDelete.toArray(new Long[0]);
+                liveAutoTaskService.deleteLiveAutoTaskByIds(ids);
+                log.info("删除直播间 {} 的商品 {} 相关的定时任务 {} 个", liveId, productId, taskIdsToDelete.size());
+            }
+        } catch (Exception e) {
+            log.error("删除直播定时任务失败,productId: {}, liveId: {}", productId, liveId, e);
+        }
+    }
+
+    /**
+     * 删除直播抽奖(产品关联被删除的商品)
+     *
+     * @param productId 商品ID
+     * @param liveId 直播间ID
+     */
+    private void deleteLiveLotteryProductConfByProductId(Long productId, Long liveId) {
+        try {
+            LiveLotteryProductConf queryConf = new LiveLotteryProductConf();
+            queryConf.setProductId(productId);
+            queryConf.setLiveId(liveId);
+            List<LiveLotteryProductConf> confList = liveLotteryProductConfMapper.selectLiveLotteryProductConfList(queryConf);
+            
+            if (!confList.isEmpty()) {
+                Long[] ids = confList.stream()
+                        .map(LiveLotteryProductConf::getId)
+                        .toArray(Long[]::new);
+                liveLotteryProductConfMapper.deleteLiveLotteryProductConfByIds(ids);
+                log.info("删除直播间 {} 的商品 {} 相关的抽奖商品配置 {} 个", liveId, productId, ids.length);
+            }
+        } catch (Exception e) {
+            log.error("删除直播抽奖商品配置失败,productId: {}, liveId: {}", productId, liveId, e);
+        }
+    }
+
+    /**
+     * 删除直播定时任务(抽奖,里面关联了这个商品的)
+     *
+     * @param productId 商品ID
+     * @param liveId 直播间ID
+     */
+    private void deleteLiveAutoTasksByLotteryProductId(Long productId, Long liveId) {
+        try {
+            // 查询该直播间下所有包含该商品的抽奖配置
+            LiveLotteryProductConf queryConf = new LiveLotteryProductConf();
+            queryConf.setProductId(productId);
+            queryConf.setLiveId(liveId);
+            List<LiveLotteryProductConf> confList = liveLotteryProductConfMapper.selectLiveLotteryProductConfList(queryConf);
+            
+            if (confList.isEmpty()) {
+                return;
+            }
+            
+            // 获取所有相关的抽奖ID
+            Set<Long> lotteryIds = confList.stream()
+                    .map(LiveLotteryProductConf::getLotteryId)
+                    .collect(Collectors.toSet());
+            
+            // 查询该直播间的所有定时任务
+            LiveAutoTask queryTask = new LiveAutoTask();
+            queryTask.setLiveId(liveId);
+            List<LiveAutoTask> taskList = liveAutoTaskService.selectLiveAutoTaskList(queryTask);
+            
+            List<Long> taskIdsToDelete = new ArrayList<>();
+            
+            for (LiveAutoTask task : taskList) {
+                // 任务类型:4-抽奖
+                if (task.getTaskType() != null && task.getTaskType() == 4L) {
+                    try {
+                        LiveLotteryConf liveLotteryConf = JSON.parseObject(task.getContent(), LiveLotteryConf.class);
+                        if (liveLotteryConf != null && lotteryIds.contains(liveLotteryConf.getLotteryId())) {
+                            taskIdsToDelete.add(task.getId());
+                        }
+                    } catch (Exception e) {
+                        log.warn("解析自动化任务content失败,taskId: {}, error: {}", task.getId(), e.getMessage());
+                    }
+                }
+            }
+            
+            if (!taskIdsToDelete.isEmpty()) {
+                Long[] ids = taskIdsToDelete.toArray(new Long[0]);
+                liveAutoTaskService.deleteLiveAutoTaskByIds(ids);
+                log.info("删除直播间 {} 的商品 {} 相关的抽奖定时任务 {} 个", liveId, productId, taskIdsToDelete.size());
+            }
+        } catch (Exception e) {
+            log.error("删除抽奖定时任务失败,productId: {}, liveId: {}", productId, liveId, e);
+        }
     }
 
     /**
@@ -181,7 +495,10 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
     @Override
     public int deleteFsStoreProductById(Long productId)
     {
-        return fsStoreProductMapper.deleteFsStoreProductById(productId);
+        int result = fsStoreProductMapper.deleteFsStoreProductById(productId);
+        // 清除缓存
+        clearProductDetailCache(productId);
+        return result;
     }
 
     @Override
@@ -388,6 +705,8 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
                 product.setIsAudit("1");
             }
             fsStoreProductMapper.updateFsStoreProduct(product);
+            // 清除缓存
+            clearProductDetailCache(product.getProductId());
             if (param.getSpecType().equals(0)) {
                 ProductArrtDTO fromatDetailDto = ProductArrtDTO.builder()
                         .value("规格")

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

@@ -37,6 +37,7 @@ import com.fs.his.domain.FsUserBill;
 import com.fs.his.domain.FsUserWx;
 import com.fs.his.service.IFsUserWxService;
 import com.fs.his.vo.OptionsVO;
+import com.fs.hisStore.vo.*;
 import com.fs.live.domain.LiveOrder;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
@@ -57,10 +58,6 @@ import com.fs.hisStore.service.IFsUserCompanyUserScrmService;
 import com.fs.hisStore.service.IFsUserProjectTagScrmService;
 import com.fs.hisStore.service.IFsUserScrmService;
 import com.fs.hisStore.service.cache.IFsUserCourseCountCacheService;
-import com.fs.hisStore.vo.FSUserVO;
-import com.fs.hisStore.vo.FsCompanyUserListQueryVO;
-import com.fs.hisStore.vo.FsUserLastCount;
-import com.fs.hisStore.vo.FsUserTuiVO;
 import com.fs.hisStore.vo.h5.*;
 import com.fs.system.mapper.SysDictDataMapper;
 import com.github.pagehelper.PageHelper;
@@ -1157,4 +1154,14 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
             throw new RuntimeException(e);
         }
     }
+
+    @Override
+    public void incPayMoney(BigDecimal payMoney, Long userId) {
+        fsUserMapper.incPayMoney(payMoney, userId);
+    }
+
+    @Override
+    public List<UserScrmVo> selectUserVOList(String phone) {
+        return fsUserMapper.selectUserVOList(phone);
+    }
 }

+ 43 - 3
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreAfterSalesVO.java

@@ -100,7 +100,47 @@ public class FsStoreAfterSalesVO implements Serializable
     @Excel(name = "提交时间",width =30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     private Date createTime;
 
-
-
-
+    private String itemJson;
+
+    @Excel(name = "用户名称")
+    private String userName;
+    @Excel(name ="产品名称")
+    private String productName;
+    @Excel(name ="产品编码")
+    private String productBarCode;
+    @Excel(name ="规格")
+    private String sku;
+    @Excel(name ="产品数量")
+    private String num;
+    @Excel(name ="产品价格")
+    private String price;
+    @Excel(name ="产品编码")
+    private String barCode;
+    @Excel(name="额外运费")
+    private BigDecimal totalPostage;
+    @Excel(name="实付金额")
+    private BigDecimal payPrice;
+    //    @Excel(name="收货人姓名")
+//    private String realName;
+    @Excel(name ="详细地址")
+    private  String userAddress;
+    @Excel(name ="支付时间" ,dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date orderPayTime;
+    @Excel(name ="银行交易流水号")
+    private String bankSerialNo;
+
+    private String bankTransactionId;
+
+    private Long orderId;
+
+    private String cost;
+
+    private BigDecimal payMoney;
+
+    private String cateName;
+    private Date orderCreateTime;
+
+    private String orderDeliverySn;
+    private String orderDeliveryName;
+    private String orderDeliveryId;
 }

+ 133 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportRefundZMVO.java

@@ -0,0 +1,133 @@
+package com.fs.hisStore.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;
+
+/**
+ * @author MixLiu
+ * @date 2025/12/4 上午11:43)
+ */
+
+@Data
+public class FsStoreOrderItemExportRefundZMVO implements Serializable  {
+
+
+    private Long itemId;
+
+    /** 订单号 */
+    @Excel(name = "订单号",sort = 1)
+    private String orderCode;
+
+    @Excel(name = "订单状态", dictType = "store_order_status",sort = 10)
+    private String status;
+
+    @Excel(name = "会员ID" ,sort = 20)
+    private Long userId;
+
+    @Excel(name = "产品名称",sort = 30)
+    private String productName;
+
+    @Excel(name = "产品编码",sort =40)
+    private String barCode;
+
+
+    @Excel(name = "规格",sort =50)
+    private String sku;
+
+    @Excel(name = "产品数量",sort =60)
+    private String num;
+
+
+    @Excel(name = "产品价格",sort =70)
+    private String price;
+
+    @Excel(name = "成本价",sort =80)
+    private String cost;
+    @Excel(name = "结算价",sort =90)
+    private BigDecimal FPrice;
+
+    @Excel(name = "实付金额",sort =91)
+    private BigDecimal payMoney;
+
+    @Excel(name = "额外运费",sort =100)
+    private BigDecimal payPostage;
+    private Integer totalNum;
+    @Excel(name = "商品分类",sort =100)
+    private String cateName;
+
+
+    private String jsonInfo;
+
+    /** 用户姓名 */
+    @Excel(name = "收货人姓名",sort =110)
+    private String realName;
+
+    /** 用户电话 */
+    @Excel(name = "收货人电话",sort =120)
+    private String userPhone;
+
+    /** 详细地址 */
+    @Excel(name = "详细地址",sort =130)
+    private String userAddress;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "下单时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss",sort = 140)
+    private Date createTime;
+    /** 支付时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss",sort = 150)
+    private Date payTime;
+
+    /** 快递公司编号 */
+    @Excel(name = "快递公司编号",sort = 160)
+    private String deliverySn;
+
+    /** 快递名称/送货人姓名 */
+    @Excel(name = "快递公司",sort = 170)
+    private String deliveryName;
+
+    /** 快递单号/手机号 */
+    @Excel(name = "快递单号",sort = 180)
+    private String deliveryId;
+
+    @Excel(name = "所属公司",sort = 190)
+    private String companyName;
+    @Excel(name = "所属销售",sort = 200)
+    private String companyUserNickName;
+
+    @Excel(name = "套餐名称",sort = 210)
+    private String packageName;
+
+    @Excel(name = "组合码",sort = 210)
+    private String groupBarCode;
+
+    @Excel(name = "是否上传凭证 0:未上传 1:已上传",sort = 210)
+    private Integer isUpload;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "上传时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss",sort = 220)
+    private Date uploadTime;
+
+    @Excel(name = "归属档期",sort = 230)
+    private String scheduleName;
+
+    //银行交易流水号
+    @Excel(name = "银行交易流水号",sort = 240)
+    private String bankTransactionId;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "退款时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss",sort = 220)
+    private Date refundTime;
+
+    @Excel(name = "退款数量" ,sort = 230)
+    private Integer afterSalesNumber;
+
+    @Excel(name = "退款金额" ,sort = 240)
+    private BigDecimal refundMoney;
+
+}

+ 3 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportVO.java

@@ -45,6 +45,9 @@ public class FsStoreOrderItemExportVO implements Serializable
     private BigDecimal cost;
     @Excel(name = "结算价")
     private BigDecimal FPrice;
+
+    private BigDecimal payMoney;
+
     @Excel(name = "额外运费")
     private BigDecimal payPostage;
     private Integer totalNum;

+ 124 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportZMVO.java

@@ -0,0 +1,124 @@
+package com.fs.hisStore.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;
+
+/**
+ * @author MixLiu
+ * @date 2025/12/4 上午11:43)
+ */
+
+@Data
+public class FsStoreOrderItemExportZMVO implements Serializable  {
+
+
+    private Long itemId;
+
+    /** 订单号 */
+    @Excel(name = "订单号",sort = 1)
+    private String orderCode;
+
+    @Excel(name = "订单状态", dictType = "store_order_status",sort = 10)
+    private String status;
+
+    @Excel(name = "会员ID" ,sort = 20)
+    private Long userId;
+
+    @Excel(name = "产品名称",sort = 30)
+    private String productName;
+
+    @Excel(name = "产品编码",sort =40)
+    private String barCode;
+
+
+    @Excel(name = "规格",sort =50)
+    private String sku;
+
+    @Excel(name = "产品数量",sort =60)
+    private Integer num;
+
+
+    @Excel(name = "产品价格",sort =70)
+    private BigDecimal price;
+
+    @Excel(name = "成本价",sort =80)
+    private BigDecimal cost;
+    @Excel(name = "结算价",sort =90)
+    private BigDecimal FPrice;
+
+    @Excel(name = "实付金额",sort =91)
+    private BigDecimal payMoney;
+
+    @Excel(name = "额外运费",sort =100)
+    private BigDecimal payPostage;
+    private Integer totalNum;
+    @Excel(name = "商品分类",sort =100)
+    private String cateName;
+
+
+    private String jsonInfo;
+
+    /** 用户姓名 */
+    @Excel(name = "收货人姓名",sort =110)
+    private String realName;
+
+    /** 用户电话 */
+    @Excel(name = "收货人电话",sort =120)
+    private String userPhone;
+
+    /** 详细地址 */
+    @Excel(name = "详细地址",sort =130)
+    private String userAddress;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "下单时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss",sort = 140)
+    private Date createTime;
+    /** 支付时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss",sort = 150)
+    private Date payTime;
+
+    /** 快递公司编号 */
+    @Excel(name = "快递公司编号",sort = 160)
+    private String deliverySn;
+
+    /** 快递名称/送货人姓名 */
+    @Excel(name = "快递公司",sort = 170)
+    private String deliveryName;
+
+    /** 快递单号/手机号 */
+    @Excel(name = "快递单号",sort = 180)
+    private String deliveryId;
+
+    @Excel(name = "所属公司",sort = 190)
+    private String companyName;
+    @Excel(name = "所属销售",sort = 200)
+    private String companyUserNickName;
+
+    @Excel(name = "套餐名称",sort = 210)
+    private String packageName;
+
+    @Excel(name = "组合码",sort = 210)
+    private String groupBarCode;
+
+    @Excel(name = "是否上传凭证 0:未上传 1:已上传",sort = 210)
+    private Integer isUpload;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "上传时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss",sort = 220)
+    private Date uploadTime;
+
+    @Excel(name = "归属档期",sort = 230)
+    private String scheduleName;
+
+    //银行交易流水号
+    @Excel(name = "银行交易流水号",sort = 240)
+    private String bankTransactionId;
+
+
+}

Some files were not shown because too many files changed in this diff