Ver código fonte

Merge remote-tracking branch 'refs/remotes/origin/红德堂' into 红德堂-test

# Conflicts:
#	fs-service/src/main/java/com/fs/his/domain/FsUser.java
#	fs-service/src/main/java/com/fs/his/service/IFsUserService.java
#	fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
#	fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
wangxy 1 mês atrás
pai
commit
33b6df50cb
100 arquivos alterados com 5088 adições e 439 exclusões
  1. 46 3
      fs-admin/src/main/java/com/fs/course/controller/FsUserVideoController.java
  2. 37 6
      fs-admin/src/main/java/com/fs/course/controller/FsVideoResourceController.java
  3. 113 0
      fs-admin/src/main/java/com/fs/task/FsCompanyTask.java
  4. 5 5
      fs-company-app/pom.xml
  5. 20 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  6. 5 5
      fs-company/pom.xml
  7. 1 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopController.java
  8. 8 8
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java
  9. 5 0
      fs-company/src/main/java/com/fs/company/controller/qw/SopUserLogsController.java
  10. 41 0
      fs-company/src/main/java/com/fs/user/FsUserAdminController.java
  11. 5 1
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  12. 2 1
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  13. 11 8
      fs-service/pom.xml
  14. 13 13
      fs-service/src/main/java/com/cloud/host/CloudHostConfig.java
  15. 8 1
      fs-service/src/main/java/com/fs/company/mapper/CompanyRedPacketBalanceDeductionRecordMapper.java
  16. 6 0
      fs-service/src/main/java/com/fs/company/service/ICompanyRedPacketBalanceDeductionRecordService.java
  17. 4 0
      fs-service/src/main/java/com/fs/company/service/ICompanyService.java
  18. 10 1
      fs-service/src/main/java/com/fs/company/service/impl/CompanyRedPacketBalanceDeductionRecordServiceImpl.java
  19. 49 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  20. 8 0
      fs-service/src/main/java/com/fs/config/cloud/CloudHostProper.java
  21. 30 0
      fs-service/src/main/java/com/fs/core/config/VolcEngineConfiguration.java
  22. 36 0
      fs-service/src/main/java/com/fs/core/service/impl/STSService.java
  23. 3 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCourseVideo.java
  24. 5 0
      fs-service/src/main/java/com/fs/course/domain/FsVideoResource.java
  25. 1 1
      fs-service/src/main/java/com/fs/course/dto/BatchSendCourseDTO.java
  26. 4 1
      fs-service/src/main/java/com/fs/course/dto/FsOrderDeliveryNoteDTO.java
  27. 18 0
      fs-service/src/main/java/com/fs/course/dto/OrderOpenIdTransDTO.java
  28. 6 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java
  29. 18 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseTrafficLogMapper.java
  30. 18 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java
  31. 58 0
      fs-service/src/main/java/com/fs/course/service/HsyAssumeRoleService.java
  32. 2 0
      fs-service/src/main/java/com/fs/course/service/IFsCoursePlaySourceConfigService.java
  33. 2 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseTrafficLogService.java
  34. 21 1
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  35. 6 0
      fs-service/src/main/java/com/fs/course/service/IFsUserVideoService.java
  36. 3 7
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  37. 6 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCoursePlaySourceConfigServiceImpl.java
  38. 82 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseTrafficLogServiceImpl.java
  39. 3 3
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  40. 522 194
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  41. 122 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserVideoServiceImpl.java
  42. 47 0
      fs-service/src/main/java/com/fs/course/service/impl/VodUploadMediaProcessListener.java
  43. 1 0
      fs-service/src/main/java/com/fs/erp/domain/ErpOrder.java
  44. 7 0
      fs-service/src/main/java/com/fs/erp/service/IErpOrderService.java
  45. 763 26
      fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java
  46. 29 0
      fs-service/src/main/java/com/fs/erp/service/impl/ErpOrderServiceImpl.java
  47. 163 0
      fs-service/src/main/java/com/fs/erp/service/impl/HzOMSErpOrderServiceImpl.java
  48. 282 4
      fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java
  49. 176 0
      fs-service/src/main/java/com/fs/erp/service/impl/K9OrderScrmServiceImpl.java
  50. 206 0
      fs-service/src/main/java/com/fs/erp/service/impl/WdtErpOrderServiceImpl.java
  51. 17 3
      fs-service/src/main/java/com/fs/his/domain/FsIntegralOrder.java
  52. 73 0
      fs-service/src/main/java/com/fs/his/domain/FsIntegralOrderDf.java
  53. 47 0
      fs-service/src/main/java/com/fs/his/domain/FsIntegralOrderLogs.java
  54. 28 0
      fs-service/src/main/java/com/fs/his/domain/FsUser.java
  55. 3 1
      fs-service/src/main/java/com/fs/his/dto/PayloadDTO.java
  56. 64 0
      fs-service/src/main/java/com/fs/his/mapper/FsIntegralOrderDfMapper.java
  57. 62 0
      fs-service/src/main/java/com/fs/his/mapper/FsIntegralOrderLogsMapper.java
  58. 7 1
      fs-service/src/main/java/com/fs/his/mapper/FsIntegralOrderMapper.java
  59. 24 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  60. 2 0
      fs-service/src/main/java/com/fs/his/param/WxSendRedPacketParam.java
  61. 62 0
      fs-service/src/main/java/com/fs/his/service/IFsIntegralOrderDfService.java
  62. 62 0
      fs-service/src/main/java/com/fs/his/service/IFsIntegralOrderLogsService.java
  63. 14 0
      fs-service/src/main/java/com/fs/his/service/IFsUserService.java
  64. 93 0
      fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderDfServiceImpl.java
  65. 92 0
      fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderLogsServiceImpl.java
  66. 6 16
      fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java
  67. 147 21
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  68. 1 1
      fs-service/src/main/java/com/fs/his/vo/FsCourseReportVO.java
  69. 32 0
      fs-service/src/main/java/com/fs/his/vo/FsIntegralOrderExcelVO.java
  70. 3 0
      fs-service/src/main/java/com/fs/hisStore/config/StoreConfig.java
  71. 2 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsStoreOrderScrm.java
  72. 27 12
      fs-service/src/main/java/com/fs/hisStore/domain/FsUserBillScrm.java
  73. 36 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsUserScrm.java
  74. 1 0
      fs-service/src/main/java/com/fs/hisStore/enums/OrderInfoEnum.java
  75. 2 1
      fs-service/src/main/java/com/fs/hisStore/enums/ShipperCodeEnum.java
  76. 19 2
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
  77. 28 1
      fs-service/src/main/java/com/fs/hisStore/mapper/FsUserScrmMapper.java
  78. 1 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreConfirmPackageIdOrderParam.java
  79. 6 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStorePackageOrderCreateParam.java
  80. 24 0
      fs-service/src/main/java/com/fs/hisStore/param/FsUserCouponScrmSendParam.java
  81. 26 1
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java
  82. 8 8
      fs-service/src/main/java/com/fs/hisStore/service/IFsUserBillScrmService.java
  83. 27 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsUserScrmService.java
  84. 539 49
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  85. 9 3
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserBillScrmServiceImpl.java
  86. 1 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserExtractScrmServiceImpl.java
  87. 120 14
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserScrmServiceImpl.java
  88. 38 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderCodeOpenIdVo.java
  89. 84 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderDeliveryNoteExportVO.java
  90. 2 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreProductListQueryVO.java
  91. 11 0
      fs-service/src/main/java/com/fs/hisStore/vo/UserScrmVo.java
  92. 5 0
      fs-service/src/main/java/com/fs/im/service/OpenIMService.java
  93. 54 2
      fs-service/src/main/java/com/fs/im/service/impl/OpenIMServiceImpl.java
  94. 26 4
      fs-service/src/main/java/com/fs/ipad/IpadSendUtils.java
  95. 2 1
      fs-service/src/main/java/com/fs/ipad/vo/BaseVo.java
  96. 16 1
      fs-service/src/main/java/com/fs/live/domain/LiveOrder.java
  97. 2 1
      fs-service/src/main/java/com/fs/live/domain/LiveOrderPayment.java
  98. 85 0
      fs-service/src/main/java/com/fs/live/dto/LiveOrderCustomerExportDTO.java
  99. 4 1
      fs-service/src/main/java/com/fs/live/dto/LiveOrderDeliveryNoteDTO.java
  100. 7 4
      fs-service/src/main/java/com/fs/live/mapper/LiveAfterSalesMapper.java

+ 46 - 3
fs-admin/src/main/java/com/fs/course/controller/FsUserVideoController.java

@@ -13,6 +13,7 @@ import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.domain.FsUserVideo;
 import com.fs.course.domain.FsVideoResource;
 import com.fs.course.param.*;
+import com.fs.course.service.HsyAssumeRoleService;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsUserVideoService;
 import com.fs.course.service.IFsVideoResourceService;
@@ -23,17 +24,18 @@ import com.fs.his.service.IFsPackageService;
 import com.fs.his.vo.FsPackageVO;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
+import com.volcengine.model.response.AssumeRoleResponse;
+import com.volcengine.service.vod.IVodService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.FilenameUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.*;
-import java.util.Date;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
 
 /**
  * 课堂视频Controller
@@ -60,6 +62,12 @@ public class FsUserVideoController extends BaseController
     @Autowired
     private IFsUserCourseVideoService fsUserCourseVideoService;
 
+
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+    @Autowired
+    private HsyAssumeRoleService hsyAssumeRoleService;
     /**
      * 查询课堂视频列表
      */
@@ -389,4 +397,39 @@ public class FsUserVideoController extends BaseController
             return R.error("服务器内部错误");
         }
     }
+
+
+    @PreAuthorize("@ss.hasPermi('course:userVideo:uploadUserVideo')")
+    @Log(title = "课堂视频", businessType = BusinessType.INSERT)
+    @PostMapping("/uploadUserVideo")
+    public AjaxResult upload(@RequestParam("file") MultipartFile file, @RequestParam("uploadId")String uploadId) {
+        String vid = fsUserVideoService.uploadVideo(file, uploadId);
+
+        Map<String, Object> res = new HashMap<>();
+        res.put("uploadId", uploadId);
+        res.put("vid", vid);
+        return new AjaxResult(200, "上传成功", res);
+    }
+
+
+    @GetMapping("/uploadProgress")
+    public AjaxResult getUploadProgress(@RequestParam String uploadId) {
+        Object percentObj = redisTemplate.opsForValue()
+                .get("vod:upload:" + uploadId);
+
+        int percent = 0;
+        if (percentObj != null) {
+            percent = Integer.parseInt(percentObj.toString());
+        }
+
+        Map<String, Object> res = new HashMap<>();
+        res.put("percent", percent);
+        return AjaxResult.success(res);
+    }
+
+    @GetMapping("/HsyAssumeRoleService")
+    public AjaxResult HsyAssumeRoleService() throws Exception {
+        AssumeRoleResponse roleResponse = hsyAssumeRoleService.getRoleResponse();
+        return AjaxResult.success(roleResponse);
+    }
 }

+ 37 - 6
fs-admin/src/main/java/com/fs/course/controller/FsVideoResourceController.java

@@ -3,6 +3,7 @@ package com.fs.course.controller;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
@@ -13,12 +14,15 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsVideoResource;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.service.IFsUserVideoService;
 import com.fs.course.service.IFsVideoResourceService;
 import com.fs.course.vo.FsVideoResourceVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import lombok.AllArgsConstructor;
+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.*;
@@ -30,6 +34,7 @@ import java.util.stream.Collectors;
 /**
  * 资源库管理
  */
+@Slf4j
 @RestController
 @RequestMapping("/course/videoResource")
 @AllArgsConstructor
@@ -42,6 +47,11 @@ public class FsVideoResourceController extends BaseController {
 
     @Autowired
     private ISysConfigService configService;
+    @Autowired
+    private IFsUserVideoService fsUserVideoService;
+    @Autowired
+    private IFsUserCourseVideoService fsUserCourseVideoService;
+
 
     /**
      * 查询视频素材库列表
@@ -88,17 +98,25 @@ 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());
-        fsVideoResourceService.save(fsVideoResource);
+        boolean save = fsVideoResourceService.save(fsVideoResource);
+        if (save&&StringUtils.isNotEmpty(fsVideoResource.getHsyVid())){
+            try {
+                fsUserCourseVideoService.updateMediaPublishStatus(fsVideoResource.getHsyVid());
+                log.info("更新视频发布状态成功,hsyVid: {}", fsVideoResource.getHsyVid());
+            } catch (Exception e) {
+                log.error("更新视频发布状态失败,hsyVid: {}, 错误: {}", fsVideoResource.getHsyVid(), e.getMessage());
+            }
+        }
         return AjaxResult.success();
     }
 
@@ -183,8 +201,21 @@ public class FsVideoResourceController extends BaseController {
             if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
                 v.setUserId(userId);
             }
-        } );
-        fsVideoResourceService.saveBatch(list);
+        });
+        boolean saveStatus = fsVideoResourceService.saveBatch(list);
+        if (saveStatus) {
+            list.forEach(fsVideoResource -> {
+                // 检查hsyVid是否存在且不为空
+                if (ObjectUtil.isNotEmpty(fsVideoResource.getHsyVid())) {
+                    try {
+                        fsUserCourseVideoService.updateMediaPublishStatus(fsVideoResource.getHsyVid());
+                        log.info("更新视频发布状态成功,hsyVid: {}", fsVideoResource.getHsyVid());
+                    } catch (Exception e) {
+                        log.error("更新视频发布状态失败,hsyVid: {}, 错误: {}", fsVideoResource.getHsyVid(), e.getMessage());
+                    }
+                }
+            });
+        }
         return AjaxResult.success();
     }
 }

+ 113 - 0
fs-admin/src/main/java/com/fs/task/FsCompanyTask.java

@@ -3,18 +3,34 @@ package com.fs.task;
 import cn.hutool.json.JSONUtil;
 import com.fs.common.constant.FsConstants;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMoneyLogs;
 import com.fs.company.domain.CompanyRedPacketBalanceDeductionRecord;
 import com.fs.company.mapper.CompanyMoneyLogsMapper;
 import com.fs.company.service.ICompanyRedPacketBalanceDeductionRecordService;
 import com.fs.company.service.ICompanyService;
+import com.fs.company.util.CompanyRedPacketBalanceUtil;
 import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.config.CourseConfig;
+import com.fs.course.config.RedPacketConfig;
+import com.fs.course.domain.FsCourseRedPacketLog;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
+import com.fs.course.service.IFsCourseRedPacketLogService;
 import com.fs.system.service.ISysConfigService;
+import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesRequest;
+import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesResult;
+import com.github.binarywang.wxpay.bean.transfer.TransferBillsGetResult;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.TransferService;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
@@ -35,6 +51,8 @@ public class FsCompanyTask {
     private final RedisCache redisCache;
     private final CompanyMoneyLogsMapper moneyLogsMapper;
     private final RedisTemplate<String, Object> redisTemplate;
+    private final IFsCourseRedPacketLogService redPacketLogService;
+    private final CompanyRedPacketBalanceUtil redPacketBalanceUtil;
 
     public void refreshCompanyMoney() {
         // 使用红包余额
@@ -155,4 +173,99 @@ public class FsCompanyTask {
         }
 
     }
+
+
+    /**
+     * 补偿检查红包发送状态未变更记录
+     */
+    public void syncRedPacketSendStatus() {
+        if (isRedPackBalance()) {
+            LocalDateTime createTime = LocalDateTime.now().minusMinutes(30);
+            List<CompanyRedPacketBalanceDeductionRecord> records = deductionRecordService.selectAllProcessDeductionRecord(createTime);
+            records.forEach(record -> {
+                try {
+                    if (Objects.isNull(record.getRedPacketId())) {
+                        redPacketBalanceUtil.invalidRecord(record);
+                        return;
+                    }
+                    FsCourseRedPacketLog log = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogByLogId(record.getRedPacketId());
+                    String batchId = checkMchTransferStatus(log.getOutBatchNo());
+                    if (StringUtils.isNotBlank(batchId)) {
+                        redPacketLogService.syncRedPacket(log.getOutBatchNo(), batchId);
+                    } else {
+                        redPacketBalanceUtil.invalidRecord(record);
+                    }
+                } catch (Exception ignore) {}
+            });
+        } else {
+            LocalDateTime startTime = LocalDateTime.now().minusMinutes(90);
+            LocalDateTime endTime = LocalDateTime.now().minusMinutes(30);
+            List<FsCourseRedPacketLog> redPacketLogs = fsCourseRedPacketLogMapper.selectRedPacketLogListByCreateTime(startTime, endTime);
+            redPacketLogs.forEach(log -> {
+                try {
+                    String batchId = checkMchTransferStatus(log.getOutBatchNo());
+                    if (StringUtils.isNotBlank(batchId)) {
+                        redPacketLogService.syncRedPacket(log.getOutBatchNo(), batchId);
+                    }
+                } catch (Exception ignore) {}
+            });
+        }
+    }
+
+    /**
+     * 检查商家转账到零钱状态
+     */
+    private String checkMchTransferStatus(String outBatchNo) {
+        String json = configService.selectConfigByKey("redPacket.config");
+        RedPacketConfig config = JSONUtil.toBean(json, RedPacketConfig.class);
+
+        WxPayConfig payConfig = new WxPayConfig();
+        BeanUtils.copyProperties(config, payConfig);
+
+        WxPayService wxPayService = new WxPayServiceImpl();
+        wxPayService.setConfig(payConfig);
+
+        TransferService transferService = wxPayService.getTransferService();
+
+        if (Objects.isNull(config.getIsNew()) || !Arrays.asList(0,1).contains(config.getIsNew())) {
+            log.error("红包配置错误 isNew is err");
+            throw new CustomException("红包配置错误 isNew is err ");
+        }
+
+        try {
+            if (config.getIsNew() == 0) {
+                QueryTransferBatchesRequest request = new QueryTransferBatchesRequest();
+                request.setOutBatchNo(outBatchNo);
+                request.setNeedQueryDetail(true);
+                request.setOffset(0);
+                request.setLimit(20);
+                request.setDetailStatus("ALL");
+                QueryTransferBatchesResult result = transferService.transferBatchesOutBatchNo(request);
+                List<QueryTransferBatchesResult.TransferDetail> detailList = result.getTransferDetailList();
+                boolean isSuccess = detailList.stream().anyMatch(d -> "SUCCESS".equals(d.getDetailStatus()));
+                if (isSuccess) {
+                    return result.getTransferBatch().getBatchId();
+                }
+
+                boolean isFail = detailList.stream().anyMatch(d -> "FAIL".equals(d.getDetailStatus()));
+                if (isFail) {
+                    return "";
+                }
+            } else {
+                TransferBillsGetResult result = transferService.getBillsByOutBillNo(outBatchNo);
+                if ("SUCCESS".equals(result.getState())) {
+                    return result.getTransferBillNo();
+                } else if ("FAIL".equals(result.getState())) {
+                    return "";
+                } else if ("CANCELLED".equals(result.getState())) {
+                    return "";
+                }
+            }
+        } catch (WxPayException e) {
+            log.error("查询转账单失败 err: {}", e.getMessage(), e);
+            throw new CustomException("查询转账单失败:" + e.getMessage());
+        }
+
+        throw new CustomException("转账处理中");
+    }
 }

+ 5 - 5
fs-company-app/pom.xml

@@ -17,11 +17,11 @@
     <dependencies>
 
         <!-- spring-boot-devtools -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-devtools</artifactId>
-            <optional>true</optional> <!-- 表示依赖不会传递 -->
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.boot</groupId>-->
+<!--            <artifactId>spring-boot-devtools</artifactId>-->
+<!--            <optional>true</optional> &lt;!&ndash; 表示依赖不会传递 &ndash;&gt;-->
+<!--        </dependency>-->
         <!-- swagger2-->
         <dependency>
             <groupId>io.springfox</groupId>

+ 20 - 0
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -10,9 +10,11 @@ import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMiniapp;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyMiniappService;
+import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.dto.BatchSendCourseDTO;
@@ -39,6 +41,7 @@ import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -81,6 +84,8 @@ public class FsUserCourseVideoController extends AppBaseController {
     private IFsImMsgSendLogService imMsgSendLogService;
     @Autowired
     private ICompanyMiniappService companyMiniAppService;
+    @Autowired
+    private ICompanyService companyService;
 
     @Login
     @GetMapping("/pageList")
@@ -275,6 +280,21 @@ public class FsUserCourseVideoController extends AppBaseController {
         return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
     }
 
+    @GetMapping("/getGotoAppLink")
+    @ApiOperation("获取跳转微信小程序的链接地址")
+    public ResponseResult<String> getGotoWxAppLink(String linkStr, Long companyId) {
+        Company company = companyService.selectCompanyById(companyId);
+        String appid;
+        if (CollectionUtils.isNotEmpty(company.getMiniAppMaster())){
+            appid = company.getMiniAppMaster().get(0);
+        }else if (CollectionUtils.isNotEmpty(company.getMiniAppServer())){
+            appid = company.getMiniAppServer().get(0);
+        }else {
+            return ResponseResult.fail(500,"请在后台配置主备小程序!");
+        }
+        return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
+    }
+
     @ApiOperation("会员批量发送课程消息")
     @PostMapping("/batchSendCourse")
     public OpenImResponseDTO batchSendCourse(@RequestBody BatchSendCourseDTO batchSendCourseDTO) throws JsonProcessingException {

+ 5 - 5
fs-company/pom.xml

@@ -17,11 +17,11 @@
 
     <dependencies>
         <!-- spring-boot-devtools -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-devtools</artifactId>
-            <optional>true</optional> <!-- 表示依赖不会传递 -->
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.boot</groupId>-->
+<!--            <artifactId>spring-boot-devtools</artifactId>-->
+<!--            <optional>true</optional> &lt;!&ndash; 表示依赖不会传递 &ndash;&gt;-->
+<!--        </dependency>-->
         <!-- swagger2-->
         <dependency>
             <groupId>io.springfox</groupId>

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

@@ -367,6 +367,7 @@ public class QwSopController extends BaseController
     /**
      * 批量执行SOP
      */
+    @Log(title = "批量执行sop", businessType = BusinessType.UPDATE)
     @GetMapping(value = "/updateStatus/{ids}")
     public R batchDoSop(@PathVariable String[] ids)
     {

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

@@ -912,14 +912,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);

+ 5 - 0
fs-company/src/main/java/com/fs/company/controller/qw/SopUserLogsController.java

@@ -195,4 +195,9 @@ public class SopUserLogsController extends BaseController
         sopUserLogsService.replaceUser(vo);
         return R.ok();
     }
+
+    @GetMapping("/getShortLink")
+    public R getShortLink(String id, String sopId, String appId){
+        return sopUserLogsService.getShortLink(id, sopId, appId);
+    }
 }

+ 41 - 0
fs-company/src/main/java/com/fs/user/FsUserAdminController.java

@@ -17,7 +17,9 @@ import com.fs.course.service.IFsUserCourseService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 
+import com.fs.his.domain.FsExportTask;
 import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsExportTaskService;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.PhoneUtil;
 import com.fs.im.dto.OpenImResponseDTO;
@@ -25,6 +27,7 @@ import com.fs.im.service.OpenIMService;
 import com.fs.qw.domain.CustomerTransferApproval;
 import com.fs.qw.dto.FsUserTransferParamDTO;
 import com.fs.qw.service.ICustomerTransferApprovalService;
+import com.fs.store.param.h5.FsUserPageListExportParam;
 import com.fs.store.param.h5.FsUserPageListParam;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -63,6 +66,8 @@ public class FsUserAdminController extends BaseController {
 
     @Autowired
     private OpenIMService openIMService;
+    @Autowired
+    private IFsExportTaskService exportTaskService;
 
     @PreAuthorize("@ss.hasPermi('user:fsUser:list')")
     @PostMapping("/list")
@@ -90,6 +95,42 @@ public class FsUserAdminController extends BaseController {
         return fsUserService.selectFsUserPageListNew(param);
     }
 
+    @PreAuthorize("@ss.hasPermi('user:fsUser:export')")
+    @PostMapping("/export")
+    @Log(title = "项目会员列表", businessType = BusinessType.EXPORT)
+    @ApiOperation("项目会员列表导出(与移动端使用的相同查询)")
+    public AjaxResult projectUserExport(FsUserPageListExportParam param) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        if(param.getPhone()!=null && !"".equals(param.getPhone())){
+            param.setPhone(PhoneUtil.encryptPhone(param.getPhone()));
+        }
+
+        Long userId = loginUser.getUser().getUserId();
+        Integer exportType1 = exportTaskService.isExportType2(userId);
+        if (exportType1 > 0){
+            return AjaxResult.error("你已经有正在导出的任务");
+        }
+
+        Long count = fsUserService.selectProjectUserExportCount(param);
+        if (count > 30000){
+            return AjaxResult.error("导出数据不可超过3w条");
+        }
+
+        FsExportTask task=new FsExportTask();
+        task.setTaskType(12);
+        task.setStatus(0);
+        task.setStartTime(new Date());
+        task.setRemark("项目会员导出");
+        task.setSysType(2);
+        task.setCompanyUserId(userId);
+        exportTaskService.insertFsExportTask(task);
+        param.setTaskId(task.getTaskId());
+        fsUserService.exportProjectUserData(param);
+
+        return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
+    }
+
     @PreAuthorize("@ss.hasPermi('user:fsUser:myList')")
     @PostMapping("/myList")
     @ApiOperation("我的会员列表(与移动端使用的相同查询)")

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

@@ -47,11 +47,14 @@ public class IpadSendServer {
     private final IQwUserVideoService qwUserVideoService;
     private final RedisCache redisCache;
     private final ICompanyMiniappService companyMiniappService;
-
     private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap, Long companyId) {
+        // 发送参数原本的appid
         String appid = content.getMiniprogramAppid();
+        // 判断销售工时ID不为空并且有小程序类型
         if(companyId != null && content.getMiniType() != null){
+            // 获取销售公司下面绑定的主备小程序,并且根据当前应该发送的主备类型查询出数据
             List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId).eq("type", content.getMiniType()));
+            // 判断当前绑定的最新的小程序,并且覆盖以前的值(可以达到实时替换小程序的功能)
             if(!list.isEmpty() && list.get(0) != null && StringUtils.isNotEmpty(list.get(0).getAppId())){
                 appid = list.get(0).getAppId();
             }
@@ -341,6 +344,7 @@ public class IpadSendServer {
         vo.setServerId(qwUser.getServerId());
         vo.setCorpCode(parentVo.getCorpCode());
         vo.setCorpId(parentVo.getCorpId());
+        vo.setQwUserId(qwUser.getId());
         try {
             content.setSendStatus(1);
             switch (content.getContentType()) {

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

@@ -1068,7 +1068,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
                     setting.setLinkUrl(linkByApp.getSortLink().replaceAll("^[\\s\\u2005]+", ""));
                     setting.setAppLinkUrl(linkByApp.getAppMsgLink().replaceAll("^[\\s\\u2005]+", ""));
-
+                    setting.setCourseUrl(setting.getLinkImageUrl());
+                    setting.setTitle(setting.getLinkTitle());
                     break;
                 //自定义小程序
                 case "10":

+ 11 - 8
fs-service/pom.xml

@@ -285,17 +285,20 @@
         </dependency>
 
         <!-- 移动云ECS SDK -->
+<!--        <dependency>-->
+<!--            <groupId>com.ecloud.sdk</groupId>-->
+<!--            <artifactId>ecloud-sdk-ecs</artifactId>-->
+<!--            <version>1.1.26</version>-->
+<!--        </dependency>-->
+
+        <!--火山云sdk-->
         <dependency>
-            <groupId>com.ecloud.sdk</groupId>
-            <artifactId>ecloud-sdk-ecs</artifactId>
-            <version>1.1.26</version>
-        </dependency>
-        <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <version>2.11.0</version>
+            <groupId>com.volcengine</groupId>
+            <artifactId>volc-sdk-java</artifactId>
+            <version>1.0.250</version>
         </dependency>
 
+
     </dependencies>
 
 </project>

+ 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);
+//    }
 
 }

+ 8 - 1
fs-service/src/main/java/com/fs/company/mapper/CompanyRedPacketBalanceDeductionRecordMapper.java

@@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
 import java.util.List;
 
 public interface CompanyRedPacketBalanceDeductionRecordMapper extends BaseMapper<CompanyRedPacketBalanceDeductionRecord> {
@@ -26,5 +27,11 @@ public interface CompanyRedPacketBalanceDeductionRecordMapper extends BaseMapper
      * 查询所有已完成扣款的数据
      */
     @Select("select * from company_red_packet_balance_deduction_record where status = 1 limit 5000")
-    List<CompanyRedPacketBalanceDeductionRecord> selectByStatus();
+    List<CompanyRedPacketBalanceDeductionRecord> selectByComplete();
+
+    /**
+     *  查询所有处理中的数据
+     */
+    @Select("select * from company_red_packet_balance_deduction_record where status = 0 and create_time <= #{createTime} ")
+    List<CompanyRedPacketBalanceDeductionRecord> selectByProcess(@Param("createTime") LocalDateTime createTime);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyRedPacketBalanceDeductionRecordService.java

@@ -3,6 +3,7 @@ package com.fs.company.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.company.domain.CompanyRedPacketBalanceDeductionRecord;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 public interface ICompanyRedPacketBalanceDeductionRecordService extends IService<CompanyRedPacketBalanceDeductionRecord> {
@@ -11,4 +12,9 @@ public interface ICompanyRedPacketBalanceDeductionRecordService extends IService
      * 查询所有已完成扣款的数据
      */
     List<CompanyRedPacketBalanceDeductionRecord> selectAllCompleteDeductionRecord();
+
+    /**
+     * 查询所有处理中的数据
+     */
+    List<CompanyRedPacketBalanceDeductionRecord> selectAllProcessDeductionRecord(LocalDateTime createTime);
 }

+ 4 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyService.java

@@ -214,4 +214,8 @@ public interface ICompanyService
      * 根据扣减记录扣减红包余额
      */
     void subDbRedPacketBalance(Long companyId, List<CompanyRedPacketBalanceDeductionRecord> records);
+
+    void addCompanyTuiLiveMoney(LiveOrder order);
+
+    void subLiveCompanyMoney(LiveOrder order);
 }

+ 10 - 1
fs-service/src/main/java/com/fs/company/service/impl/CompanyRedPacketBalanceDeductionRecordServiceImpl.java

@@ -6,6 +6,7 @@ import com.fs.company.mapper.CompanyRedPacketBalanceDeductionRecordMapper;
 import com.fs.company.service.ICompanyRedPacketBalanceDeductionRecordService;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDateTime;
 import java.util.Collections;
 import java.util.List;
 
@@ -18,6 +19,14 @@ public class CompanyRedPacketBalanceDeductionRecordServiceImpl extends ServiceIm
      */
     @Override
     public List<CompanyRedPacketBalanceDeductionRecord> selectAllCompleteDeductionRecord() {
-        return baseMapper.selectByStatus();
+        return baseMapper.selectByComplete();
+    }
+
+    /**
+     *  查询所有处理中的数据
+     */
+    @Override
+    public List<CompanyRedPacketBalanceDeductionRecord> selectAllProcessDeductionRecord(LocalDateTime createTime) {
+        return baseMapper.selectByProcess(createTime);
     }
 }

+ 49 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -34,6 +34,7 @@ import com.fs.hisStore.domain.FsStoreOrderScrm;
 import com.fs.hisStore.domain.FsStorePaymentScrm;
 import com.fs.hisStore.mapper.FsStoreOrderScrmMapper;
 import com.fs.live.domain.LiveOrder;
+import com.fs.live.mapper.LiveOrderMapper;
 import com.fs.live.service.ILiveService;
 import com.fs.store.config.CompanyMenuConfig;
 import com.fs.system.domain.SysConfig;
@@ -50,6 +51,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.support.TransactionTemplate;
 
@@ -124,6 +126,8 @@ public class CompanyServiceImpl implements ICompanyService
     private ICompanyRedPacketBalanceDeductionRecordService deductionRecordService;
     @Autowired
     private CompanyRedPacketBalanceUtil companyRedPacketBalanceUtil;
+    @Autowired
+    private LiveOrderMapper liveOrderMapper;
 
 
     @Override
@@ -137,6 +141,51 @@ public class CompanyServiceImpl implements ICompanyService
         liveService.asyncToCache();
     }
 
+    @Override
+    @Transactional
+    public void addCompanyTuiLiveMoney(LiveOrder order) {
+        if(order.getCompanyId()!=null&&order.getCompanyId()>0){
+            Company company=companyMapper.selectCompanyByIdForUpdate(order.getCompanyId());
+            if(company!=null){
+                String json =configService.selectConfigByKey("store.config");
+                com.fs.store.config.StoreConfig config= JSONUtil.toBean(json, com.fs.store.config.StoreConfig.class);
+                //支付金额-(订单金额*rate%)
+                Double rate=config.getTuiMoneyRate()/100d;
+                BigDecimal tuiMoney=order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+                logger.info("写入公司推广佣金:"+tuiMoney);
+                company.setTuiMoney(company.getTuiMoney().add(tuiMoney));
+                companyMapper.updateCompany(company);
+                LiveOrder storeOrderMap=new LiveOrder();
+                storeOrderMap.setOrderId(order.getOrderId());
+                storeOrderMap.setTuiMoney(tuiMoney);
+                liveOrderMapper.updateLiveOrder(storeOrderMap);
+            }
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Throwable.class,propagation = Propagation.REQUIRED)
+    public void subLiveCompanyMoney(LiveOrder order) {
+        if(order.getCompanyId()>0){
+            Company company=companyMapper.selectCompanyByIdForUpdate(order.getCompanyId());
+            if(company!=null){
+                company.setMoney(company.getMoney().subtract(order.getTuiMoney()));
+                company.setTuiMoney(company.getTuiMoney().subtract(order.getTuiMoney()));
+                companyMapper.updateCompany(company);
+                //写入日志
+                CompanyMoneyLogs log=new CompanyMoneyLogs();
+                log.setCompanyId(order.getCompanyId());
+                log.setRemark("订单佣金退款");
+                log.setMoney(order.getTuiMoney().multiply(new BigDecimal(-1)));
+                log.setLogsType(5);
+                log.setBalance(company.getMoney());
+                log.setCreateTime(new Date());
+                log.setBusinessId(order.getOrderId().toString());
+                moneyLogsMapper.insertCompanyMoneyLogs(log);
+            }
+        }
+    }
+
     /**
      * 根据扣减记录扣减红包余额
      */

+ 8 - 0
fs-service/src/main/java/com/fs/config/cloud/CloudHostProper.java

@@ -15,4 +15,12 @@ public class CloudHostProper {
 
     @Value("${cloud_host.projectCode}")
     private String projectCode;
+
+    //火山云空间名称
+    @Value("${cloud_host.spaceName}")
+    public String spaceName;
+
+    //火山云空间绑定域名
+    @Value("${cloud_host.volcengineUrl}")
+    public String volcengineUrl;
 }

+ 30 - 0
fs-service/src/main/java/com/fs/core/config/VolcEngineConfiguration.java

@@ -0,0 +1,30 @@
+package com.fs.core.config;
+
+import com.volcengine.service.vod.IVodService;
+import com.volcengine.service.vod.impl.VodServiceImpl;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class VolcEngineConfiguration {
+    @Value("${hsy.access_key:''}")
+    private String access_key;
+
+    @Value("${hsy.secret_key:''}")
+    private String secret_key;
+    @Value("${hsy.region:''}")
+    private String region;
+    @Bean
+    public IVodService vodService() throws Exception {
+        // 根据区域获取火山云点播服务实例
+        IVodService vodService = VodServiceImpl.getInstance(region);
+
+        // 设置 AccessKey 和 SecretKey
+        vodService.setAccessKey(access_key);
+        vodService.setSecretKey(secret_key);
+
+        return vodService;
+    }
+}
+

+ 36 - 0
fs-service/src/main/java/com/fs/core/service/impl/STSService.java

@@ -0,0 +1,36 @@
+package com.fs.core.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.config.cloud.CloudHostProper;
+import com.volcengine.model.request.AssumeRoleRequest;
+import com.volcengine.model.response.AssumeRoleResponse;
+import com.volcengine.service.sts.ISTSService;
+import com.volcengine.service.sts.impl.STSServiceImpl;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class STSService {
+
+    private final CloudHostProper cloudHostProper;
+
+    public STSService(CloudHostProper cloudHostProper) {
+        this.cloudHostProper = cloudHostProper;
+    }
+
+//    public AssumeRoleResponse geSTSToken() throws Exception {
+//        ISTSService stsService = STSServiceImpl.getInstance();
+//
+//        stsService.setAccessKey(cloudHostProper.accessKey);
+//        stsService.setSecretKey(cloudHostProper.secretKey);
+//
+//        AssumeRoleRequest request = new AssumeRoleRequest();
+//        request.setRoleSessionName("just_for_test");
+//        request.setDurationSeconds(900);
+//        request.setRoleTrn("trn:iam::yourAccountID:role/yourRoleName");
+//
+//        AssumeRoleResponse resp = stsService.assumeRole(request);
+//        return resp;
+//    }
+
+}

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

@@ -112,4 +112,7 @@ public class FsUserCourseVideo extends BaseEntity
 
     private Long listingEndTime;//商品结束售卖时间
 
+    private String jobId;
+
+    private String vid;
 }

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

@@ -99,4 +99,9 @@ public class FsVideoResource {
     private Integer sort;
 
     private Long userId;
+
+    //火山云vid
+    private String hsyVid;
+
+    private String jobId;
 }

+ 1 - 1
fs-service/src/main/java/com/fs/course/dto/BatchSendCourseDTO.java

@@ -70,7 +70,7 @@ public class BatchSendCourseDTO implements Serializable {
     private Long projectId;
 
     @ApiModelProperty(value = "是否催课", required = true)
-    private Boolean isUrgeCourse;
+    private Boolean isUrgeCourse = false;
 
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @ApiModelProperty(value = "催课时间")

+ 4 - 1
fs-service/src/main/java/com/fs/course/dto/FsOrderDeliveryNoteDTO.java

@@ -16,9 +16,12 @@ public class FsOrderDeliveryNoteDTO {
     @Excel(name = "系统订单号(必填)",width = 20,sort = 1)
     private String orderNumber;
 
-    @Excel(name = "物流公司编号(必填)(SF:顺丰、EMS:邮政、ZTO:中通、JD:京东、DBL:德邦)",width = 30,sort = 2)
+    @Excel(name = "物流公司编号(必填)(SF:顺丰、EMS:邮政、ZTO:中通、JD:京东、DBL:德邦、YTO:圆通)",width = 30,sort = 2)
     private String deliverySn;
 
+    @Excel(name = "物流公司",width = 10,sort = 10)
+    private String logisticsCompany;
+
     private String deliveryName;
 
     @Excel(name = "快递单号(必填)",width = 20,sort = 3)

+ 18 - 0
fs-service/src/main/java/com/fs/course/dto/OrderOpenIdTransDTO.java

@@ -0,0 +1,18 @@
+package com.fs.course.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class OrderOpenIdTransDTO implements Serializable {
+    /**
+     * 用户微信openId
+     * **/
+    private String openId;
+
+    /**
+     * 交易单号
+     * **/
+    private String transactionId;
+}

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

@@ -178,4 +178,10 @@ public interface FsCourseRedPacketLogMapper
     List<CourseRedPacketStatisticsDTO> statistics(CourseRedPacketStatisticsParam param);
 
     List<FsCourseRedPacketLog> selectFsCourseRedPacketLogListBySending(@Param("maps") Map<String, Object> map);
+
+    /**
+     * 根据创建时间查询红包记录
+     */
+    @Select("select * from fs_course_red_packet_log where status = 0 and batch_id is not null and create_time >= #{startTime} and create_time <= #{endTime}")
+    List<FsCourseRedPacketLog> selectRedPacketLogListByCreateTime(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime);
 }

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

@@ -195,4 +195,22 @@ public interface FsCourseTrafficLogMapper
     @Select("SELECT SUM(T.internet_traffic)/1024 AS totalInternetTraffic FROM fs_course_traffic_log T " +
             "WHERE DATE(T.create_time) = DATE(CURDATE() - INTERVAL 1 DAY) AND T.company_id = #{companyId} GROUP BY T.company_id ")
     Long sumTrafficByCompanyYesterday(Long companyId);
+
+    /**
+     * 查询过期的流量记录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 - 1
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java

@@ -1,6 +1,7 @@
 package com.fs.course.mapper;
 
 import com.fs.course.domain.FsUserCourseVideo;
+import com.fs.course.domain.FsVideoResource;
 import com.fs.course.param.CourseVideoUpdates;
 import com.fs.course.param.FsCourseListBySidebarParam;
 import com.fs.course.param.FsUserCourseVideoListUParam;
@@ -204,7 +205,7 @@ public interface FsUserCourseVideoMapper
             "        <if test=\"data.keyword != null and data.keyword !='' \">\n" +
             "            AND v.title LIKE concat('%',#{data.keyword},'%')\n" +
             "        </if>" +
-            "order by v.video_id asc " +
+            "order by v.course_sort asc, v.video_id asc " +
             "</script>")
     List<FsCourseVideoListBySidebarVO> getFsCourseVideoListBySidebar(@Param("data") FsCourseListBySidebarParam param);
 
@@ -240,6 +241,12 @@ public interface FsUserCourseVideoMapper
             "WHERE file_key = #{fileKey}")
     void updateFsUserCourseVideoByFileKey(FsUserCourseVideo courseVideo);
 
+    @Update("UPDATE fs_user_course_video " +
+            "SET line_two = #{lineTwo}, " +
+            "    update_time = NOW() " +  // 添加更新时间
+            "WHERE file_key = #{fileKey}")
+    void updateFsUserCourseVideoByFileKeyForHsy(FsUserCourseVideo courseVideo);
+
     @Select("select title from fs_user_course_video WHERE video_id=#{videoId}")
     String selectFsUserCourseVideoByVideoForTitle(@Param("videoId") Long videoId);
 
@@ -260,4 +267,14 @@ public interface FsUserCourseVideoMapper
     @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")
     Map<Long, FsUserCourseVideo> selectAllMap();
+
+    @Select("select * from fs_video_resource where line2 is not null and job_id is null")
+    List<FsVideoResource> selectVideoByHuaWei();
+
+    @Select("select * from fs_video_resource where job_id is not null and  (hsy_vid is null or hsy_vid='')")
+    List<FsVideoResource> selectVideoByJobId();
+
+
+    @Select("select * from fs_video_resource where hsy_vid is not null")
+    List<FsVideoResource> selectVideoByVid();
 }

+ 58 - 0
fs-service/src/main/java/com/fs/course/service/HsyAssumeRoleService.java

@@ -0,0 +1,58 @@
+package com.fs.course.service;
+
+import com.fs.config.cloud.CloudHostProper;
+import com.hc.openapi.tool.fastjson.JSON;
+import com.volcengine.model.request.AssumeRoleRequest;
+import com.volcengine.model.response.AssumeRoleResponse;
+import com.volcengine.service.sts.ISTSService;
+import com.volcengine.service.sts.impl.STSServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class HsyAssumeRoleService {
+    @Autowired
+    private CloudHostProper cloudHostProper;
+
+    @Value("${hsy.role_access_key:''}")
+    private String role_access_key;
+
+    @Value("${hsy.role_secret_key:''}")
+    private String role_secret_key;
+
+    @Value("${hsy.role_trn:''}")
+    private String role_trn;
+    public AssumeRoleResponse getRoleResponse() throws Exception {
+        ISTSService stsService = STSServiceImpl.getInstance();
+
+        stsService.setAccessKey(role_access_key);
+        stsService.setSecretKey(role_secret_key);
+
+//        stsService.setAccessKey("AKLTZTc4YTE4ZjI2OWViNDNjZGI2NjhiYTI5Njc5ZjA1Mzk");
+//        stsService.setSecretKey("WXpjelpUYzFOakF5TUdObE5EZGtNR0ZsWXpKaU1tTmtZakk1WXpObE4yRQ==");
+
+        AssumeRoleRequest request = new AssumeRoleRequest();
+        request.setRoleSessionName(cloudHostProper.spaceName);
+        request.setDurationSeconds(900);
+        request.setRoleTrn(role_trn);
+
+        AssumeRoleResponse resp = stsService.assumeRole(request);
+        return resp;
+    }
+
+    public static void main(String[] args) throws Exception {
+        ISTSService stsService = STSServiceImpl.getInstance();
+
+        stsService.setAccessKey("AKLTNmMwNjJkNDFhYTVjNDIzYzhhNzEyZmZmZTlmYzBhNGM");
+        stsService.setSecretKey("T0RaaFl6UmhZV1V4WXpKbU5EWTBNMkZpT0RNNU9UY3daak0wTjJFd09XUQ==");
+
+        AssumeRoleRequest request = new AssumeRoleRequest();
+        request.setRoleSessionName("just_for_test");
+        request.setDurationSeconds(900);
+        request.setRoleTrn("trn:iam::2114522511:role/hylj");
+        AssumeRoleResponse resp = stsService.assumeRole(request);
+        System.out.println(JSON.toJSONString(resp));
+
+    }
+}

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

@@ -13,4 +13,6 @@ public interface IFsCoursePlaySourceConfigService extends IService<FsCoursePlayS
      * 查询点播配置列表
      */
     List<FsCoursePlaySourceConfigVO> selectCoursePlaySourceConfigVOListByMap(Map<String, Object> params);
+
+    List<FsCoursePlaySourceConfig> selectByAppIds(List<String> miniAppList);
 }

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

@@ -80,4 +80,6 @@ public interface IFsCourseTrafficLogService
      * 定时统计流量总数
      */
     void sumTrafficlog();
+
+    void batchDelTraffic();
 }

+ 21 - 1
fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -17,7 +17,7 @@ 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 org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 import java.util.Map;
@@ -209,4 +209,24 @@ public interface IFsUserCourseVideoService
     R sendAppReward(FsCourseSendRewardUParam param);
 
     R isSaveKf(FsUserCourseVideoAddKfUParam param);
+
+    /**
+     * 上传视频到火山云通过URL(把华为云的视频传到火山去并且存储JOBID)
+     * @return
+     */
+    R uploadVideoToHuoShanByUrl();
+
+    /**
+     * 通过jobId拿vid
+     * @return
+     */
+    R getVidByJob();
+
+    /**
+     * 通过vid获取视频路径并且组装地址存到替换线路二
+     * @return
+     */
+    R getVideoInfoByVid();
+
+    void updateMediaPublishStatus(String vid);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserVideoService.java

@@ -9,6 +9,7 @@ import com.fs.course.vo.FsUserVideoListPVO;
 import com.fs.course.vo.FsUserVideoPVO;
 import com.fs.course.param.FsUserVideoParam;
 import com.fs.course.vo.FsUserVideoListUVO;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * 课堂视频Service接口
@@ -104,4 +105,9 @@ public interface IFsUserVideoService {
     R deleteFsUserVideoByVideoIdWithVerify(Long videoId, Long userId);
 
     R updateVideoStatusWithVerify(FsUserVideo fsUserVideo, Long userId);
+
+    String getVideoInfoByVid(String vid);
+
+
+    String uploadVideo(MultipartFile file, String uploadId);
 }

+ 3 - 7
fs-service/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java

@@ -773,7 +773,8 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         CloseableHttpClient client = null;
         try {
             client = HttpClients.createDefault();
-            String[] split = linkStr.split("\\?");
+            String[] split = linkStr.split("\\?.*?=");
+            String key = linkStr.replaceAll(".*\\?(.*?)=.*", "$1");
             if (split.length == 2 && split[0].length() > 0 && split[1].length() > 0) {
                 //处理页面路径
                 String pageUrl = split[0];
@@ -782,7 +783,7 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
                 }
                 //处理参数
                 String query = split[1];
-                query = URLEncoder.encode(query, StandardCharsets.UTF_8.toString());
+                query = key + "=" + URLEncoder.encode(query, StandardCharsets.UTF_8.toString());
 //                String json = configService.selectConfigByKey("course.config");
 //                CourseConfig config = JSON.parseObject(json, CourseConfig.class);
 //                String miniprogramAppid = config.getMiniprogramAppid();
@@ -898,14 +899,9 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
                             }else {
                                 return R.error("获取失败:"+jsonObject.getString("errmsg"));
                             }
-                        }else {
-                            return R.error("未配置小程序id");
                         }
                     }
                 }
-
-
-
             } else {
                 return R.error("页面链接错误,获取失败");
             }

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

@@ -1,5 +1,6 @@
 package com.fs.course.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
 import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
@@ -24,4 +25,9 @@ public class FsCoursePlaySourceConfigServiceImpl extends ServiceImpl<FsCoursePla
     public List<FsCoursePlaySourceConfigVO> selectCoursePlaySourceConfigVOListByMap(Map<String, Object> params) {
         return baseMapper.selectCoursePlaySourceConfigVOListByMap(params);
     }
+
+    @Override
+    public List<FsCoursePlaySourceConfig> selectByAppIds(List<String> miniAppList) {
+        return baseMapper.selectList(new QueryWrapper<FsCoursePlaySourceConfig>().in("appid", miniAppList));
+    }
 }

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

@@ -1,6 +1,8 @@
 package com.fs.course.service.impl;
 
 import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneId;
 import java.util.*;
 
 import com.alibaba.fastjson.JSONObject;
@@ -402,4 +404,84 @@ public class FsCourseTrafficLogServiceImpl implements IFsCourseTrafficLogService
                 minutes % 60,
                 seconds % 60);
     }
+
+    @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);
+        }
+    }
 }

+ 3 - 3
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java

@@ -133,8 +133,8 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
 
     private static final String userRealLink = "/pages/user/users/becomeVIP?";
 
-    private static final String appRealLink = "/#/pages_course/videovip?course=";
-    public static final String appShortLink = "/#/pages_course/videovip?s=";
+    private static final String appRealLink = "/courseH5/pages_course/videovip?course=";
+    public static final String appShortLink = "/pages_course/videovip?s=";
 
     /**
      * 查询课程
@@ -756,7 +756,7 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
         int i = fsCourseLinkMapper.insertFsCourseLink(link);
         if (i > 0){
             String domainName = getDomainName(param.getCompanyUserId(), config);
-            String sortLink = domainName + appShortLink + link.getLink();
+            String sortLink = domainName + link.getRealLink();
             return R.ok().put("url", sortLink).put("link", random);
         }
         return R.error("生成链接失败!");

+ 522 - 194
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -31,6 +31,7 @@ import com.fs.company.mapper.*;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.util.CompanyRedPacketBalanceUtil;
 import com.fs.config.cloud.CloudHostProper;
+import com.fs.core.utils.OrderCodeUtils;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.*;
 import com.fs.course.dto.CoursePackageDTO;
@@ -62,48 +63,47 @@ import com.fs.qw.mapper.*;
 import com.fs.qw.param.FsUserCourseRedPageParam;
 import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.service.IQwExternalContactService;
-import com.fs.qw.vo.SortDayVo;
 import com.fs.qwApi.Result.QwAddContactWayResult;
-import com.fs.qwApi.Result.QwGroupChatDetailsResult;
 import com.fs.qwApi.param.QwAddContactWayParam;
 import com.fs.qwApi.service.QwApiService;
-import com.fs.sop.domain.QwSopTempDay;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.SopUserLogsInfoMapper;
 import com.fs.sop.service.ISopUserLogsInfoService;
-import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysDictDataMapper;
 import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
 import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
-import com.google.common.collect.Sets;
+import com.volcengine.service.vod.IVodService;
+import com.volcengine.service.vod.model.business.VodUrlUploadURLSet;
+import com.volcengine.service.vod.model.request.VodGetMediaInfosRequest;
+import com.volcengine.service.vod.model.request.VodQueryUploadTaskInfoRequest;
+import com.volcengine.service.vod.model.request.VodUpdateMediaPublishStatusRequest;
+import com.volcengine.service.vod.model.request.VodUrlUploadRequest;
+import com.volcengine.service.vod.model.response.VodGetMediaInfosResponse;
+import com.volcengine.service.vod.model.response.VodQueryUploadTaskInfoResponse;
+import com.volcengine.service.vod.model.response.VodUpdateMediaPublishStatusResponse;
+import com.volcengine.service.vod.model.response.VodUrlUploadResponse;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.rocketmq.spring.core.RocketMQTemplate;
-import org.jetbrains.annotations.NotNull;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
-import org.redisson.client.RedisClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.text.SimpleDateFormat;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
-import java.time.temporal.ChronoUnit;
 import java.util.*;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -140,8 +140,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
     @Autowired
     ICompanyService companyService;
     @Autowired
-    private CompanyMoneyLogsMapper moneyLogsMapper;
-    @Autowired
     private FsUserCourseVideoMapper fsUserCourseVideoMapper;
     @Autowired
     private QwGroupChatMapper qwGroupChatMapper;
@@ -152,8 +150,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
     @Autowired
     private FsUserCourseCompanyUserTimeMapper companyUserTimeMapper;
     @Autowired
-    private RocketMQTemplate rocketMQTemplate;
-    @Autowired
     private FsUserCourseStudyLogMapper courseStudyLogMapper;
 
     @Autowired
@@ -168,9 +164,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
     @Autowired
     private FsCourseLinkMapper fsCourseLinkMapper;
 
-    @Autowired
-    private AsyncIsAddKfXfkService xfkService;
-
 
     @Autowired
     private RedissonClient redissonClient;
@@ -179,8 +172,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
     @Autowired
     private ISysConfigService configService;
     @Autowired
-    private FsCourseSopLogsMapper courseSopLogsMapper;
-    @Autowired
     private QwApiService qwApiService;
     @Autowired
     private QwUserMapper qwUserMapper;
@@ -196,11 +187,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
     private IFsStorePaymentService paymentService;
     @Autowired
     private FsCourseRedPacketLogMapper redPacketLogMapper;
-    @Autowired
-    private FsCourseFinishTempMapper fsCourseFinishTempMapper;
 
-    @Autowired
-    private QwSopLogsMapper qwSopLogsMapper;
     @Autowired
     private FsCourseLinkMapper courseLinkMapper;
     @Autowired
@@ -211,8 +198,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
     @Autowired
     private CompanyUserMapper companyUserMapper;
 
-    @Autowired
-    private IFsVideoResourceService fsVideoResourceService;
     @Autowired
     private FsVideoResourceMapper fsVideoResourceMapper;
 
@@ -225,8 +210,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
     @Autowired
     private FsUserCoursePeriodDaysMapper fsUserCoursePeriodDaysMapper;
 
-    @Autowired
-    private FsUserCompanyUserMapper fsUserCompanyUserMapper;
 
     @Autowired
     private IQwExternalContactService qwExternalContactService;
@@ -251,6 +234,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
 
     @Autowired
     ConfigUtil configUtil;
+
     @Autowired
     private FsUserCoursePeriodCompanyMapper periodCompanyMapper;
 
@@ -314,8 +298,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
             fsUserCourseVideo.setListingStartTime(null);
             fsUserCourseVideo.setListingEndTime(null);
         }
-        String videoRedisKey = "h5user:video:duration:" + fsUserCourseVideo.getVideoId();
-        redisCache.setCacheObject(videoRedisKey, fsUserCourseVideo.getDuration());
+        String videoRedisKey1 = "h5user:video:duration:" + fsUserCourseVideo.getVideoId();
+        redisCache.setCacheObject(videoRedisKey1, fsUserCourseVideo.getDuration());
+        String videoRedisKey2 = "h5wxuser:video:duration:" + fsUserCourseVideo.getVideoId();
+        redisCache.setCacheObject(videoRedisKey2, fsUserCourseVideo.getDuration());
         return fsUserCourseVideoMapper.updateFsUserCourseVideo(fsUserCourseVideo);
     }
 
@@ -492,7 +478,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         FsUser fsUser = fsUserMapper.selectFsUserByUserId(param.getUserId());
         //用户不存在唤起重新授权
         if (fsUser == null) {
-            return R.error(504, "未授权");
+            return R.error(401, "未授权");
         }
 
         if (fsUser.getStatus() == 0) {
@@ -513,12 +499,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
 //            }
 //            return  R.ok();
 //        }
-        String msg = "<div style=\"color: red;margin-bottom: 15px;font-weight: bold;\">本课程为会员独享<br>请长按二维码</div>\n" +
-                "\t\t\t\t\t<div style=\"color: #999;font-size: 14px;font-weight: bold;\">添加伴学助手免费领取会员权限</div>";
-        //济南联志隐藏二维码
-        if ("济南联志健康".equals(signProjectName)) {
-            msg = "<div style=\"color: red;margin-bottom: 15px;font-weight: bold;\">本课程为会员独享<br></div>";
-        }
+        //未注册提示
+        String noRegisterMsg = "由于您还未完成注册,请联系伴学助手完成注册即可观看!";
+        //非独属链接提示
+        String noMemberMsg = "此链接已被绑定,请联系伴学助手领取您的专属链接,专属链接请勿分享哦!";
 //        try {
 //            new Thread(() -> {
 //                try {
@@ -535,10 +519,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
 
-        //服务号授权的,缺mpOpenId的重新登录
-        if (config.getMiniAppAuthType() == 2 && StringUtil.strIsNullOrEmpty(fsUser.getMpOpenId())) {
-            return R.error(401, "授权后可继续!");
-        }
+//        //服务号授权的,缺mpOpenId的重新登录 linkType = 4为app看课 不需要mp
+//        if (param.getLinkType() != 4 && config.getMiniAppAuthType()==2 && StringUtil.strIsNullOrEmpty(fsUser.getMpOpenId())){
+//            return R.error(401,"授权后可继续!");
+//        }
 
         boolean oneCompanyCourse = config.isOneCompanyCourse();
         if (oneCompanyCourse && fsUser.getQwExtId() != null) {
@@ -563,15 +547,15 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         Integer isRoom = param.getIsRoom();
 
         // 处理逻辑
-        if (StringUtils.isNotEmpty(param.getChatId())) {
-            return handleQwRoom(param, fsUser);
+        if(StringUtils.isNotEmpty(param.getChatId())){
+            return handleQwRoom(param, fsUser,noRegisterMsg);
         }
         if (isRoom == null || isRoom == 0) {
             // 当 isRoom 为 null 或 0 时走 handleExt
-            return handleExt(param, msg, oneCompanyCourse);
+            return handleExt(param,noMemberMsg, oneCompanyCourse);
         } else if (isRoom == 1) {
             // 当 isRoom 为 1 时走 handleRoom
-            return handleRoom(param, fsUser);
+            return handleRoom(param,fsUser,noRegisterMsg);
         } else {
             // 非法参数
             logger.warn("非法参数 isRoom: {}", isRoom);
@@ -579,11 +563,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         }
 
     }
-
-    private R handleQwRoom(FsUserCourseVideoAddKfUParam param, FsUser user) {
+    private R handleQwRoom(FsUserCourseVideoAddKfUParam param,FsUser user,String noRegisterMsg) {
         FsCourseLink courseLink = courseLinkMapper.selectFsCourseLinkByLink(param.getLink());
-        String msg = "<div style=\"color: red;margin-bottom: 15px;font-weight: bold;\">本课程为群会员独享<br>请长按二维码</div>\n" +
-                "\t\t\t\t\t<div style=\"color: #999;font-size: 14px;font-weight: bold;\">添加伴学助手免费领取会员权限</div>";
+//        String msg = "<div style=\"color: red;margin-bottom: 15px;font-weight: bold;\">本课程为群会员独享<br>请长按二维码</div>\n" +
+//                "\t\t\t\t\t<div style=\"color: #999;font-size: 14px;font-weight: bold;\">添加伴学助手免费领取会员权限</div>";
         QwGroupChat qwGroupChat = qwGroupChatMapper.selectQwGroupChatByChatId(courseLink.getChatId());
         if (qwGroupChat == null) {
             return R.error("群参数异常");
@@ -594,36 +577,94 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         if (qwGroupChatUsers == null || qwGroupChatUsers.isEmpty()) {
             return R.error("群参数异常");
         }
-        //修改成通过昵称匹配
-        QwExternalContact qwExternalContact =
-                qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
+        //群聊寻找用户新逻辑
+        QwExternalContact qwExternalContact = null;
+        if (null != param.getUserId() && null == qwExternalContact) {
+            try {
+                qwExternalContact = qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
                         .eq("user_id", qwGroupChat.getOwner())
-                        .eq("name", user.getNickName())
+                        .eq("fs_user_id", param.getUserId())
                         .eq("corp_id", param.getCorpId())
                         .eq("status", 0));
-        if (qwExternalContact == null) {
-            return addCustomerService(param.getQwUserId(), msg);
+            } catch (Exception e) {
+                log.error("群聊用户id匹配异常,参数user_id:{},fs_user_id:{},corp_id:{}", qwGroupChat.getOwner(), param.getUserId(), param.getCorpId(), e);
+            }
+        }
+        //找当前群中的用户匹配
+        if (StringUtils.isNotBlank(param.getChatId()) && null == qwExternalContact) {
+            List<QwExternalContact> groupChatUserByChatIdAndUserName = qwExternalContactMapper.getGroupChatUserByChatIdAndUserName(qwGroupChat.getOwner(), user.getNickName(), param.getCorpId(), param.getChatId());
+            log.info("群聊用户查询结果,参数user_id:{},name:{},corp_id:{},chatId:{},groupChatUserByChatIdAndUserName:{}", qwGroupChat.getOwner(), user.getNickName(), param.getCorpId(), param.getChatId(), groupChatUserByChatIdAndUserName);
+            //没找到用户 || 找到的用户数量大于1 使用userid查询匹配
+            if (null == groupChatUserByChatIdAndUserName || groupChatUserByChatIdAndUserName.isEmpty() || groupChatUserByChatIdAndUserName.size() > 1) {
+                log.error("群聊用户昵称匹配异常,参数user_id:{},name:{},corp_id:{},chatId:{}", qwGroupChat.getOwner(), user.getNickName(), param.getCorpId(), param.getChatId());
+            } else {
+                qwExternalContact = groupChatUserByChatIdAndUserName.get(0);
+            }
+        }
+//        QwExternalContact qwExternalContact =  qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
+//                            .eq("user_id", qwGroupChat.getOwner())
+//                            .eq("fs_user_id", param.getUserId())
+//                            .eq("corp_id", param.getCorpId())
+//                            .eq("status",0));
+//
+//        if(null == qwExternalContact){
+//            try{
+//                //修改成通过昵称匹配
+//                qwExternalContact =
+//                        qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
+//                                .eq("user_id", qwGroupChat.getOwner())
+//                                .eq("name", user.getNickName())
+//                                .eq("corp_id", param.getCorpId())
+//                                .eq("status",0));
+//            } catch(Exception e){
+//                log.error("群聊用户昵称匹配异常,参数user_id:{},name:{},corp_id:{}",qwGroupChat.getOwner(),user.getNickName(),param.getCorpId(),e);
+//            }
+//
+//        }
+//        QwExternalContact qwExternalContact =
+//                qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
+//                        .eq("user_id", qwGroupChat.getOwner())
+//                        .eq("fs_user_id", param.getUserId())
+//                        .eq("corp_id", param.getCorpId())
+//                        .eq("status",0));
+//        if(null == qwExternalContact){
+//            try{
+//                //修改成通过昵称匹配
+//                qwExternalContact =
+//                        qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
+//                                .eq("user_id", qwGroupChat.getOwner())
+//                                .eq("name", user.getNickName())
+//                                .eq("corp_id", param.getCorpId())
+//                                .eq("status",0));
+//            } catch(Exception e){
+//                log.error("群聊用户昵称匹配异常,参数user_id:{},name:{},corp_id:{}",qwGroupChat.getOwner(),user.getNickName(),param.getCorpId(),e);
+//            }
+//
+//        }
+        if(qwExternalContact==null){
+            return R.error(noRegisterMsg);
         }
-        if (qwGroupChatUsers.stream().noneMatch(e -> e.getUserId().equals(qwExternalContact.getExternalUserId()))) {
+        QwExternalContact finalQwExternalContact = qwExternalContact;
+        if (qwGroupChatUsers.stream().noneMatch(e -> e.getUserId().equals(finalQwExternalContact.getExternalUserId()))) {
             log.error("客户不在群:{},里面:{}", qwGroupChat.getChatId(), qwExternalContact.getExternalUserId());
-            return addCustomerService(param.getQwUserId(), msg);
+            return R.error(noRegisterMsg);
         }
         Long qwExternalId = qwExternalContact.getId();
 //        addCompanyCompanyFsUser(param);
-        FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(qwExternalId, param.getVideoId(), param.getQwUserId());
-        if (log == null) {
-            return addCustomerService(param.getQwUserId(), msg);
+        FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(qwExternalId, param.getVideoId(),param.getQwUserId());
+        if (log==null ){
+            return R.error(noRegisterMsg);
         }
         //判断外部联系人有没有绑定userId
         if (qwExternalContact.getFsUserId() != null) {
             //有客户有小程序id  但 登录的小程序id和根据外部联系人id查出来的小程序id不一致
             if (!qwExternalContact.getFsUserId().equals(param.getUserId())) {
-                return addCustomerService(param.getQwUserId(), msg);
+                return R.error(noRegisterMsg);
             }
             List<QwExternalContact> qwExternalContacts = qwExternalContactMapper.selectQwExternalContactByMiniUserId(param.getUserId());
             //匹配客户公司id
-            if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))) {
-                return addCustomerService(param.getQwUserId(), msg);
+            if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))){
+                return R.error(noRegisterMsg);
             }
 
             //看课记录中userId为0绑定userId
@@ -667,14 +708,16 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         fsUserCompanyBindService.bindFsUser(param.getUserId(), qwExternalId, log.getLogId());
         return R.error(567, "群聊通用链接").put("qwExternalId", qwExternalContact.getId());
     }
-
-    private R handleRoom(FsUserCourseVideoAddKfUParam param, FsUser user) {
+    private R handleRoom(FsUserCourseVideoAddKfUParam param,FsUser user,String noRegisterMsg) {
         //查询客户列表
         List<QwExternalContact> contacts = qwExternalContactMapper.selectQwExternalContactListVOByfsUserId(user.getUserId());
-        log.info("查出来的企微客户数量:" + contacts.size());
-        if (!contacts.isEmpty()) {
+        List<QwExternalContact> nonNullContacts = contacts.stream()
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        log.info("查出来的企微客户数量:" + nonNullContacts.size());
+        if (!nonNullContacts.isEmpty()) {
             //找出对应销售匹配的客户
-            QwExternalContact matchedContact = contacts.stream()
+            QwExternalContact matchedContact = nonNullContacts.stream()
                     .filter(contact -> contact.getQwUserId().equals(Long.parseLong(param.getQwUserId())))
                     .findFirst()
                     .orElse(null);
@@ -690,85 +733,50 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
                     createWatchLog(param);
                 }
                 return R.error(567, "群聊通用链接").put("qwExternalId", matchedContact.getId());
+            } else {
+                QwExternalContact contact = nonNullContacts.get(0);
+                log.info("匹配到的第一个企微用户:" + contact.getUserId());
+                log.info("企微id:" + contact.getId());
+                log.info("用户:" + param.getVideoId());
+                log.info("企微用户:" + param.getQwUserId());
+                param.setQwExternalId(contact.getId());
+                FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(contact.getId(), param.getVideoId(), param.getQwUserId());
+                if (log == null) {
+                    createWatchLog(param);
+                }
+                return R.error(567, "群聊通用链接").put("qwExternalId", contact.getId());
+            }
+        }
+        if ("今正科技".equals(cloudHostProper.getCompanyName())) {
+            QwExternalContact UnionEXt = qwExternalContactMapper.selectQwExternalByUnionID(user.getUnionId());
+            if (UnionEXt != null) {
+                log.info("匹配到的第一个企微用户:" + UnionEXt.getUserId());
+                log.info("企微id:" + UnionEXt.getId());
+                log.info("用户:" + param.getVideoId());
+                log.info("企微用户:" + param.getQwUserId());
+                param.setQwExternalId(UnionEXt.getId());
+                FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(UnionEXt.getId(), param.getVideoId(), param.getQwUserId());
+                if (log == null) {
+                    param.setUserId(user.getUserId());
+                    createWatchLog(param);
+                } else {
+                    if (log.getUserId() == null || log.getUserId().equals(0L) || !log.getUserId().equals(param.getUserId())) {
+                        log.setUserId(param.getUserId());
+                    }
+                    log.setUpdateTime(new Date());
+                    courseWatchLogMapper.updateFsCourseWatchLog(log);
+                }
+                return R.error(567, "群聊通用链接").put("qwExternalId", UnionEXt.getId());
             }
         }
 
-        String msg = "<div style=\"color: red;margin-bottom: 15px;font-weight: bold;\">本课程为群会员独享<br>请长按二维码</div>\n" +
-                "\t\t\t\t\t<div style=\"color: #999;font-size: 14px;font-weight: bold;\">添加伴学助手免费领取会员权限</div>";
 
-        return addCustomerService(param.getQwUserId(), msg);
+     /*   String msg = "<div style=\"color: red;margin-bottom: 15px;font-weight: bold;\">本课程为群会员独享<br>请长按二维码</div>\n" +
+                "\t\t\t\t\t<div style=\"color: #999;font-size: 14px;font-weight: bold;\">添加伴学助手免费领取会员权限</div>";*/
+
+        return R.error(noRegisterMsg);
+
 
-//        QwGroupChatDetailsResult result = qwApiService.groupChatDetails(courseLink.getChatId(), param.getCorpId());
-//        if(result.getErrCode() != 0){
-//            log.info("企微接口请求失败,请联系管理员:" +result.getErrMsg());
-//            return R.error("不是此群成员");
-//        }
-//        List<QwGroupChatDetailsResult.Member> collect = result.getGroupChat().getMemberList().stream().filter(e -> e.getType() == 2).collect(Collectors.toList());
-//        if(collect.isEmpty()){
-//            logger.info("群聊里面为空弹二维码:"+param.getCorpId()+":"+param.getQwUserId()+":"+param.getChatId()+":"+param.getUserId());
-//            return addCustomerService(param.getQwUserId(),msg);
-//        }
-//        Optional<QwGroupChatDetailsResult.Member> optional = collect.stream().filter(e -> e.getName().equals(user.getNickName()) || e.getName().equals(param.getNickName())).findFirst();
-//        if(!optional.isPresent()){
-//            logger.info("昵称未匹配上弹二维码:"+param.getCorpId()+":"+param.getQwUserId()+":"+param.getChatId()+":"+param.getUserId());
-//
-//            return addCustomerService(param.getQwUserId(),msg);
-//        }
-//        QwGroupChatDetailsResult.Member member = optional.get();
-//        QwExternalContact qwExternalContact = qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>().eq("user_id", result.getGroupChat().getOwner()).eq("external_user_id", member.getUserId()));
-//        if(qwExternalContact==null){
-//            logger.info("外部联系人为空弹二维码:"+param.getCorpId()+":"+param.getQwUserId()+":"+param.getChatId()+":"+param.getUserId()+":"+member.getUserId()+param.getNickName());
-//            return addCustomerService(param.getQwUserId(),msg);
-//        }
-//        Long qwExternalId = qwExternalContact.getId();
-//        log.info("外部联系人数据:{}", qwExternalContact);
-////        addCompanyCompanyFsUser(param);
-//        FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(qwExternalId, param.getVideoId(),param.getQwUserId());
-//        if (log==null ){
-//            logger.info("看课记录为空弹二维码:"+param.getCorpId()+":"+param.getQwUserId()+":"+param.getChatId()+":"+param.getUserId()+qwExternalId+":"+param.getVideoId()+":"+param.getQwUserId());
-//            return addCustomerService(param.getQwUserId(),msg);
-//        }
-//        //判断外部联系人有没有绑定userId
-//        if (qwExternalContact.getFsUserId()!=null){
-//            //有客户有小程序id  但 登录的小程序id和根据外部联系人id查出来的小程序id不一致
-//            if (!qwExternalContact.getFsUserId().equals(param.getUserId())) {
-//                logger.info("小程序id不一致空弹二维码:"+param.getCorpId()+":"+param.getQwUserId()+":"+param.getChatId()+":"+param.getUserId());
-//                return addCustomerService(param.getQwUserId(),msg);
-//            }
-//            List<QwExternalContact> qwExternalContacts = qwExternalContactMapper.selectQwExternalContactByMiniUserId(param.getUserId());
-//            //匹配客户公司id
-//            if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))){
-//                logger.info("未匹配上公司空弹二维码:"+param.getCorpId()+":"+param.getQwUserId()+":"+param.getChatId()+":"+param.getUserId());
-//                return addCustomerService(param.getQwUserId(),msg);
-//            }
-//
-//            //看课记录中userId为0绑定userId
-//            if (log.getUserId()==null||log.getUserId().equals(0L) || !log.getUserId().equals(param.getUserId())){
-//                log.setUserId(param.getUserId());
-//            }
-//
-//            log.setUpdateTime(new Date());
-//            courseWatchLogMapper.updateFsCourseWatchLog(log);
-//
-//            iSopUserLogsInfoService.updateSopUserInfoByExternalId(qwExternalId,param.getUserId());
-//        }else {
-//            //没绑定fsUser直接绑定fsUser
-//            QwExternalContact contact = new QwExternalContact();
-//            contact.setId(qwExternalId);
-//            contact.setFsUserId(param.getUserId());
-//            qwExternalContactMapper.updateQwExternalContact(contact);
-//            FsUser fsUser = new FsUser();
-//            fsUser.setUserId(user.getUserId());
-//            fsUser.setIsAddQw(1);
-//            fsUserMapper.updateFsUser(fsUser);
-//            //绑定上之后 更新观看记录
-//            //看课记录中userId为0绑定userId
-//            log.setUserId(param.getUserId());
-//            log.setUpdateTime(new Date());
-//            courseWatchLogMapper.updateFsCourseWatchLog(log);
-//        }
-//
-//        return R.error(567,"群聊通用链接").put("qwExternalId", qwExternalContact.getId());
     }
 
     private void createWatchLog(FsUserCourseVideoAddKfUParam param) {
@@ -819,15 +827,15 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         if (param.getLinkType() != null && param.getLinkType() == 5) {
             log = courseWatchLogMapper.selectFsCourseWatchLogByCourseSopIdAndVideoId(param.getUserId(), param.getVideoId(), param.getQwUserId());
             if (log == null) {
-                return addCustomerService(param.getQwUserId(), msg);
-            } else {
-                qwExternalId = log.getQwExternalContactId();
+                return R.error(msg);
+            }else {
+                qwExternalId=log.getQwExternalContactId();
             }
 
-        } else {
-            log = courseWatchLogMapper.getWatchCourseVideoByExt(qwExternalId, param.getVideoId(), param.getQwUserId());
-            if (log == null) {
-                return addCustomerService(param.getQwUserId(), msg);
+        }else {
+            log = courseWatchLogMapper.getWatchCourseVideoByExt(qwExternalId, param.getVideoId(),param.getQwUserId());
+            if (log==null ){
+                return R.error(msg);
             }
         }
 
@@ -844,19 +852,19 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
 
 
         //如果查不出来客户信息,加好友
-        if (externalContact == null) {
-            return addCustomerService(param.getQwUserId(), msg);
+        if(externalContact==null){
+            return R.error(msg);
         }
 
         //判断外部联系人有没有绑定userId
         if (externalContact.getFsUserId() != null) {
             //有客户有小程序id  但 登录的小程序id和根据外部联系人id查出来的小程序id不一致
             if (!externalContact.getFsUserId().equals(param.getUserId())) {
-                return addCustomerService(param.getQwUserId(), msg);
+                return R.error(msg);
             }
             //匹配客户公司id
-            if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))) {
-                return addCustomerService(param.getQwUserId(), msg);
+            if (qwExternalContacts.stream().noneMatch(contact -> contact.getCorpId().equals(param.getCorpId()))){
+                return R.error(msg);
             }
 
             //看课记录中userId为0绑定userId
@@ -948,7 +956,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
             qwAddContactWayParam.setType(1);
             qwAddContactWayParam.setScene(2);
             qwAddContactWayParam.setUser(users);
-            qwAddContactWayParam.setSkip_verify(true);
+            qwAddContactWayParam.setSkip_verify(false);
             QwAddContactWayResult qwAddContactWayResult = qwApiService.addContactWay(qwAddContactWayParam, qwUser.getCorpId());
             if (qwAddContactWayResult.getErrcode() == 0) {
                 qwUser.setContactWay(qwAddContactWayResult.getQr_code());
@@ -1220,10 +1228,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
                         return R.error("操作频繁,请稍后再试!");
                     }
                 }
-                if (packetLog != null && packetLog.getStatus() == 2) {
-                    return R.error("请联系客服补发");
-                }
-                return R.error("奖励已发放");
             }
             // 获取视频信息
             FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(param.getVideoId());
@@ -1379,10 +1383,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
         // 判断来源是否是app,如是app,则发放积分奖励
-        int sourceApp = 3;
-        if (sourceApp == param.getSource()) {
-            return sendIntegralReward(param, user, log, config);
-        }
+//        int sourceApp = 3;
+//        if (sourceApp == param.getSource()) {
+//            return sendIntegralReward(param, user, log, config);
+//        }
 
         // 根据奖励类型发放不同奖励
         switch (config.getRewardType()) {
@@ -1501,6 +1505,34 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
             }
         }
 
+        FsCourseRedPacketLog redPacketLog = redPacketLogMapper.selectFsCourseRedPacketLogByTemporary(param.getVideoId(), param.getUserId());
+        if (redPacketLog != null && redPacketLog.getStatus() != 2) {
+            return R.error("操作频繁,请稍后再试!");
+        } else {
+            // 添加红包记录
+            redPacketLog = new FsCourseRedPacketLog();
+            redPacketLog.setCourseId(param.getCourseId());
+            redPacketLog.setCompanyId(param.getCompanyId());
+            redPacketLog.setUserId(param.getUserId());
+            redPacketLog.setVideoId(param.getVideoId());
+            redPacketLog.setStatus(2);
+            redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+            redPacketLog.setCompanyUserId(param.getCompanyUserId());
+            redPacketLog.setCreateTime(new Date());
+            redPacketLog.setAmount(amount);
+            redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+            redPacketLog.setPeriodId(param.getPeriodId());
+
+            String code =  OrderCodeUtils.getOrderSn();
+            if (StringUtils.isEmpty(code)) {
+                return R.error("红包单号生成失败,请重试");
+            }
+            redPacketLog.setOutBatchNo("fsCourse"+cloudHostProper.getProjectCode() + code);
+
+            redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+        }
+
+        packetParam.setOutBatchNo(redPacketLog.getOutBatchNo());
         // 发送红包
         R sendRedPacket;
         try {
@@ -1511,29 +1543,20 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         }
 
         if (sendRedPacket.get("code").equals(200)) {
-            FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
             TransferBillsResult transferBillsResult;
+            OffsetDateTime offsetDateTime;
             if (sendRedPacket.get("isNew").equals(1)) {
                 transferBillsResult = (TransferBillsResult) sendRedPacket.get("data");
                 redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
                 redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                offsetDateTime = OffsetDateTime.parse(transferBillsResult.getCreateTime());
             } else {
                 redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                offsetDateTime = OffsetDateTime.parse(sendRedPacket.get("createTime").toString());
             }
-            // 添加红包记录
-            redPacketLog.setCourseId(param.getCourseId());
-            redPacketLog.setCompanyId(param.getCompanyId());
-            redPacketLog.setUserId(param.getUserId());
-            redPacketLog.setVideoId(param.getVideoId());
             redPacketLog.setStatus(0);
-            redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-            redPacketLog.setCompanyUserId(param.getCompanyUserId());
-            redPacketLog.setCreateTime(new Date());
-            redPacketLog.setAmount(amount);
-            redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-            redPacketLog.setPeriodId(param.getPeriodId());
-
-            redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+            redPacketLog.setCreateTime(Date.from(offsetDateTime.toInstant()));
+            redPacketLogMapper.updateFsCourseRedPacketLog(redPacketLog);
             // 更新观看记录的奖励类型
             log.setRewardType(config.getRewardType());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
@@ -1643,6 +1666,36 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
                 }
             }
 
+            FsCourseRedPacketLog redPacketLog = redPacketLogMapper.selectUserFsCourseRedPacketLog(param.getVideoId(), param.getUserId(), param.getPeriodId());
+            if (redPacketLog != null && redPacketLog.getStatus() != 2) {
+                return R.error("操作频繁,请稍后再试!");
+            } else {
+                // 添加红包记录
+                redPacketLog = new FsCourseRedPacketLog();
+                redPacketLog.setCourseId(param.getCourseId());
+                redPacketLog.setCompanyId(param.getCompanyId());
+                redPacketLog.setUserId(param.getUserId());
+                redPacketLog.setVideoId(param.getVideoId());
+                redPacketLog.setStatus(2);
+                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                redPacketLog.setCreateTime(new Date());
+                redPacketLog.setAmount(amount);
+                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                redPacketLog.setPeriodId(param.getPeriodId());
+                redPacketLog.setAppId(param.getAppId());
+
+                String code =  OrderCodeUtils.getOrderSn();
+                if (StringUtils.isEmpty(code)) {
+                    return R.error("红包单号生成失败,请重试");
+                }
+                redPacketLog.setOutBatchNo("fsCourse"+cloudHostProper.getProjectCode() + code);
+
+                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+            }
+
+            packetParam.setOutBatchNo(redPacketLog.getOutBatchNo());
+
             // 发送红包
             R sendRedPacket;
             try {
@@ -1654,32 +1707,22 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
 
             // 红包发送成功处理
             if (sendRedPacket.get("code").equals(200)) {
-                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
                 TransferBillsResult transferBillsResult;
+                OffsetDateTime offsetDateTime;
                 if (sendRedPacket.get("isNew").equals(1)){
                     transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
                     redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
                     redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
                     redPacketLog.setBatchId(transferBillsResult.getTransferBillNo());
+                    offsetDateTime = OffsetDateTime.parse(transferBillsResult.getCreateTime());
                 }else {
                     redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
                     redPacketLog.setBatchId(sendRedPacket.get("batchId").toString());
+                    offsetDateTime = OffsetDateTime.parse(sendRedPacket.get("createTime").toString());
                 }
-                // 添加红包记录
-                redPacketLog.setCourseId(param.getCourseId());
-                redPacketLog.setCompanyId(param.getCompanyId());
-                redPacketLog.setUserId(param.getUserId());
-                redPacketLog.setVideoId(param.getVideoId());
                 redPacketLog.setStatus(0);
-                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-                redPacketLog.setCompanyUserId(param.getCompanyUserId());
-                redPacketLog.setCreateTime(new Date());
-                redPacketLog.setAmount(amount);
-                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-                redPacketLog.setPeriodId(param.getPeriodId());
-                redPacketLog.setAppId(param.getAppId());
-
-                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+                redPacketLog.setCreateTime(Date.from(offsetDateTime.toInstant()));
+                redPacketLogMapper.updateFsCourseRedPacketLog(redPacketLog);
 
                 // 更新观看记录的奖励类型
                 log.setRewardType(config.getRewardType());
@@ -3466,6 +3509,291 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         return R.ok();
     }
 
+    @Override
+    public R uploadVideoToHuoShanByUrl() {
+        List <FsVideoResource> videos = fsUserCourseVideoMapper.selectVideoByHuaWei();
+        for (FsVideoResource video : videos){
+            uploadVideoByUrl(video);
+        }
+        return R.ok();
+    }
+
+    @Autowired
+    private IVodService vodService;
+
+    //通过Url上传视频
+    public void uploadVideoByUrl(FsVideoResource videoResource) {
+
+        try {
+            VodUrlUploadRequest.Builder reqBuilder = VodUrlUploadRequest.newBuilder();
+            //空间名称
+            reqBuilder.setSpaceName(cloudHostProper.getSpaceName());
+            VodUrlUploadURLSet.Builder uRLSetsBuilder = VodUrlUploadURLSet.newBuilder();
+            //源文件 URL
+            uRLSetsBuilder.setSourceUrl(videoResource.getLine2());//华为云
+            //存储类型。默认为 1。取值如下:
+            //1:标准存储。
+            //2:归档存储。
+            //3:低频存储。
+            uRLSetsBuilder.setStorageClass(1);
+            //文件后缀,即点播存储中文件的类型。
+            uRLSetsBuilder.setFileExtension(".mp4");
+            //用户额外信息,最大长度 512 字节。
+            uRLSetsBuilder.setCallbackArgs("");
+            // 火山云存储路径(文件上传后在火山云的路径)
+//            String datePath = new SimpleDateFormat("yyyyMMdd").format(new Date());
+//            String fileName = System.currentTimeMillis() + ".mp4";
+//            String remoteFileName = "fs/" + datePath + "/" + fileName;
+
+            uRLSetsBuilder.setFileName(videoResource.getFileKey());
+            reqBuilder.addURLSets(uRLSetsBuilder);
+
+            VodUrlUploadResponse resp = vodService.uploadMediaByUrl(reqBuilder.build());
+
+            if (resp.getResponseMetadata().hasError()) {
+                log.info("上传返回异常:{}",resp.getResponseMetadata().getError());
+                System.exit(-1);
+            }else {
+                FsVideoResource video = new FsVideoResource();
+                video.setId(videoResource.getId());
+                video.setJobId(resp.getResult().getData(0).getJobId());
+                //更新JobId
+                fsVideoResourceMapper.updateById(video);
+            }
+            log.info("上传返回参数:{}",resp);
+        } catch (Exception e) {
+            throw new RuntimeException("视频上传失败: " + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public R getVidByJob() {
+        // 查询有JobId的视频
+        List<FsVideoResource> list = fsUserCourseVideoMapper.selectVideoByJobId();
+        if (list.isEmpty()) {
+            log.info("没有待上传的视频任务");
+            return R.error();
+        }
+        // 按五百一批切割
+        List<List<FsVideoResource>> batches = splitList(list, 500);
+        log.info("总任务 {} 条,分成 {} 批", list.size(), batches.size());
+
+        int batchIndex = 1;
+
+        // 批次顺序执行,每批内部多线程并发执行
+        for (List<FsVideoResource> batch : batches) {
+
+            log.info("开始执行批次 {}/{},本批任务 {} 条", batchIndex, batches.size(), batch.size());
+
+            CountDownLatch latch = new CountDownLatch(batch.size());
+
+            for (FsVideoResource video : batch) {
+                uploadExecutor.submit(() -> {
+                    try {
+                        uploadSingleTaskWithRetry(video,1);
+                    } finally {
+                        latch.countDown();
+                    }
+                });
+            }
+
+            // 等待这一批全部完成
+            try {
+                latch.await();
+            } catch (InterruptedException e) {
+                log.error("批次等待异常", e);
+            }
+
+            log.info("批次 {}/{} 执行完成", batchIndex, batches.size());
+            batchIndex++;
+        }
+
+        log.info("全部批次执行完成");
+        return R.ok();
+    }
+
+    @Override
+    public R getVideoInfoByVid() {
+        // 查询有JobId的视频
+        List<FsVideoResource> list = fsUserCourseVideoMapper.selectVideoByVid();
+        if (list.isEmpty()) {
+            log.info("没有包含vid的视频");
+            return R.error();
+        }
+        // 按五百一批切割
+        List<List<FsVideoResource>> batches = splitList(list, 500);
+        log.info("总任务 {} 条,分成 {} 批", list.size(), batches.size());
+
+        int batchIndex = 1;
 
+        // 批次顺序执行,每批内部多线程并发执行
+        for (List<FsVideoResource> batch : batches) {
+
+            log.info("开始执行批次 {}/{},本批任务 {} 条", batchIndex, batches.size(), batch.size());
+
+            CountDownLatch latch = new CountDownLatch(batch.size());
+
+            for (FsVideoResource video : batch) {
+                uploadExecutor.submit(() -> {
+                    try {
+                        uploadSingleTaskWithRetry(video,2);
+                    } finally {
+                        latch.countDown();
+                    }
+                });
+            }
+
+            // 等待这一批全部完成
+            try {
+                latch.await();
+            } catch (InterruptedException e) {
+                log.error("批次等待异常", e);
+            }
+
+            log.info("批次 {}/{} 执行完成", batchIndex, batches.size());
+            batchIndex++;
+        }
+
+        log.info("全部批次执行完成");
+        return R.ok();
+    }
+
+    private final ExecutorService uploadExecutor = new ThreadPoolExecutor(
+            8,  // core
+            16, // max
+            60L, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<>(2000),
+            new ThreadFactory() {
+                private final AtomicInteger index = new AtomicInteger(1);
+
+                @Override
+                public Thread newThread(Runnable r) {
+                    return new Thread(r, "video-upload-" + index.getAndIncrement());
+                }
+            },
+            new ThreadPoolExecutor.CallerRunsPolicy()
+    );
+
+    public <T> List<List<T>> splitList(List<T> list, int batchSize) {
+        List<List<T>> result = new ArrayList<>();
+        int total = list.size();
+        for (int i = 0; i < total; i += batchSize) {
+            result.add(list.subList(i, Math.min(total, i + batchSize)));
+        }
+        return result;
+    }
+
+    //根据jobid查询上传视频的vid
+    public void getVidByJobId(FsVideoResource videoResource){
+        try {
+            VodQueryUploadTaskInfoRequest.Builder reqBuilder = VodQueryUploadTaskInfoRequest.newBuilder();
+            reqBuilder.setJobIds(videoResource.getJobId());
+
+            VodQueryUploadTaskInfoResponse resp = vodService.queryUploadTaskInfo(reqBuilder.build());
+            if (resp.getResponseMetadata().hasError()) {
+                System.out.println(resp.getResponseMetadata().getError());
+                System.exit(-1);
+            }else {
+                FsVideoResource video = new FsVideoResource();
+                video.setId(videoResource.getId());
+                //视频上传失败,清空jobid
+                if (StringUtils.isEmpty(resp.getResult().getData().getMediaInfoList(0).getVid())){
+                    video.setJobId("");
+                }else {
+                    video.setHsyVid(resp.getResult().getData().getMediaInfoList(0).getVid());
+                }
+                fsVideoResourceMapper.updateById(video);
+            }
+            System.out.println(resp);
+        } catch (Exception e) {
+            throw new RuntimeException("查询 URL 批量上传任务状态: " + e.getMessage(), e);
+        }
+    }
+
+
+    //根据vid获取视频详情
+    public void getVideoInfoByVid(FsVideoResource videoResource) {
+        try {
+            VodGetMediaInfosRequest.Builder reqBuilder = VodGetMediaInfosRequest.newBuilder();
+            reqBuilder.setVids(videoResource.getHsyVid());
+
+            VodGetMediaInfosResponse resp = vodService.getMediaInfos20230701(reqBuilder.build());
+            if (resp.getResponseMetadata().hasError()) {
+                System.out.println(resp.getResponseMetadata().getError());
+                System.exit(-1);
+            }else {
+                //如果路径是空的直接返回
+                if (StringUtils.isEmpty(resp.getResult().getMediaInfoList(0).getSourceInfo().getStoreUri())){
+                    return;
+                }
+                //如果是未发布状态修改发布状态
+                if (resp.getResult().getMediaInfoList(0).getBasicInfo().getPublishStatus().equals("Unpublished")){
+                    updateMediaPublishStatus(videoResource.getHsyVid());
+                }
+                //更新视频资源
+//                String storeUri = resp.getResult().getMediaInfoList(0).getSourceInfo().getStoreUri();
+//                String uri = storeUri.substring(storeUri.indexOf("/") + 1);
+                String url = cloudHostProper.volcengineUrl+"/"+resp.getResult().getMediaInfoList(0).getSourceInfo().getFileName();
+
+                FsVideoResource fsVideoResource = new FsVideoResource();
+                fsVideoResource.setId(videoResource.getId());
+                fsVideoResource.setLine2(url);
+                fsVideoResourceMapper.updateById(fsVideoResource);
+
+                //更新小节
+                FsUserCourseVideo courseVideo = new FsUserCourseVideo();
+                courseVideo.setFileKey(videoResource.getFileKey());
+                courseVideo.setLineTwo(url);
+                fsUserCourseVideoMapper.updateFsUserCourseVideoByFileKeyForHsy(courseVideo);
+
+            }
+            System.out.println(resp);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    //修改发布状态
+    public void updateMediaPublishStatus(String vid){
+        String statusPublished = "Published";
+
+        try {
+            // publish
+            VodUpdateMediaPublishStatusRequest.Builder req = VodUpdateMediaPublishStatusRequest.newBuilder();
+            req.setVid(vid);
+            req.setStatus(statusPublished);
+
+            VodUpdateMediaPublishStatusResponse resp = vodService.updateMediaPublishStatus(req.build());
+            System.out.println(resp);
+
+            Thread.sleep(1000);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    public void uploadSingleTaskWithRetry(FsVideoResource videoResource,Integer type) {
+        int maxRetry = 3;
+        for (int i = 1; i <= maxRetry; i++) {
+            try {
+                if (type == 1){
+                    //获取上传成功的视频vid,同步到数据库
+                    getVidByJobId(videoResource);
+                }else if (type == 2){
+                    //获取视频地址同步到线路二
+                    getVideoInfoByVid(videoResource);
+                }
+                return;
+            } catch (Exception e) {
+                log.error("视频 {} 上传失败,第 {} 次重试,原因:{}",
+                        videoResource.getId(), i, e.getMessage());
+                if (i == maxRetry) {
+                    log.error("视频 {} 上传最终失败!", videoResource.getId());
+                }
+            }
+        }
+    }
 }
 

+ 122 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserVideoServiceImpl.java

@@ -1,10 +1,14 @@
 package com.fs.course.service.impl;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
@@ -20,11 +24,20 @@ import com.fs.course.vo.FsUserVideoPVO;
 import com.fs.course.param.FsUserVideoParam;
 import com.fs.course.vo.FsUserVideoListUVO;
 
+import com.volcengine.helper.VodUploadProgressListener;
+import com.volcengine.model.beans.Functions;
+import com.volcengine.service.vod.IVodService;
+import com.volcengine.service.vod.model.request.VodGetMediaInfosRequest;
+import com.volcengine.service.vod.model.request.VodUploadMediaRequest;
+import com.volcengine.service.vod.model.response.VodCommitUploadInfoResponse;
+import com.volcengine.service.vod.model.response.VodGetMediaInfosResponse;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import com.fs.course.domain.FsUserVideo;
 import com.fs.course.service.IFsUserVideoService;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * 课堂视频Service业务层处理
@@ -32,6 +45,7 @@ import com.fs.course.service.IFsUserVideoService;
  * @author fs
  * @date 2024-05-15
  */
+@Slf4j
 @Service
 public class FsUserVideoServiceImpl implements IFsUserVideoService {
     @Autowired
@@ -46,6 +60,9 @@ public class FsUserVideoServiceImpl implements IFsUserVideoService {
     private IRecommendationService recommendationService;
     @Autowired
     private RedisTemplate redisTemplate;
+
+    @Autowired
+    private IVodService vodService;
     private static final String LIKE_KEY_PREFIX = "like:video:";
     private static final String FAVORITE_KEY_PREFIX = "favorite:video:";
 
@@ -444,5 +461,110 @@ public class FsUserVideoServiceImpl implements IFsUserVideoService {
         return url;
     }
 
+    //本地文件视频上传
+    @Override
+    public String uploadVideo(MultipartFile file, String uploadId) {
+        log.info("上传视频开始",uploadId);
+        File tempFile = null;
+        try {
+            // 将 MultipartFile 转换为临时文件
+            String originalFilename = file.getOriginalFilename();
+            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
+            tempFile = File.createTempFile("upload_", suffix);
+            file.transferTo(tempFile);
+
+            // 点播空间名称
+            String space = "rt-huoshan";
+            // 本地临时文件路径(用于上传)
+            String localFilePath = tempFile.getAbsolutePath();
+
+            // 火山云存储路径(文件上传后在火山云的路径)
+            String datePath = new SimpleDateFormat("yyyyMMdd").format(new Date());
+            String fileName = System.currentTimeMillis() + ".mp4";
+            String remoteFileName = "course/" + datePath + "/" + fileName;
+
+            // 上传功能函数,可用于实现截图、设置媒资信息、触发工作流进行转码等功能。详见[上传功能函数说明](https://www.volcengine.com/docs/4/1185644)
+            List<Functions> functionsList = new ArrayList<>();
+            Functions getMetaFunc = Functions.GetMetaFunction();
+            functionsList.add(getMetaFunc);
+            Functions snapShotFunc = Functions.SnapShotFunction(2.3);
+            functionsList.add(snapShotFunc);
+            Functions addOptionInfo = Functions.AddOptionInfoFunction(fileName, "test", "用户上传视频", 0, true);
+            functionsList.add(addOptionInfo);
+
+            // 使用工作流进行转码
+
+            List<String> impTemplateIds = new ArrayList<>();
+//            impTemplateIds.add("a4a7066c968344e9bf7a56ec0891fbb8");
+//            FunctionsWorkflowTemplate impTemplate = new FunctionsWorkflowTemplate(impTemplateIds, "imp");
+//
+//            List<String> transcodeTemplateIds = new ArrayList<>();
+//            transcodeTemplateIds.add("a4a7066c968344e9bf7a56ec0891fbb8");
+//            FunctionsWorkflowTemplate transcodeTemplate = new FunctionsWorkflowTemplate(transcodeTemplateIds,
+//                    "transcode");
+
+//            List<FunctionsWorkflowTemplate> templates = new ArrayList<>();
+//            templates.add(impTemplate);
+//            templates.add(transcodeTemplate);
+
+//            Functions workflowFunc = Functions.StartWorkFlowFunction(templates);
+//            functionsList.add(workflowFunc);
+
+            VodUploadMediaRequest vodUploadMediaRequest = VodUploadMediaRequest.newBuilder()
+                    .setSpaceName(space)
+                    .setFilePath(localFilePath)  // 使用本地临时文件路径
+                    .setFileName(remoteFileName)  // 火山云存储路径
+                    .setFunctions(JSON.toJSONString(functionsList))
+                    .setStorageClass(1)
+                    .build();
+
+            // 需要进度条功能时添加相应 listener,如无需求,传 null 值即可
+            VodUploadProgressListener listener = new VodUploadMediaProcessListener(uploadId, redisTemplate);
+
+            VodCommitUploadInfoResponse vodCommitUploadInfoResponse = vodService.uploadMedia(vodUploadMediaRequest, listener);
+
+            redisTemplate.opsForValue()
+                    .set("vod:upload:" + uploadId, 100, 10, TimeUnit.MINUTES);
+
+            if (vodCommitUploadInfoResponse.getResponseMetadata().hasError()) {
+                throw new RuntimeException("火山云上传失败: " + vodCommitUploadInfoResponse.getResponseMetadata().getError());
+            }
+
+            // 返回视频 VID
+            String vid = vodCommitUploadInfoResponse.getResult().getData().getVid();
+            System.out.println("视频上传成功,VID: " + vid);
+            System.out.println("RequestId: " + vodCommitUploadInfoResponse.getResponseMetadata().getRequestId());
+
+            return vid;
+        } catch (Exception e) {
+            throw new RuntimeException("视频上传失败: " + e.getMessage(), e);
+        } finally {
+            // 删除临时文件
+            if (tempFile != null && tempFile.exists()) {
+                tempFile.delete();
+            }
+        }
+    }
+
+    //根据vid查询视频信息
+    @Override
+    public String getVideoInfoByVid(String vid) {
+        try {
+            VodGetMediaInfosRequest.Builder reqBuilder = VodGetMediaInfosRequest.newBuilder();
+            reqBuilder.setVids("vid");
+
+            VodGetMediaInfosResponse resp = vodService.getMediaInfos20230701(reqBuilder.build());
+            if (resp.getResponseMetadata().hasError()) {
+                System.out.println(resp.getResponseMetadata().getError());
+                System.exit(-1);
+            }else {
+                return resp.getResult().getMediaInfoList(0).getSourceInfo().getStoreUri();
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
 
 }

+ 47 - 0
fs-service/src/main/java/com/fs/course/service/impl/VodUploadMediaProcessListener.java

@@ -0,0 +1,47 @@
+package com.fs.course.service.impl;
+
+
+import com.volcengine.helper.VodUploadProgressEvent;
+import com.volcengine.helper.VodUploadProgressListener;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+public class VodUploadMediaProcessListener implements VodUploadProgressListener {
+
+    private long bytesUploaded = 0;
+    private long fileSize = -1;
+
+    private final String uploadId;
+    private final RedisTemplate<String, Object> redisTemplate;
+
+    public VodUploadMediaProcessListener(String uploadId,
+                                         RedisTemplate redisTemplate) {
+        this.uploadId = uploadId;
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Override
+    public void progressChanged(VodUploadProgressEvent event) {
+        switch (event.getEventType()) {
+            case FILE_SIZE_EVENT:
+                fileSize = event.getByteSize();
+                break;
+
+            case UPLOAD_BYTES_EVENT:
+                bytesUploaded += event.getByteSize();
+                if (fileSize > 0) {
+                    int percent = Math.min(
+                            (int) (bytesUploaded * 100.0 / fileSize),
+                            100
+                    );
+                    redisTemplate.opsForValue()
+                            .set("vod:upload:" + uploadId, percent, 10, TimeUnit.MINUTES);
+                    log.info("uploadId={} progress={}%", uploadId, percent);
+                }
+                break;
+        }
+    }
+}

+ 1 - 0
fs-service/src/main/java/com/fs/erp/domain/ErpOrder.java

@@ -34,4 +34,5 @@ public class ErpOrder {
     List<ErpOrderPayment> payments;
 
     String buyer_account;
+    Boolean isIntegralOrder;
 }

+ 7 - 0
fs-service/src/main/java/com/fs/erp/service/IErpOrderService.java

@@ -5,6 +5,7 @@ import com.fs.erp.domain.ErpRefundOrder;
 import com.fs.erp.dto.*;
 import com.fs.his.domain.FsStoreOrder;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
+import com.fs.live.domain.LiveOrder;
 
 public interface IErpOrderService
 {
@@ -13,19 +14,25 @@ public interface IErpOrderService
 
     ErpOrderResponse addOrderScrm(ErpOrder order);
 
+    ErpOrderResponse addLiveOrder(ErpOrder order);
+
     //用户发起退款,然后后台审核通过后,提交退款单
     ErpOrderResponse refundOrder(ErpRefundOrder order);
     ErpOrderResponse refundOrderScrm(ErpRefundOrder order);
+    ErpOrderResponse refundLiveOrder(ErpRefundOrder order);
     ErpDeliverysResponse getDeliver(ErpDeliverysRequest param);
     ErpOrderQueryResponse getOrder(ErpOrderQueryRequert param);
     ErpOrderQueryResponse getScrmOrder(ErpOrderQueryRequert param);
+    ErpOrderQueryResponse getLiveOrder(ErpOrderQueryRequert param);
     BaseResponse refundUpdate(ErpRefundUpdateRequest param);
     BaseResponse refundUpdateScrm(ErpRefundUpdateRequest param);
+    BaseResponse refundUpdateLive(ErpRefundUpdateRequest param);
 
     ErpOrderResponse finishOrder(ErpOrder order);
     //代服管家查物流状态
     void getOrderDeliveryStatus(FsStoreOrder order);
 
     void getOrderScrmDeliveryStatus(FsStoreOrderScrm order);
+    void getOrderLiveDeliveryStatus(LiveOrder order);
 }
 

Diferenças do arquivo suprimidas por serem muito extensas
+ 763 - 26
fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java


+ 29 - 0
fs-service/src/main/java/com/fs/erp/service/impl/ErpOrderServiceImpl.java

@@ -12,6 +12,8 @@ import com.fs.his.config.FsSysConfig;
 import com.fs.his.domain.FsStoreOrder;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
+import com.fs.live.domain.LiveOrder;
+import com.fs.live.mapper.LiveOrderMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -23,6 +25,8 @@ public class ErpOrderServiceImpl implements IErpOrderService
     public final static Logger LOGGER = LoggerFactory.getLogger(ErpOrderServiceImpl.class);
     @Autowired
     ConfigUtil configUtil;
+    @Autowired
+    private LiveOrderMapper liveOrderMapper;
     @Override
     public ErpOrderResponse addOrder(ErpOrder order) {
         FsSysConfig sysConfig = configUtil.getSysConfig();
@@ -65,6 +69,11 @@ public class ErpOrderServiceImpl implements IErpOrderService
         return addOrder( order);
     }
 
+    @Override
+    public ErpOrderResponse addLiveOrder(ErpOrder order) {
+        return addOrder(order);
+    }
+
     @Override
     public ErpOrderResponse refundOrder(ErpRefundOrder order) {
         FsSysConfig sysConfig = configUtil.getSysConfig();
@@ -89,6 +98,11 @@ public class ErpOrderServiceImpl implements IErpOrderService
         return null;
     }
 
+    @Override
+    public ErpOrderResponse refundLiveOrder(ErpRefundOrder order) {
+        return null;
+    }
+
     @Override
     public ErpDeliverysResponse getDeliver(ErpDeliverysRequest request) {
         FsSysConfig sysConfig = configUtil.getSysConfig();
@@ -129,6 +143,11 @@ public class ErpOrderServiceImpl implements IErpOrderService
         return null;
     }
 
+    @Override
+    public ErpOrderQueryResponse getLiveOrder(ErpOrderQueryRequert param) {
+        return null;
+    }
+
     @Override
     public BaseResponse refundUpdate(ErpRefundUpdateRequest request) {
         FsSysConfig sysConfig = configUtil.getSysConfig();
@@ -150,6 +169,11 @@ public class ErpOrderServiceImpl implements IErpOrderService
         return null;
     }
 
+    @Override
+    public BaseResponse refundUpdateLive(ErpRefundUpdateRequest param) {
+        return null;
+    }
+
     @Override
     public ErpOrderResponse finishOrder(ErpOrder order) {
         return null;
@@ -164,4 +188,9 @@ public class ErpOrderServiceImpl implements IErpOrderService
     public void getOrderScrmDeliveryStatus(FsStoreOrderScrm order) {
 
     }
+
+    @Override
+    public void getOrderLiveDeliveryStatus(LiveOrder order) {
+
+    }
 }

+ 163 - 0
fs-service/src/main/java/com/fs/erp/service/impl/HzOMSErpOrderServiceImpl.java

@@ -14,6 +14,14 @@ import com.fs.his.domain.*;
 import com.fs.his.service.*;
 import com.fs.his.utils.PhoneUtil;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
+import com.fs.hisStore.domain.FsStoreProductAttrValueScrm;
+import com.fs.hisStore.domain.FsStoreProductScrm;
+import com.fs.hisStore.service.IFsStoreProductAttrValueScrmService;
+import com.fs.hisStore.service.IFsStoreProductScrmService;
+import com.fs.live.domain.LiveOrder;
+import com.fs.live.domain.LiveOrderItem;
+import com.fs.live.mapper.LiveOrderItemMapper;
+import com.fs.live.mapper.LiveOrderMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -49,6 +57,18 @@ public class HzOMSErpOrderServiceImpl implements IErpOrderService {
     @Autowired
     HzOMSClient hzOMSClient;
 
+    @Autowired
+    private LiveOrderMapper liveOrderMapper;
+
+    @Autowired
+    private LiveOrderItemMapper liveOrderItemMapper;
+
+    @Autowired
+    private IFsStoreProductScrmService fsStoreProductScrmService;
+
+    @Autowired
+    private IFsStoreProductAttrValueScrmService fsStoreProductAttrValueScrmService;
+
     @Override
     public ErpOrderResponse addOrder(ErpOrder order) {
         try {
@@ -69,6 +89,129 @@ public class HzOMSErpOrderServiceImpl implements IErpOrderService {
         return addOrder( order);
     }
 
+    @Override
+    public ErpOrderResponse addLiveOrder(ErpOrder order) {
+        try {
+            JSONObject hzOrder = buildHzOMSOrderLive(order.getPlatform_code());
+
+            JSONObject omsOpenPtorderCreate = hzOMSClient.send("oms_open_ptorder_create", hzOrder);
+            ErpOrderResponse res = new ErpOrderResponse();
+            res.setCode("hzomssuccess");
+            return res;
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+        return null;
+    }
+
+    private JSONObject buildHzOMSOrderLive(String orderCode) {
+        //通过订单号查询更多订单信息
+        LiveOrder liveOrder = liveOrderMapper.selectLiveOrderByOrderCode(orderCode);
+        FsStore fsStore = fsStoreService.selectFsStoreByStoreId(liveOrder.getStoreId());
+
+        JSONObject obj = new JSONObject();
+        String iordertype = "2"; // 直播订单默认B2C快递配送
+        //订单类型 0 O2O配送订单; 1 O2O 自提订单; 2 B2C 快递配送
+        obj.put("iordertype", iordertype);
+        //订单单号
+        obj.put("cptordercode", liveOrder.getOrderCode());
+        //平台店铺编码 todo 测试环境只能写死 110111 用于联调测试 liveOrder.getStoreId()
+        obj.put("cptshopcode", 110111);
+        //平台店铺名称
+        obj.put("cptshopname", fsStore.getStoreName());
+        Integer iorderstatus = null;
+        //自提且已经支付
+        if ("1".equals(iordertype) && Integer.valueOf(1).equals(liveOrder.getIsPay())) {
+            iorderstatus = 30;
+        } else {
+            if (Integer.valueOf(1).equals(liveOrder.getStatus())) {
+                iorderstatus = 0;
+            } else if (Integer.valueOf(2).equals(liveOrder.getStatus())) {
+                iorderstatus = 5;
+            }
+        }
+        //订单状态 0待接单 5待拣货 10待配送 15配送中 20已完成 25已取消 30待自提
+        obj.put("iorderstatus", iorderstatus);
+
+        String userAddress = liveOrder.getUserAddress();
+        String[] s = null;
+        if (StringUtils.isNotBlank(userAddress)) {
+            s = userAddress.split(" ");
+        } else {
+            throw new RuntimeException("用户收货地址有问题");
+        }
+
+        //收货省
+        obj.put("cprovince", s[0]);
+        //收货市
+        obj.put("ccity", s[1]);
+        //收货区
+        obj.put("carea", s[2]);
+        //收货人密文 无密文填写明文
+        obj.put("creceivers", liveOrder.getRealName());
+        //收货人联系电话密文 无密文填写明文
+        obj.put("creceiversphone", liveOrder.getUserPhone());
+        //收货人详细地址密文 无密文填写明文
+        obj.put("creceiversaddress", userAddress);
+        //收货人脱敏信息
+        obj.put("cdesenreceivers", liveOrder.getRealName());
+        //收货联系电话脱敏信息
+        obj.put("cdesenreceiversphone", PhoneUtil.decryptPhone(liveOrder.getUserPhone()));
+        //收货联系地址脱敏信息
+        obj.put("cdesenreceiversaddress", userAddress);
+        //订单总金额 单位元
+        obj.put("forderamount", liveOrder.getTotalPrice());
+        //支付金额 单位元
+        obj.put("fpayamount", liveOrder.getPayPrice());
+        //订单总优惠 单位元
+        obj.put("fdiscamount", liveOrder.getDiscountMoney());
+        //运费
+        obj.put("ffreight", liveOrder.getPayDelivery());
+        //下单时间 格式 yyyy-MM-dd hh:mm:ss 如:2025-01-01 00:11:22
+        obj.put("dorderstarttime", liveOrder.getCreateTime());
+        //是否处方 0 否 1 是 可选
+        obj.put("iisprescription", 0);
+        //买家备注
+        obj.put("corderremark", liveOrder.getRemark());
+        //支付时间  格式 yyyy-MM-dd hh:mm:ss 如:2025-01-01 00:11:22
+        obj.put("dpurchasetime", liveOrder.getPayTime());
+        List<LiveOrderItem> liveOrderItems = liveOrderItemMapper.selectLiveOrderItemByOrderId(liveOrder.getOrderId());
+        int totalOrderItemCount = liveOrderItems.size();
+        BigDecimal divide = liveOrder.getDiscountMoney().divide(BigDecimal.valueOf(totalOrderItemCount), 2, BigDecimal.ROUND_HALF_UP);
+        JSONArray goodArr = new JSONArray();
+
+        liveOrderItems.forEach(liveOrderItem -> {
+            JSONObject goodItem = new JSONObject();
+            //商品编码
+            goodItem.put("cptgoodsid", liveOrderItem.getProductId());
+            //商品规格编码
+            goodItem.put("cptspeccode", liveOrderItem.getProductAttrValueId());
+
+            FsStoreProductScrm fsStoreProduct = fsStoreProductScrmService.selectFsStoreProductById(liveOrderItem.getProductId());
+            FsStoreProductAttrValueScrm fsStoreProductAttrValue = new FsStoreProductAttrValueScrm();
+            //判断是否含有商品规格信息 有则查询商品规格信息 没有取商品的价格
+            if(null != liveOrderItem.getProductAttrValueId()){
+                fsStoreProductAttrValue = fsStoreProductAttrValueScrmService.selectFsStoreProductAttrValueById(liveOrderItem.getProductAttrValueId());
+            }else{
+                fsStoreProductAttrValue.setPrice(fsStoreProduct.getPrice());
+            }
+            //商品名称
+            goodItem.put("cgoodsname", fsStoreProduct.getProductName());
+            //购买数量
+            goodItem.put("fqty", liveOrderItem.getNum());
+            //原单价
+            goodItem.put("fnormprice", fsStoreProductAttrValue.getPrice());
+
+            //实售单价 优惠后的单价 单位元
+            goodItem.put("fprice", fsStoreProductAttrValue.getPrice().subtract(divide));
+            goodArr.add(goodItem);
+        });
+        //订单商品信息
+        obj.put("ptorder_goods_list", goodArr);
+
+        return obj;
+    }
+
     @Override
     public ErpOrderResponse refundOrder(ErpRefundOrder order) {
         String orderCode = order.getOrderCode();
@@ -83,6 +226,11 @@ public class HzOMSErpOrderServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public ErpOrderResponse refundLiveOrder(ErpRefundOrder order) {
+        return null;
+    }
+
     @Override
     public ErpDeliverysResponse getDeliver(ErpDeliverysRequest param) {
         return null;
@@ -98,6 +246,11 @@ public class HzOMSErpOrderServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public ErpOrderQueryResponse getLiveOrder(ErpOrderQueryRequert param) {
+        return null;
+    }
+
     @Override
     public BaseResponse refundUpdate(ErpRefundUpdateRequest param) {
 
@@ -167,6 +320,11 @@ public class HzOMSErpOrderServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public BaseResponse refundUpdateLive(ErpRefundUpdateRequest param) {
+        return null;
+    }
+
     @Override
     public ErpOrderResponse finishOrder(ErpOrder order) {
         return null;
@@ -182,6 +340,11 @@ public class HzOMSErpOrderServiceImpl implements IErpOrderService {
 
     }
 
+    @Override
+    public void getOrderLiveDeliveryStatus(LiveOrder order) {
+
+    }
+
     /**
      * 构建瀚智创建订单参数
      *

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

@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.fs.common.utils.StringUtils;
 import com.fs.erp.constant.AfterSalesOrderStatusEnum;
 import com.fs.erp.constant.ErpQueryOrderStatusEnum;
 import com.fs.erp.constant.OrderStatusEnum;
@@ -31,6 +32,10 @@ import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.service.IFsStoreProductScrmService;
 import com.fs.hisStore.service.impl.FsStoreProductScrmServiceImpl;
 import com.fs.hisStore.vo.FsStoreOrderItemVO;
+import com.fs.live.domain.LiveOrder;
+import com.fs.live.domain.LiveOrderItem;
+import com.fs.live.mapper.LiveOrderItemMapper;
+import com.fs.live.mapper.LiveOrderMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.http.util.Asserts;
@@ -40,10 +45,7 @@ import org.springframework.util.CollectionUtils;
 
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -76,6 +78,12 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
     @Autowired
     private IFsStoreProductScrmService fsStoreProductScrmService;
 
+    @Autowired
+    private LiveOrderMapper liveOrderMapper;
+
+    @Autowired
+    private LiveOrderItemMapper liveOrderItemMapper;
+
     @Override
     public ErpOrderResponse addOrder(ErpOrder order) {
         FsStoreOrder fsStoreOrder = fsStoreOrderService.selectFsStoreOrderByOrderCode(order.getPlatform_code());
@@ -327,7 +335,127 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         return erpOrderResponse;
     }
 
+    @Override
+    public ErpOrderResponse addLiveOrder(ErpOrder order) {
+        LiveOrder liveOrder = liveOrderMapper.selectLiveOrderByOrderCode(order.getPlatform_code());
+        log.info("ERP订单号: {}, 订单信息: {}",order.getPlatform_code(), JSON.toJSONString(liveOrder));
+
+        ErpOrderPayment erpOrderPayment = order.getPayments().get(0);
 
+        ShopOrderDTO shopOrderDTO = new ShopOrderDTO();
+
+        shopOrderDTO.setShopId(Long.valueOf(order.getShop_code()));
+        shopOrderDTO.setSoId(order.getPlatform_code());
+        shopOrderDTO.setOrderDate(order.getDeal_datetime());
+        // 待发货
+        shopOrderDTO.setShopStatus(OrderStatusEnum.WAIT_SELLER_SEND_GOODS.name());
+        // 买家账号
+        shopOrderDTO.setShopBuyerId(order.getBuyer_account());
+        // 收货人省份
+        shopOrderDTO.setReceiverState(order.getReceiver_province());
+        // 收货人城市
+        shopOrderDTO.setReceiverCity(order.getReceiver_city());
+        // 收货人区域
+        shopOrderDTO.setReceiverDistrict(order.getReceiver_district());
+        // 收货人详细地址
+        shopOrderDTO.setReceiverAddress(order.getReceiver_address());
+        // 收货人详细地址
+        shopOrderDTO.setReceiverName(order.getReceiver_name());
+        // 收货人电话
+        shopOrderDTO.setReceiverPhone(order.getReceiver_mobile());
+        // 支付金额
+        shopOrderDTO.setPayAmount(erpOrderPayment.getPayment());
+        // 运费 改
+        if (ObjectUtil.isNull(liveOrder.getPayDelivery())) {
+            shopOrderDTO.setFreight(Double.valueOf("0.00"));
+        } else {
+            shopOrderDTO.setFreight(liveOrder.getPayDelivery().doubleValue());
+        }
+        // 备注
+        shopOrderDTO.setRemark(DateUtil.format(new Date(), "dd"));
+        // 买家留言
+        shopOrderDTO.setBuyerMessage(order.getBuyer_memo());
+
+        // 订单商品项列表
+        List<OrderItemDTO> itemDTOList = new ArrayList<>();
+
+        List<LiveOrderItem> liveOrderItems = liveOrderItemMapper.selectLiveOrderItemByOrderId(liveOrder.getOrderId());
+        log.info("liveOrderItems==========>{}",liveOrderItems);
+        for (LiveOrderItem item : liveOrderItems) {
+            OrderItemDTO orderItemDTO = new OrderItemDTO();
+            JSONObject jsonObject = JSON.parseObject(item.getJsonInfo());
+
+            String barCode = jsonObject.getString("barCode");
+            String productName = jsonObject.getString("productName");
+            String sku=jsonObject.getString("sku");
+
+            orderItemDTO.setSkuId(barCode);
+            orderItemDTO.setShopSkuId(barCode);
+            orderItemDTO.setName(productName);
+            orderItemDTO.setShopIId(orderItemDTO.getSkuId());//款式编码ID,当前没有这个目前就与SKU ID一致
+            orderItemDTO.setPropertiesValue(sku);//商品属性
+
+            FsStoreProductScrm fsStoreProduct = fsStoreProductScrmService.selectFsStoreProductById(item.getProductId());
+
+            orderItemDTO.setAmount(fsStoreProduct.getPrice().multiply(new BigDecimal(item.getNum())));
+            orderItemDTO.setPic(fsStoreProduct.getImage());
+            orderItemDTO.setPrice(fsStoreProduct.getPrice());
+            orderItemDTO.setQty(item.getNum().intValue());
+            orderItemDTO.setOuterOiId(String.format("%s%s",liveOrder.getOrderCode(),item.getItemId()));
+            itemDTOList.add(orderItemDTO);
+        }
+        shopOrderDTO.setItems(itemDTOList);
+
+        // 实际支付金额
+        PaymentDTO paymentDTO = new PaymentDTO();
+        paymentDTO.setAmount(liveOrder.getPayMoney().doubleValue());
+        paymentDTO.setOuterPayId(order.getPlatform_code());
+        paymentDTO.setPayDate(order.getDeal_datetime());
+        paymentDTO.setPayment("微信支付");
+        paymentDTO.setBuyerAccount(order.getBuyer_account());
+        paymentDTO.setSellerAccount("平台销售");
+        shopOrderDTO.setPay(paymentDTO);
+
+        // 如果是货到付款
+        if("2".equals(liveOrder.getPayType()) || "3".equals(liveOrder.getPayType())){
+            shopOrderDTO.setIsCod(true);
+            // 货到付款金额 = 物流代收金额-优惠金额
+            //应付金额(货到付款= 订单总金额 - 已付金额)
+            shopOrderDTO.setPayAmount(liveOrder.getTotalPrice().subtract(liveOrder.getPayPrice()).doubleValue());
+
+            // 货到付款要推两次
+            PaymentDTO paymentDTO2 = new PaymentDTO();
+            // 物流代收金额
+            paymentDTO2.setAmount(liveOrder.getTotalPrice().subtract(liveOrder.getPayPrice()).doubleValue());
+            paymentDTO2.setOuterPayId(String.format("%s%d",order.getPlatform_code(),1));
+            paymentDTO2.setPayDate(order.getDeal_datetime());
+            paymentDTO2.setPayment("货到付款");
+            paymentDTO2.setBuyerAccount(order.getBuyer_account());
+            paymentDTO2.setSellerAccount("平台销售");
+            shopOrderDTO.setPay(paymentDTO2);
+
+            FsJstCodPush fsJstCodPush = new FsJstCodPush();
+            fsJstCodPush.setOrderId(liveOrder.getOrderCode());
+            fsJstCodPush.setType("0");
+            fsJstCodPush.setParams(JSON.toJSONString(shopOrderDTO));
+            fsJstCodPush.setRetryCount(0);
+            fsJstCodPush.setTaskStatus(TaskStatusEnum.PENDING.getCode());
+            fsJstCodPushScrmMapper.insert(fsJstCodPush);
+        }
+
+        ErpOrderResponseDTO upload = jstErpHttpService.upload(shopOrderDTO);
+
+        if(CollectionUtils.isEmpty(upload.getDatas())) {
+            log.info("推送ERP返回结果: {}",upload);
+            throw new IllegalArgumentException("推送ERP返回数不应该为0");
+        }
+        ErpOrderResponseDTO.OrderData orderData = upload.getDatas().get(0);
+
+        ErpOrderResponse erpOrderResponse = new ErpOrderResponse();
+        erpOrderResponse.setSuccess(true);
+        erpOrderResponse.setCode(String.valueOf(orderData.getOId()));
+        return erpOrderResponse;
+    }
 
     @Override
     public ErpOrderResponse refundOrder(ErpRefundOrder order) {
@@ -339,6 +467,11 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public ErpOrderResponse refundLiveOrder(ErpRefundOrder order) {
+        return null;
+    }
+
     @Override
     public ErpDeliverysResponse getDeliver(ErpDeliverysRequest param) {
         return null;
@@ -402,6 +535,36 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
 
         return response;
     }
+
+    @Override
+    public ErpOrderQueryResponse getLiveOrder(ErpOrderQueryRequert param) {
+        // 1. 构建查询请求DTO
+        OrderQueryRequestDTO requestDTO = new OrderQueryRequestDTO();
+        requestDTO.setOIds(Collections.singletonList(Long.valueOf(param.getCode())));
+        requestDTO.setOrderItemFlds(Arrays.asList("status"));
+
+        // 2. 调用ERP服务查询订单
+        OrderQueryResponseDTO query = jstErpHttpService.query(requestDTO);
+
+        // 3. 构建响应对象
+        ErpOrderQueryResponse response = new ErpOrderQueryResponse();
+
+        // 4. 设置基本响应信息
+
+        // 5. 转换订单数据
+        if (query.getOrders() != null && !query.getOrders().isEmpty()) {
+            List<ErpOrderQuery> erpOrders = query.getOrders().stream()
+                    .map(this::convertToErpOrderQueryLive)
+                    .collect(Collectors.toList());
+
+            response.setOrders(erpOrders);
+        } else {
+            response.setOrders(Collections.emptyList());
+        }
+
+        return response;
+    }
+
     /**
      * 将OrderQueryResponseDTO.Order转换为ErpOrderQuery
      *
@@ -562,6 +725,83 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         return erpOrder;
     }
 
+    private ErpOrderQuery convertToErpOrderQueryLive(OrderQueryResponseDTO.Order order) {
+        ErpOrderQuery erpOrder = new ErpOrderQuery();
+
+        LiveOrder liveOrder = liveOrderMapper.selectLiveOrderByOrderCode(order.getSoId());
+        Asserts.notNull(liveOrder,"该订单号没有找到!");
+
+        // 设置基本订单信息
+        erpOrder.setCode(order.getSoId());
+
+        // 计算订单总数量
+        if (order.getItems() != null && !order.getItems().isEmpty()) {
+            int totalQty = order.getItems().stream()
+                    .mapToInt(OrderQueryResponseDTO.OrderItem::getQty)
+                    .sum();
+            erpOrder.setQty(totalQty);
+        }
+        if (StringUtils.isNotEmpty(order.getStatus()) && "Cancelled".equals(order.getStatus())) {
+            erpOrder.setCancle(true);
+        }
+
+        // 设置金额相关信息
+        erpOrder.setAmount(order.getAmount() != null ? order.getAmount().doubleValue() : null);
+        erpOrder.setPayment(order.getPaidAmount() != null ? order.getPaidAmount().doubleValue() : null);
+
+        // 设置其他订单属性
+        erpOrder.setCod(order.getIsCod());
+        erpOrder.setPlatform_code(order.getOrderFrom());
+
+        // 尝试解析创建时间
+        try {
+            if (order.getCreated() != null) {
+                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                erpOrder.setCreatetime(formatter.parse(order.getCreated()));
+            }
+        } catch (Exception e) {
+            // 日期解析异常处理
+            // 可以记录日志或保持为null
+        }
+
+        // 设置店铺信息
+        erpOrder.setShop_name(order.getShopName());
+        erpOrder.setShop_code(String.valueOf(order.getShopId()));
+
+        // 设置物流信息
+        erpOrder.setExpress_name(order.getLogisticsCompany());
+        erpOrder.setExpress_code(order.getLId());
+
+        // 设置收件人信息
+        erpOrder.setReceiver_name(liveOrder.getUserName());
+        erpOrder.setReceiver_phone(liveOrder.getUserPhone());
+        erpOrder.setReceiver_mobile(liveOrder.getUserPhone());
+
+        // 构建完整地址
+        erpOrder.setReceiver_address(liveOrder.getUserAddress());
+
+        // 如果是已发货
+        if(ObjectUtil.equal(order.getStatus(), ErpQueryOrderStatusEnum.SENT.getCode())){
+            List<ErpDeliverys> deliverysList = new ArrayList<>();
+
+            ErpDeliverys delivery = new ErpDeliverys();
+            delivery.setMail_no(order.getLId());
+            delivery.setExpress_code(order.getLcId());
+            delivery.setExpress_name(order.getLogisticsCompany());
+            delivery.setDelivery(true);
+            delivery.setCode(order.getSoId());
+
+            deliverysList.add(delivery);
+            erpOrder.setDeliverys(deliverysList);
+
+            // 设置发货状态,假设有物流单号就是已发货状态
+            erpOrder.setDelivery_state(1);
+        } else {
+            erpOrder.setDelivery_state(0);
+        }
+
+        return erpOrder;
+    }
 
     @Override
     public BaseResponse refundUpdate(ErpRefundUpdateRequest param) {
@@ -632,6 +872,39 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         return baseResponse;
     }
 
+    @Override
+    public BaseResponse refundUpdateLive(ErpRefundUpdateRequest param) {
+        LiveOrder liveOrder = liveOrderMapper.selectLiveOrderByOrderCode(param.getTid());
+        log.info("订单号: {},发货状态: {},是否发货后: {}",liveOrder.getOrderCode(),liveOrder.getStatus(),ObjectUtils.equals(liveOrder.getStatus(),2));
+
+        // 发货后退款
+        if(ObjectUtils.equals(param.getOrderStatus(),2)){
+
+            FsJstAftersalePush fsJstAftersalePush = new FsJstAftersalePush();
+            fsJstAftersalePush.setOrderId(liveOrder.getOrderCode());
+            fsJstAftersalePush.setTaskStatus(com.fs.hisStore.enums.TaskStatusEnum.PENDING.getCode());
+            fsJstAftersalePush.setType(String.valueOf(AfterSalesOrderStatusEnum.WAIT_SELLER_AGREE.getIndex()));
+            fsJstAftersalePush.setRetryCount(0);
+            fsJstAftersalePush.setAfterSaleId(String.valueOf(param.getStoreAfterSalesId()));
+            fsJstAftersalePushScrmMapper.insert(fsJstAftersalePush);
+
+        } else {
+            // 如果是发货前退款,直接走取消订单流程
+            // 如果是发货后退款,走售后流程
+            OrderCancelRequestDTO requestDTO = new OrderCancelRequestDTO();
+            requestDTO.setOIds(Collections.singletonList(Integer.valueOf(liveOrder.getExtendOrderId())));
+            requestDTO.setCancelType("用户退款");
+            requestDTO.setRemark("用户退款");
+
+            jstErpHttpService.cancel(requestDTO);
+        }
+
+        BaseResponse baseResponse = new BaseResponse();
+        baseResponse.setSuccess(true);
+
+        return baseResponse;
+    }
+
     @Override
     public ErpOrderResponse finishOrder(ErpOrder order) {
         return null;
@@ -646,5 +919,10 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
     public void getOrderScrmDeliveryStatus(FsStoreOrderScrm order) {
 
     }
+
+    @Override
+    public void getOrderLiveDeliveryStatus(LiveOrder order) {
+
+    }
 }
 

+ 176 - 0
fs-service/src/main/java/com/fs/erp/service/impl/K9OrderScrmServiceImpl.java

@@ -22,6 +22,10 @@ import com.fs.hisStore.mapper.FsStoreOrderScrmMapper;
 import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
 import com.fs.hisStore.service.IFsStoreOrderItemScrmService;
 import com.fs.hisStore.vo.FsStoreOrderItemVO;
+import com.fs.live.domain.LiveOrder;
+import com.fs.live.domain.LiveOrderItem;
+import com.fs.live.mapper.LiveOrderItemMapper;
+import com.fs.live.mapper.LiveOrderMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -51,6 +55,13 @@ public class K9OrderScrmServiceImpl implements IErpOrderService {
     private IFsStoreOrderItemScrmService storeOrderItemService;
     @Autowired
     private FsStoreProductScrmMapper productMapper;
+
+    @Autowired
+    private LiveOrderMapper liveOrderMapper;
+
+    @Autowired
+    private LiveOrderItemMapper liveOrderItemMapper;
+
     @Override
     /**
      * 推送erp订单
@@ -64,6 +75,151 @@ public class K9OrderScrmServiceImpl implements IErpOrderService {
         return addOmsOrder(order.getPlatform_code());
     }
 
+    @Override
+    public ErpOrderResponse addLiveOrder(ErpOrder order) {
+        return addOmsOrderLive(order.getPlatform_code());
+    }
+
+    private ErpOrderResponse addOmsOrderLive(String orderCode) {
+        try {
+            KingbosOrderResponse response = this.createOmsOrderLive(orderCode, NO_DELIVERED.getCode());
+            if (response.getIsSuccess()){
+                log.info("订单推送成功: {}", response);
+                ErpOrderResponse erpOrderResponse = new ErpOrderResponse();
+                erpOrderResponse.setCode(response.getCbilid());
+                return erpOrderResponse;
+            }else {
+                throw new RuntimeException(String.format("订单推送失败,原因: %s",response.getErrmsg()));
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new ErpOrderResponse();
+    }
+
+    private KingbosOrderResponse createOmsOrderLive(String orderCode, String orderStatus){
+        logger.info("【金博网络订单】开始创建订单,订单Code: {}", orderCode);
+        try {
+            LiveOrder order = liveOrderMapper.selectLiveOrderByOrderCode(orderCode);
+            logger.info("【金博网络订单】订单信息获取成功,订单id: {}", order.getOrderId());
+            logger.info("【金博网络订单】订单信息获取成功,订单号: {}", order.getOrderCode());
+
+            KingbosOrderData data = buildKingbosOrderDataLive(order, orderStatus);
+            logger.info("【金博网络订单】订单数据构建完成,金博订单号: {}", data.getCo_id());
+
+            List<KingbosOrderD1Data> d1Datas = buildOrderDetailDataLive(order, data.getCdo_id());
+            logger.info("【金博网络订单】订单明细数据构建完成,明细数量: {}", d1Datas.size());
+
+            KingbosOrderResponse response = sendOrderRequest(data, d1Datas);
+            if (response.getCbilid()==null){
+                response.setCbilid(order.getExtendOrderId());
+            }
+            return response;
+        } catch (Exception e) {
+            logger.error("【金博网络订单】创建订单失败,订单Code: {}, 错误信息: {}", orderCode, e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    private KingbosOrderData buildKingbosOrderDataLive(LiveOrder order, String orderStatus) {
+        FsErpConfig erpConfig = configUtil.getErpConfig();
+        logger.debug("【金博网络订单】开始构建订单数据,订单号: {}", order.getOrderCode());
+        KingbosOrderData data = new KingbosOrderData();
+        data.setCordersource("MALL");
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        data.setDorder_date(sdf.format(order.getCreateTime()));
+        data.setCplatformtype("99");
+        data.setCshop_id("zk2025");
+        data.setCshop_name("自建商城");
+        String orderSn = "";
+        if (orderStatus.equals(NO_DELIVERED.getCode())){
+            orderSn= IdUtil.getSnowflake(0, 0).nextIdStr();
+        }else if (orderStatus.equals(CANCELLED.getCode())){
+            orderSn = order.getExtendOrderId();
+        }
+
+        data.setCso_id(order.getOrderCode());
+        data.setCo_id(orderSn);
+        data.setCdo_id(orderSn);
+        order.setExtendOrderId(orderSn);
+
+        data.setFpay_amount(order.getPayPrice());
+        data.setFpaid_amount(order.getPayMoney());
+        data.setFfreight(order.getTotalPostage());
+        data.setFfree_amount(order.getCouponPrice());
+        data.setFpostcost(BigDecimal.ZERO);
+
+        data.setCstatus(orderStatus);
+        data.setDmodified(sdf.format(order.getUpdateTime()));
+
+        setReceiverInfoLive(data, order);
+
+        data.setCwarehouse_code(erpConfig.getCwarehouseCode());
+        data.setCwarehouse_name(erpConfig.getCwarehouseName());
+
+        if (!order.getPayType().equals("1")) {
+            data.setBis_cod(1);
+            data.setFshouldpaymoney(order.getDeliveryPayMoney());
+        }
+
+        return data;
+    }
+
+    private void setReceiverInfoLive(KingbosOrderData data, LiveOrder order) {
+        logger.debug("【金博网络订单】开始处理收货信息,订单号: {}", order.getOrderCode());
+        String[] address = order.getUserAddress().split(" ");
+        if (address.length < 3) {
+            logger.error("【金博网络订单】收货地址格式不正确,订单号: {}, 地址: {}", order.getOrderCode(), order.getUserAddress());
+            throw new ServiceException("收货地址格式不正确");
+        }
+
+        data.setCreceiver_state(address[0]);
+        data.setCreceiver_city(address[1]);
+        data.setCreceiver_district(address[2]);
+
+        String detailAddress = address.length > 3 ?
+            String.join("", Arrays.copyOfRange(address, 3, address.length)) :
+            address[2];
+
+        detailAddress = detailAddress.replace("+", "加")
+                                   .replace("\n", "");
+
+        data.setCreceiver_address(detailAddress);
+        data.setCreceiver_name(order.getRealName());
+        data.setCreceiver_mobile(order.getUserPhone());
+    }
+
+    private List<KingbosOrderD1Data> buildOrderDetailDataLive(LiveOrder order, String orderId) {
+        logger.debug("【金博网络订单】开始构建订单明细,订单号: {}", order.getOrderCode());
+        List<LiveOrderItem> orderItems = liveOrderItemMapper.selectLiveOrderItemByOrderId(order.getOrderId());
+        List<KingbosOrderD1Data> d1Datas = orderItems.stream()
+                .map(item -> buildOrderDetailItemLive(item, orderId))
+                .collect(Collectors.toList());
+
+        logger.debug("【金博网络订单】订单明细构建完成,订单号: {}, 明细数量: {}", order.getOrderCode(), d1Datas.size());
+        return d1Datas;
+    }
+
+    private KingbosOrderD1Data buildOrderDetailItemLive(LiveOrderItem orderItem, String orderId) {
+        FsStoreCartDTO cartDTO = JSONUtil.toBean(orderItem.getJsonInfo(), FsStoreCartDTO.class);
+        BigDecimal quantity = new BigDecimal(orderItem.getNum());
+
+        KingbosOrderD1Data item = new KingbosOrderD1Data();
+        item.setCdo_id(orderId);
+        item.setCo_id(orderId);
+        item.setCsku_id(cartDTO.getBarCode());
+        item.setCgoodsid(cartDTO.getBarCode());
+        item.setCname(cartDTO.getProductName());
+        item.setCshop_sku_id(cartDTO.getSku());
+        item.setIqty(quantity);
+        item.setFprice(cartDTO.getPrice());
+        item.setFamount(quantity.multiply(cartDTO.getPrice()));
+        item.setCordersource("MALL");
+        item.setFpromoamount(BigDecimal.ZERO);
+
+        return item;
+    }
+
 
     /**
      * 退款
@@ -93,6 +249,11 @@ public class K9OrderScrmServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public ErpOrderResponse refundLiveOrder(ErpRefundOrder order) {
+        return null;
+    }
+
     private KingbosRefundOrderRequest getKingbosRefundOrderRequest(String orderCode) {
         FsStoreOrderScrm order = fsStoreOrderMapper.selectFsStoreOrderByOrderCode(orderCode);
         if (order != null){
@@ -138,6 +299,11 @@ public class K9OrderScrmServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public ErpOrderQueryResponse getLiveOrder(ErpOrderQueryRequert param) {
+        return null;
+    }
+
     @Override
     public BaseResponse refundUpdate(ErpRefundUpdateRequest param) {
         return null;
@@ -148,6 +314,11 @@ public class K9OrderScrmServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public BaseResponse refundUpdateLive(ErpRefundUpdateRequest param) {
+        return null;
+    }
+
 
     /**
      * 退款 运单号已获取到
@@ -460,4 +631,9 @@ public class K9OrderScrmServiceImpl implements IErpOrderService {
     public void getOrderScrmDeliveryStatus(FsStoreOrderScrm order) {
 
     }
+
+    @Override
+    public void getOrderLiveDeliveryStatus(LiveOrder order) {
+
+    }
 }

+ 206 - 0
fs-service/src/main/java/com/fs/erp/service/impl/WdtErpOrderServiceImpl.java

@@ -32,6 +32,10 @@ import com.fs.hisStore.domain.FsStoreProductScrm;
 import com.fs.hisStore.service.IFsStoreOrderItemScrmService;
 import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.service.IFsStoreProductScrmService;
+import com.fs.live.domain.LiveOrder;
+import com.fs.live.domain.LiveOrderItem;
+import com.fs.live.mapper.LiveOrderItemMapper;
+import com.fs.live.mapper.LiveOrderMapper;
 import com.hc.openapi.tool.util.StringUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
@@ -70,6 +74,12 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
     @Autowired
     private IFsStoreProductScrmService fsStoreProductScrmService;
 
+    @Autowired
+    private LiveOrderMapper liveOrderMapper;
+
+    @Autowired
+    private LiveOrderItemMapper liveOrderItemMapper;
+
 //    @Value("${fsConfig.erpWdShopCode}")
 //    private String shopCode;
 
@@ -103,6 +113,182 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
         return getErpOrderResponseScrm(order);
     }
 
+    @Override
+    public ErpOrderResponse addLiveOrder(ErpOrder order) {
+        return getErpOrderResponseLive(order);
+    }
+
+    private ErpOrderResponse getErpOrderResponseLive(ErpOrder order) {
+        FsSysConfig sysConfig = configUtil.getSysConfig();
+        String shopCode = sysConfig.getErpWdShopCode();
+
+        ErpWdtBusinessRequestParams erpWdtBusinessRequestParams = new ErpWdtBusinessRequestParams();
+        erpWdtBusinessRequestParams.setShopNo(shopCode);
+        erpWdtBusinessRequestParams.setSwitchMode(0);
+        ErpWdtTrade erpWdtTrade = new ErpWdtTrade();
+        LiveOrder liveOrder = liveOrderMapper.selectLiveOrderByOrderCode(order.getPlatform_code());
+        BigDecimal couponPrice = liveOrder.getCouponPrice();
+        if (couponPrice == null) {
+            couponPrice = BigDecimal.ZERO;
+        }
+
+        // 平台状态
+        erpWdtTrade.setTradeStatus(TradeStatus.PAID_WAITING_FOR_SHIPMENT.getValue());
+
+        // 订单id
+        erpWdtTrade.setTid(order.getPlatform_code());
+
+        // 如果是货到付款
+        if("2".equals(liveOrder.getPayType())){
+            // 支付状态
+            erpWdtTrade.setPayStatus(PaymentStatus.PARTIALLY_PAID.getValue());
+            // 发货条件
+            erpWdtTrade.setDeliveryTerm(DeliveryTerm.CASH_ON_DELIVERY.getValue());
+            // 货到付款金额 = 物流代收金额-优惠金额
+            erpWdtTrade.setCodAmount(liveOrder.getPayDelivery().subtract(couponPrice));
+
+        } else if("3".equals(liveOrder.getPayType())){
+            // 支付状态
+            erpWdtTrade.setPayStatus(PaymentStatus.UNPAID.getValue());
+            // 发货条件
+            erpWdtTrade.setDeliveryTerm(DeliveryTerm.CASH_ON_DELIVERY.getValue());
+            // 货到付款金额 = 物流代收金额-优惠金额
+            erpWdtTrade.setCodAmount(liveOrder.getPayDelivery().subtract(couponPrice));
+        } else { // 如果是线上付款
+            // 支付状态
+            erpWdtTrade.setPayStatus(PaymentStatus.FULLY_PAID.getValue());
+            // 发货条件
+            erpWdtTrade.setDeliveryTerm(DeliveryTerm.PAYMENT_BEFORE_DELIVERY.getValue());
+        }
+
+        // 下单时间
+        erpWdtTrade.setTradeTime(order.getDeal_datetime());
+        // 支付时间
+        erpWdtTrade.setPayTime(order.getDeal_datetime());
+        // 分销类别
+        erpWdtTrade.setFenxiaoType(FenxiaoType.JINGXIAO.getValue());
+        // 客户网名
+        erpWdtTrade.setBuyerNick(order.getReceiver_name());
+        // 收件人姓名
+        erpWdtTrade.setReceiverName(order.getReceiver_name());
+        // 省份
+        erpWdtTrade.setReceiverProvince(order.getReceiver_province());
+        // 市
+        erpWdtTrade.setReceiverCity(order.getReceiver_city());
+        // 区
+        erpWdtTrade.setReceiverDistrict(order.getReceiver_district());
+        // 详细地址
+        erpWdtTrade.setReceiverAddress(order.getReceiver_address());
+        // 手机
+        erpWdtTrade.setReceiverMobile(order.getReceiver_mobile());
+        // 固定电话
+        erpWdtTrade.setReceiverTelno(order.getReceiver_mobile());
+        // 买家备注
+        erpWdtTrade.setBuyerMessage(order.getBuyer_memo());
+        // 卖家备注
+        erpWdtTrade.setSellerMemo(order.getSeller_memo());
+        erpWdtTrade.setWarehouseNo(order.getWarehouse_code());
+
+        // 运费金额
+        erpWdtTrade.setPostAmount(liveOrder.getPayDelivery());
+        // 已付金额
+        if(ObjectUtils.isNotNull(liveOrder)){
+            erpWdtTrade.setPaid(liveOrder.getPayMoney());
+        }
+
+        List<LiveOrderItem> liveOrderItems = liveOrderItemMapper.selectLiveOrderItemByOrderId(liveOrder.getOrderId());
+        List<ErpWdtOrder> erpWdtOrderList = new ArrayList<>();
+        // 商品总价
+        BigDecimal totalPrice = liveOrder.getTotalPrice();
+
+        // 总折扣金额 = 优惠券金额 + 折扣金额
+        BigDecimal totalDiscountPrice = BigDecimal.ZERO;
+        // 折扣金额
+        BigDecimal discountPrice = totalPrice.subtract(liveOrder.getPayPrice());
+        totalDiscountPrice = couponPrice.add(discountPrice);
+
+        // 防止除零错误
+        if (totalPrice.compareTo(BigDecimal.ZERO) == 0) {
+            totalPrice = BigDecimal.ONE;
+        }
+
+        for (LiveOrderItem liveOrderItem : liveOrderItems) {
+            ErpWdtOrder erpWdtOrder = new ErpWdtOrder();
+            //平台订单货品表主键
+            erpWdtOrder.setOid(String.format("%s%s",liveOrder.getOrderCode(),liveOrderItem.getItemId()));
+            erpWdtOrder.setNum(BigDecimal.valueOf(liveOrderItem.getNum()));
+            FsStoreProductScrm fsStoreProduct = fsStoreProductScrmService.selectFsStoreProductById(liveOrderItem.getProductId());
+            Asserts.check(ObjectUtils.isNotNull(fsStoreProduct),"该产品不存在! 产品id: {} ",liveOrderItem.getProductId());
+            // 单价
+            erpWdtOrder.setPrice(fsStoreProduct.getPrice());
+            // 状态
+            erpWdtOrder.setStatus(TradeStatus.PAID_WAITING_FOR_SHIPMENT.getValue());
+            // 退款状态
+            erpWdtOrder.setRefundStatus(RefundStatus.NO_REFUND.getValue());
+
+            // 平台货品ID
+            erpWdtOrder.setGoodsId(String.valueOf(fsStoreProduct.getProductId()));
+            JSONObject jsonObject = JSON.parseObject(liveOrderItem.getJsonInfo());
+            erpWdtOrder.setSpecId(jsonObject.getString("sku"));
+            erpWdtOrder.setGoodsNo(jsonObject.getString("barCode"));
+            erpWdtOrder.setSpecNo(jsonObject.getString("sku"));
+            // 货品名称
+            erpWdtOrder.setGoodsName(fsStoreProduct.getProductName());
+            // 调整
+            erpWdtOrder.setAdjustAmount(BigDecimal.ZERO);
+            // 优惠
+            erpWdtOrder.setDiscount(BigDecimal.ZERO);
+            // 分摊优惠
+            // 分摊比例
+            BigDecimal price = fsStoreProduct.getPrice().multiply(BigDecimal.valueOf(liveOrderItem.getNum()));
+            BigDecimal divide = price.divide(totalPrice, RoundingMode.HALF_UP);
+            erpWdtOrder.setShareDiscount(divide.multiply(totalDiscountPrice));
+
+            erpWdtOrderList.add(erpWdtOrder);
+        }
+
+        // 最后一个商品的分摊优惠等于总优惠减去前面分摊优惠之和
+        Asserts.check(CollectionUtils.isNotEmpty(erpWdtOrderList),"订单 {} 商品不能为空!", order.getPlatform_code());
+        long size = erpWdtOrderList.size();
+        if(size > 1) {
+            ErpWdtOrder erpWdtOrder = erpWdtOrderList.get(erpWdtOrderList.size() - 1);
+            erpWdtOrder.setShareDiscount(totalDiscountPrice.subtract(erpWdtOrderList.stream()
+                    .limit(size - 1L)
+                    .map(item -> Optional.ofNullable(item.getShareDiscount()).orElse(BigDecimal.ZERO))
+                    .reduce(BigDecimal.ZERO, BigDecimal::add)));
+        }
+
+        erpWdtTrade.setOrderList(erpWdtOrderList);
+        erpWdtBusinessRequestParams.setTradeList(new ArrayList<>(Arrays.asList(erpWdtTrade)));
+
+        Map<String,String> map = new HashMap<>();
+        map.put("shop_no",erpWdtBusinessRequestParams.getShopNo());
+        map.put("switch_mode", String.valueOf(erpWdtBusinessRequestParams.getSwitchMode()));
+        map.put("trade_list", convertToSnakeCase(erpWdtBusinessRequestParams.getTradeList()));
+
+
+        try {
+            String response = client.execute("trade_push.php", map);
+            ParserConfig config = new ParserConfig();
+            config.propertyNamingStrategy = PropertyNamingStrategy.CamelCase;
+            ErpWdtApiResponse erpWdtApiResponse = JSON.parseObject(response, ErpWdtApiResponse.class,config);
+            if(ObjectUtil.equal(0,erpWdtApiResponse.getCode())){
+                log.info("订单推送成功: {}", response);
+                ErpOrderResponse erpOrderResponse = new ErpOrderResponse();
+                erpOrderResponse.setCode(order.getPlatform_code());
+                erpOrderResponse.setSuccess(true);
+                erpOrderResponse.setRequestRawData(JSON.toJSONString(map));
+                erpOrderResponse.setResponseRawData(response);
+                return erpOrderResponse;
+            } else {
+                throw new RuntimeException(String.format("订单推送失败,原因: %s",erpWdtApiResponse.getMessage()));
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return new ErpOrderResponse();
+    }
+
     /**
      * 获取erp推送参数
      * @param order 订单参数
@@ -800,6 +986,11 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public ErpOrderResponse refundLiveOrder(ErpRefundOrder order) {
+        return null;
+    }
+
     @Override
     public ErpDeliverysResponse getDeliver(ErpDeliverysRequest param) {
         return null;
@@ -837,6 +1028,11 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public ErpOrderQueryResponse getLiveOrder(ErpOrderQueryRequert param) {
+        return null;
+    }
+
     @Override
     public BaseResponse refundUpdate(ErpRefundUpdateRequest param) {
         FsSysConfig sysConfig = configUtil.getSysConfig();
@@ -921,6 +1117,11 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
         return null;
     }
 
+    @Override
+    public BaseResponse refundUpdateLive(ErpRefundUpdateRequest param) {
+        return null;
+    }
+
     @Override
     public ErpOrderResponse finishOrder(ErpOrder order) {
         FsSysConfig sysConfig = configUtil.getSysConfig();
@@ -1103,6 +1304,11 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
 
     }
 
+    @Override
+    public void getOrderLiveDeliveryStatus(LiveOrder order) {
+
+    }
+
     public static String convertToSnakeCase(Object obj) {
         SerializeConfig config = new SerializeConfig();
         config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;

+ 17 - 3
fs-service/src/main/java/com/fs/his/domain/FsIntegralOrder.java

@@ -1,5 +1,8 @@
 package com.fs.his.domain;
 
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
@@ -16,11 +19,12 @@ import java.util.Date;
  * @date 2023-11-02
  */
 @Data
-public class FsIntegralOrder extends BaseEntity
+public class FsIntegralOrder
 {
     private static final long serialVersionUID = 1L;
 
     /** id */
+    @TableId
     private Long orderId;
 
     /** 订单编号 */
@@ -47,9 +51,10 @@ public class FsIntegralOrder extends BaseEntity
     @Excel(name = "商品信息")
     private String itemJson;
 
+    @TableField(exist = false)
     @Excel(name = "商品名称")
     private String goodsName;
-
+    @TableField(exist = false)
     @Excel(name = "原价")
     private BigDecimal otPrice;
 
@@ -73,7 +78,7 @@ public class FsIntegralOrder extends BaseEntity
     @Excel(name = "支付类型 1积分 2现金 3积分+现金")
     private Integer payType;
 
-    /** 1:待发货;2:待收货;3:已完成 4待支付 5取消 */
+    /** 1:待发货;2:待收货;3:已完成 4待支付 -1取消 */
     @Excel(name = "状态",dictType="sys_integral_order_status")
     private Integer status;
     @Excel(name = "商品编号")
@@ -116,4 +121,13 @@ public class FsIntegralOrder extends BaseEntity
 
     private String loginAccount;
 
+    private String remark;
+
+    private Date createTime;
+    @TableField(exist = false)
+    private Date updateTime;
+
+    private Integer deliveryStatus;
+
+    private String deliveryType;
 }

+ 73 - 0
fs-service/src/main/java/com/fs/his/domain/FsIntegralOrderDf.java

@@ -0,0 +1,73 @@
+package com.fs.his.domain;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+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_integral_order_df
+ *
+ * @author fs
+ * @date 2025-11-11
+ */
+@Data
+public class FsIntegralOrderDf{
+
+    @TableId
+    /** 订单id */
+    private Long orderId;
+
+    /** 订单编号 */
+    @Excel(name = "订单编号")
+    private String orderCode;
+
+    /** $column.columnComment */
+    @Excel(name = "订单编号")
+    private String appKey;
+
+    /** $column.columnComment */
+    @Excel(name = "订单编号")
+    private String appSecret;
+
+    /** $column.columnComment */
+    @Excel(name = "订单编号")
+    private String loginAccount;
+
+    /** 顺丰月结账户 */
+    @Excel(name = "顺丰月结账户")
+    private String monthlyCard;
+
+    /** 物流产品编码:1-顺丰标快,2-顺丰标快(陆运),204-陆运微小件,231-陆运包裹,208-特惠专配 */
+    @Excel(name = "物流产品编码:1-顺丰标快,2-顺丰标快", readConverterExp = "陆=运")
+    private String expressProductCode;
+
+    /** 订单总价 */
+    @Excel(name = "订单总价")
+    private BigDecimal totalPrice;
+
+    /** 平台收款 */
+    @Excel(name = "平台收款")
+    private BigDecimal platformPrice;
+
+    /** 0:默认 1下单 2取消订单 */
+    @Excel(name = "0:默认 1下单 2取消订单")
+    private Integer status;
+
+    /** 失败原因 */
+    @Excel(name = "失败原因")
+    private String failMsg;
+
+    /** 包裹数量 */
+    @Excel(name = "包裹数量")
+    private Integer parcelQuantity;
+
+    private LocalDateTime updateTime;
+    private LocalDateTime createTime;
+    private String erpPhone;
+    private Integer isPush;
+}

+ 47 - 0
fs-service/src/main/java/com/fs/his/domain/FsIntegralOrderLogs.java

@@ -0,0 +1,47 @@
+package com.fs.his.domain;
+
+import com.fs.common.annotation.Excel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+/**
+ * 订单操作记录对象 fs_integral_order_logs
+ *
+ * @author fs
+ * @date 2025-11-25
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class FsIntegralOrderLogs{
+
+    /** $column.columnComment */
+    private String logsId;
+
+    /** 订单id */
+    @Excel(name = "订单id")
+    private Long orderId;
+
+    /** 操作类型 */
+    @Excel(name = "操作类型")
+    private String changeType;
+
+    /** 操作备注 */
+    @Excel(name = "操作备注")
+    private String changeMessage;
+
+    /** 操作时间 */
+    private LocalDateTime changeTime;
+
+    /** 操作员 */
+    @Excel(name = "操作员")
+    private String operator;
+
+
+}

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

@@ -96,6 +96,15 @@ public class FsUser extends BaseEntity
     private Long companyId;
     private Long companyUserId;
     private String companyUserName;
+
+    /** 公司用户ID,逗号拼接*/
+    @TableField(exist = false)
+    private String companyUserIdMulti;
+
+    /** 公司用户ID,用于查询*/
+    @TableField(exist = false)
+    private String[] companyUserIds;
+
     @JsonFormat(pattern = "yyyy-MM-dd")
     @Excel(name = "推线日期", width = 30, dateFormat = "yyyy-MM-dd")
     private Date registerDate;
@@ -155,11 +164,25 @@ public class FsUser extends BaseEntity
 
     /** 下单次数 */
     private Long orderCount;
+
+    /** 累计成交总额 */
+    @Excel(name = "累计成交总额")
+    private BigDecimal totalAmount;
+
     /**
      * 企微销售ID
      * **/
     private Long qwUserId;
 
+    /**
+     * 小程序appId,多个用逗号分隔
+     */
+    private String appId;
+
+
+    /** 推广上级用户ID */
+    private Long spreadUserId;
+
     public void setNickName(String nickname)
     {
         if(StringUtils.isNotEmpty(nickname)){
@@ -200,6 +223,11 @@ public class FsUser extends BaseEntity
     @TableField(exist = false)
     private String nickname;
 
+    /**
+     * 昵称-精确查询
+     * **/
+    @TableField(exist = false)
+    private String nicknameExact;
 
     public String getNickname() {
         return nickname;

+ 3 - 1
fs-service/src/main/java/com/fs/his/dto/PayloadDTO.java

@@ -1,5 +1,6 @@
 package com.fs.his.dto;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
 import lombok.Data;
 
 import java.io.Serializable;
@@ -12,6 +13,7 @@ public class PayloadDTO implements Serializable {
     private String description;
 
     @Data
+    @JsonInclude(JsonInclude.Include.NON_NULL)
     public static class Extension implements Serializable{
         private String title;
         private String patientName;
@@ -33,7 +35,7 @@ public class PayloadDTO implements Serializable {
         private Long companyId;
         private Long companyUserId;
         private Long doctorId;
-
+        private Long userInformationId;
     }
 
 }

+ 64 - 0
fs-service/src/main/java/com/fs/his/mapper/FsIntegralOrderDfMapper.java

@@ -0,0 +1,64 @@
+package com.fs.his.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsIntegralOrderDf;
+
+import java.util.List;
+
+/**
+ * 【请填写功能名称】Mapper接口
+ *
+ * @author fs
+ * @date 2025-11-11
+ */
+public interface FsIntegralOrderDfMapper extends BaseMapper<FsIntegralOrderDf> {
+    /**
+     * 查询【请填写功能名称】
+     *
+     * @param orderId 【请填写功能名称】主键
+     * @return 【请填写功能名称】
+     */
+    FsIntegralOrderDf selectFsIntegralOrderDfByOrderId(Long orderId);
+
+    /**
+     * 查询【请填写功能名称】列表
+     *
+     * @param fsIntegralOrderDf 【请填写功能名称】
+     * @return 【请填写功能名称】集合
+     */
+    List<FsIntegralOrderDf> selectFsIntegralOrderDfList(FsIntegralOrderDf fsIntegralOrderDf);
+
+    /**
+     * 新增【请填写功能名称】
+     *
+     * @param fsIntegralOrderDf 【请填写功能名称】
+     * @return 结果
+     */
+    int insertFsIntegralOrderDf(FsIntegralOrderDf fsIntegralOrderDf);
+
+    /**
+     * 修改【请填写功能名称】
+     *
+     * @param fsIntegralOrderDf 【请填写功能名称】
+     * @return 结果
+     */
+    int updateFsIntegralOrderDf(FsIntegralOrderDf fsIntegralOrderDf);
+
+    /**
+     * 删除【请填写功能名称】
+     *
+     * @param orderId 【请填写功能名称】主键
+     * @return 结果
+     */
+    int deleteFsIntegralOrderDfByOrderId(Long orderId);
+
+    /**
+     * 批量删除【请填写功能名称】
+     *
+     * @param orderIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsIntegralOrderDfByOrderIds(Long[] orderIds);
+
+    List<FsIntegralOrderDf> selectByIsPush();
+}

+ 62 - 0
fs-service/src/main/java/com/fs/his/mapper/FsIntegralOrderLogsMapper.java

@@ -0,0 +1,62 @@
+package com.fs.his.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsIntegralOrderLogs;
+
+import java.util.List;
+
+/**
+ * 订单操作记录Mapper接口
+ *
+ * @author fs
+ * @date 2025-11-25
+ */
+public interface FsIntegralOrderLogsMapper extends BaseMapper<FsIntegralOrderLogs> {
+    /**
+     * 查询订单操作记录
+     *
+     * @param logsId 订单操作记录主键
+     * @return 订单操作记录
+     */
+    FsIntegralOrderLogs selectFsIntegralOrderLogsByLogsId(String logsId);
+
+    /**
+     * 查询订单操作记录列表
+     *
+     * @param fsIntegralOrderLogs 订单操作记录
+     * @return 订单操作记录集合
+     */
+    List<FsIntegralOrderLogs> selectFsIntegralOrderLogsList(FsIntegralOrderLogs fsIntegralOrderLogs);
+
+    /**
+     * 新增订单操作记录
+     *
+     * @param fsIntegralOrderLogs 订单操作记录
+     * @return 结果
+     */
+    int insertFsIntegralOrderLogs(FsIntegralOrderLogs fsIntegralOrderLogs);
+
+    /**
+     * 修改订单操作记录
+     *
+     * @param fsIntegralOrderLogs 订单操作记录
+     * @return 结果
+     */
+    int updateFsIntegralOrderLogs(FsIntegralOrderLogs fsIntegralOrderLogs);
+
+    /**
+     * 删除订单操作记录
+     *
+     * @param logsId 订单操作记录主键
+     * @return 结果
+     */
+    int deleteFsIntegralOrderLogsByLogsId(String logsId);
+
+    /**
+     * 批量删除订单操作记录
+     *
+     * @param logsIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsIntegralOrderLogsByLogsIds(String[] logsIds);
+}

+ 7 - 1
fs-service/src/main/java/com/fs/his/mapper/FsIntegralOrderMapper.java

@@ -1,6 +1,8 @@
 package com.fs.his.mapper;
 
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.his.domain.FsIntegralOrder;
+import com.fs.his.domain.FsIntegralOrderDf;
 import com.fs.his.param.FsIntegralOrderListUParam;
 import com.fs.his.param.FsIntegralOrderParam;
 import com.fs.his.vo.FsIntegralOrderListUVO;
@@ -17,7 +19,7 @@ import java.util.List;
  * @author fs
  * @date 2023-11-02
  */
-public interface FsIntegralOrderMapper
+public interface FsIntegralOrderMapper extends BaseMapper<FsIntegralOrder>
 {
     /**
      * 查询积分商品订单
@@ -110,4 +112,8 @@ public interface FsIntegralOrderMapper
     int cancelOrder(@Param("orderId") Long orderId);
 
     int finishOrder(@Param("orderId") Long orderId,@Param("oldStatus") Integer status);
+
+    List<FsIntegralOrder> findOrderByIds(@Param("orderIds") List<Long> orderIds);
+
+    List<FsIntegralOrderListVO> selectFsIntegralOrderListJn(FsIntegralOrderParam fsIntegralOrder);
 }

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

@@ -1,5 +1,6 @@
 package com.fs.his.mapper;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
@@ -20,6 +21,7 @@ import com.fs.hisStore.vo.FsCompanyUserListQueryVO;
 import com.fs.qw.dto.FsUserTransferParamDTO;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
+import com.fs.store.param.h5.FsUserPageListExportParam;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.param.h5.UserStatisticsCommonParam;
 import com.fs.store.vo.UserProjectPair;
@@ -36,6 +38,8 @@ import org.apache.ibatis.annotations.Param;
  */
 public interface FsUserMapper
 {
+    @Select("select * from fs_user where phone=#{phone}")
+    List<FsUser> selectFsUsersByPhoneLimitOne(String phone);
     /**
      * 查询用户
      *
@@ -467,4 +471,24 @@ public interface FsUserMapper
      * 查询会员列表
      */
     List<FsUser> findUsersByIdsV2(@Param("userIds") List<Long> userIds);
+
+    /**
+     * 更新用户下单次数和累计成交总额
+     * @param userId 用户ID
+     * @param amount 成交金额
+     */
+    @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);
+
+    List<FsUser> selectFsUserListByPhone(String phone);
+
+    /**
+     * 项目会员导出数据条数查询
+     */
+    Long selectProjectUserExportCount(FsUserPageListExportParam param);
+
+    /**
+     * 项目会员导出数据
+     */
+    List<FsUserPageListExportVO> FsUserPageListExportVO(FsUserPageListExportParam param);
 }

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

@@ -28,5 +28,7 @@ public class WxSendRedPacketParam implements Serializable {
 
     private String mpAppId;
 
+    private String outBatchNo; // 批次号
+
 
 }

+ 62 - 0
fs-service/src/main/java/com/fs/his/service/IFsIntegralOrderDfService.java

@@ -0,0 +1,62 @@
+package com.fs.his.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.his.domain.FsIntegralOrderDf;
+
+import java.util.List;
+
+/**
+ * 【请填写功能名称】Service接口
+ *
+ * @author fs
+ * @date 2025-11-11
+ */
+public interface IFsIntegralOrderDfService extends IService<FsIntegralOrderDf> {
+    /**
+     * 查询【请填写功能名称】
+     *
+     * @param orderId 【请填写功能名称】主键
+     * @return 【请填写功能名称】
+     */
+    FsIntegralOrderDf selectFsIntegralOrderDfByOrderId(Long orderId);
+
+    /**
+     * 查询【请填写功能名称】列表
+     *
+     * @param fsIntegralOrderDf 【请填写功能名称】
+     * @return 【请填写功能名称】集合
+     */
+    List<FsIntegralOrderDf> selectFsIntegralOrderDfList(FsIntegralOrderDf fsIntegralOrderDf);
+
+    /**
+     * 新增【请填写功能名称】
+     *
+     * @param fsIntegralOrderDf 【请填写功能名称】
+     * @return 结果
+     */
+    int insertFsIntegralOrderDf(FsIntegralOrderDf fsIntegralOrderDf);
+
+    /**
+     * 修改【请填写功能名称】
+     *
+     * @param fsIntegralOrderDf 【请填写功能名称】
+     * @return 结果
+     */
+    int updateFsIntegralOrderDf(FsIntegralOrderDf fsIntegralOrderDf);
+
+    /**
+     * 批量删除【请填写功能名称】
+     *
+     * @param orderIds 需要删除的【请填写功能名称】主键集合
+     * @return 结果
+     */
+    int deleteFsIntegralOrderDfByOrderIds(Long[] orderIds);
+
+    /**
+     * 删除【请填写功能名称】信息
+     *
+     * @param orderId 【请填写功能名称】主键
+     * @return 结果
+     */
+    int deleteFsIntegralOrderDfByOrderId(Long orderId);
+}

+ 62 - 0
fs-service/src/main/java/com/fs/his/service/IFsIntegralOrderLogsService.java

@@ -0,0 +1,62 @@
+package com.fs.his.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.his.domain.FsIntegralOrderLogs;
+
+import java.util.List;
+
+/**
+ * 订单操作记录Service接口
+ *
+ * @author fs
+ * @date 2025-11-25
+ */
+public interface IFsIntegralOrderLogsService extends IService<FsIntegralOrderLogs> {
+    /**
+     * 查询订单操作记录
+     *
+     * @param logsId 订单操作记录主键
+     * @return 订单操作记录
+     */
+    FsIntegralOrderLogs selectFsIntegralOrderLogsByLogsId(String logsId);
+
+    /**
+     * 查询订单操作记录列表
+     *
+     * @param fsIntegralOrderLogs 订单操作记录
+     * @return 订单操作记录集合
+     */
+    List<FsIntegralOrderLogs> selectFsIntegralOrderLogsList(FsIntegralOrderLogs fsIntegralOrderLogs);
+
+    /**
+     * 新增订单操作记录
+     *
+     * @param fsIntegralOrderLogs 订单操作记录
+     * @return 结果
+     */
+    int insertFsIntegralOrderLogs(FsIntegralOrderLogs fsIntegralOrderLogs);
+
+    /**
+     * 修改订单操作记录
+     *
+     * @param fsIntegralOrderLogs 订单操作记录
+     * @return 结果
+     */
+    int updateFsIntegralOrderLogs(FsIntegralOrderLogs fsIntegralOrderLogs);
+
+    /**
+     * 批量删除订单操作记录
+     *
+     * @param logsIds 需要删除的订单操作记录主键集合
+     * @return 结果
+     */
+    int deleteFsIntegralOrderLogsByLogsIds(String[] logsIds);
+
+    /**
+     * 删除订单操作记录信息
+     *
+     * @param logsId 订单操作记录主键
+     * @return 结果
+     */
+    int deleteFsIntegralOrderLogsByLogsId(String logsId);
+}

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

@@ -19,11 +19,13 @@ import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserParam;
 import com.fs.his.vo.*;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
+import com.fs.hisStore.domain.FsUserScrm;
 import com.fs.hisStore.vo.FsCompanyUserListQueryVO;
 import com.fs.live.vo.HisFsUserVO;
 import com.fs.qw.dto.FsUserTransferParamDTO;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
+import com.fs.store.param.h5.FsUserPageListExportParam;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.param.h5.UserStatisticsCommonParam;
 import com.fs.store.vo.h5.*;
@@ -246,6 +248,18 @@ public interface IFsUserService
 
     HisFsUserVO getHisUserIntegralWithLogs(FsUser fsUser);
 
+    List<FsUser> selectFsUserListByPhone(String phone);
+
+    /**
+     * 项目会员导出数据条数查询
+     */
+    Long selectProjectUserExportCount(FsUserPageListExportParam param);
+
+    /**
+     * 项目会员导出
+     */
+    void exportProjectUserData(FsUserPageListExportParam param);
+
     /**
      * 销售分享app下载链接给用户
      */

+ 93 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderDfServiceImpl.java

@@ -0,0 +1,93 @@
+package com.fs.his.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.DateUtils;
+import com.fs.his.domain.FsIntegralOrderDf;
+import com.fs.his.mapper.FsIntegralOrderDfMapper;
+import com.fs.his.service.IFsIntegralOrderDfService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 【请填写功能名称】Service业务层处理
+ *
+ * @author fs
+ * @date 2025-11-11
+ */
+@Service
+public class FsIntegralOrderDfServiceImpl extends ServiceImpl<FsIntegralOrderDfMapper, FsIntegralOrderDf> implements IFsIntegralOrderDfService {
+
+    /**
+     * 查询【请填写功能名称】
+     *
+     * @param orderId 【请填写功能名称】主键
+     * @return 【请填写功能名称】
+     */
+    @Override
+    public FsIntegralOrderDf selectFsIntegralOrderDfByOrderId(Long orderId)
+    {
+        return baseMapper.selectFsIntegralOrderDfByOrderId(orderId);
+    }
+
+    /**
+     * 查询【请填写功能名称】列表
+     *
+     * @param fsIntegralOrderDf 【请填写功能名称】
+     * @return 【请填写功能名称】
+     */
+    @Override
+    public List<FsIntegralOrderDf> selectFsIntegralOrderDfList(FsIntegralOrderDf fsIntegralOrderDf)
+    {
+        return baseMapper.selectFsIntegralOrderDfList(fsIntegralOrderDf);
+    }
+
+    /**
+     * 新增【请填写功能名称】
+     *
+     * @param fsIntegralOrderDf 【请填写功能名称】
+     * @return 结果
+     */
+    @Override
+    public int insertFsIntegralOrderDf(FsIntegralOrderDf fsIntegralOrderDf)
+    {
+        return baseMapper.insertFsIntegralOrderDf(fsIntegralOrderDf);
+    }
+
+    /**
+     * 修改【请填写功能名称】
+     *
+     * @param fsIntegralOrderDf 【请填写功能名称】
+     * @return 结果
+     */
+    @Override
+    public int updateFsIntegralOrderDf(FsIntegralOrderDf fsIntegralOrderDf)
+    {
+        return baseMapper.updateFsIntegralOrderDf(fsIntegralOrderDf);
+    }
+
+    /**
+     * 批量删除【请填写功能名称】
+     *
+     * @param orderIds 需要删除的【请填写功能名称】主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsIntegralOrderDfByOrderIds(Long[] orderIds)
+    {
+        return baseMapper.deleteFsIntegralOrderDfByOrderIds(orderIds);
+    }
+
+    /**
+     * 删除【请填写功能名称】信息
+     *
+     * @param orderId 【请填写功能名称】主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsIntegralOrderDfByOrderId(Long orderId)
+    {
+        return baseMapper.deleteFsIntegralOrderDfByOrderId(orderId);
+    }
+}

+ 92 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderLogsServiceImpl.java

@@ -0,0 +1,92 @@
+package com.fs.his.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.his.domain.FsIntegralOrderLogs;
+import com.fs.his.mapper.FsIntegralOrderLogsMapper;
+import com.fs.his.service.IFsIntegralOrderLogsService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 订单操作记录Service业务层处理
+ *
+ * @author fs
+ * @date 2025-11-25
+ */
+@Service
+public class FsIntegralOrderLogsServiceImpl extends ServiceImpl<FsIntegralOrderLogsMapper, FsIntegralOrderLogs> implements IFsIntegralOrderLogsService {
+
+    /**
+     * 查询订单操作记录
+     *
+     * @param logsId 订单操作记录主键
+     * @return 订单操作记录
+     */
+    @Override
+    public FsIntegralOrderLogs selectFsIntegralOrderLogsByLogsId(String logsId)
+    {
+        return baseMapper.selectFsIntegralOrderLogsByLogsId(logsId);
+    }
+
+    /**
+     * 查询订单操作记录列表
+     *
+     * @param fsIntegralOrderLogs 订单操作记录
+     * @return 订单操作记录
+     */
+    @Override
+    public List<FsIntegralOrderLogs> selectFsIntegralOrderLogsList(FsIntegralOrderLogs fsIntegralOrderLogs)
+    {
+        return baseMapper.selectFsIntegralOrderLogsList(fsIntegralOrderLogs);
+    }
+
+    /**
+     * 新增订单操作记录
+     *
+     * @param fsIntegralOrderLogs 订单操作记录
+     * @return 结果
+     */
+    @Override
+    public int insertFsIntegralOrderLogs(FsIntegralOrderLogs fsIntegralOrderLogs)
+    {
+        return baseMapper.insertFsIntegralOrderLogs(fsIntegralOrderLogs);
+    }
+
+    /**
+     * 修改订单操作记录
+     *
+     * @param fsIntegralOrderLogs 订单操作记录
+     * @return 结果
+     */
+    @Override
+    public int updateFsIntegralOrderLogs(FsIntegralOrderLogs fsIntegralOrderLogs)
+    {
+        return baseMapper.updateFsIntegralOrderLogs(fsIntegralOrderLogs);
+    }
+
+    /**
+     * 批量删除订单操作记录
+     *
+     * @param logsIds 需要删除的订单操作记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsIntegralOrderLogsByLogsIds(String[] logsIds)
+    {
+        return baseMapper.deleteFsIntegralOrderLogsByLogsIds(logsIds);
+    }
+
+    /**
+     * 删除订单操作记录信息
+     *
+     * @param logsId 订单操作记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsIntegralOrderLogsByLogsId(String logsId)
+    {
+        return baseMapper.deleteFsIntegralOrderLogsByLogsId(logsId);
+    }
+}

+ 6 - 16
fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java

@@ -816,13 +816,7 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
         TransferBillsRequest request = new TransferBillsRequest();
         request.setAppid(config.getAppId());
         request.setOpenid(param.getOpenId());
-
-        String code =  OrderCodeUtils.getOrderSn();
-        if(StringUtils.isEmpty(code)){
-            return R.error("订单生成失败,请重试");
-        }
-//        String code = String.valueOf(IdUtil.getSnowflake(0, 0).nextId());
-        request.setOutBillNo("fsCourse" + code);
+        request.setOutBillNo(param.getOutBatchNo());
 
         Integer amount = WxPayUnifiedOrderRequest.yuanToFen(param.getAmount() != null ? param.getAmount().toString() : "0.1");
         request.setTransferAmount(amount);
@@ -872,14 +866,7 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
         TransferBatchesRequest request = new TransferBatchesRequest();
         request.setAppid(config.getAppId());
 
-
-        // todo 如果未配置负载均衡请还原原本的单号方式
-//        String code = IdUtil.getSnowflake(0, 0).nextIdStr();
-        String code =  OrderCodeUtils.getOrderSn();
-        if(StringUtils.isEmpty(code)){
-            return R.error("红包单号生成失败,请重试");
-        }
-        request.setOutBatchNo("fsCourse"+cloudHostProper.getProjectCode() + code);
+        request.setOutBatchNo(param.getOutBatchNo());
         request.setBatchRemark("课堂答题奖励");
         request.setBatchName("课堂答题奖励");
         Integer amount = WxPayUnifiedOrderRequest.yuanToFen(param.getAmount().toString());
@@ -899,7 +886,10 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
 
         try {
             TransferBatchesResult transferBatchesResult = transferService.transferBatches(request);
-            return R.ok("发送红包成功").put("orderCode", transferBatchesResult.getOutBatchNo()).put("batchId", transferBatchesResult.getBatchId());
+            return R.ok("发送红包成功")
+                    .put("orderCode", transferBatchesResult.getOutBatchNo())
+                    .put("batchId", transferBatchesResult.getBatchId())
+                    .put("createTime", transferBatchesResult.getCreateTime());
         } catch (Exception e) {
             logger.error("商家转账支付失败:参数: {} :原因: {}",JSON.toJSONString(param), e.getMessage(),e);
             throw new RuntimeException(e);

+ 147 - 21
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -19,6 +19,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.constant.HttpStatus;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.domain.entity.SysDictData;
@@ -32,6 +33,7 @@ import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.DictUtils;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.cache.ICompanyTagCacheService;
 import com.fs.company.cache.ICompanyUserCacheService;
 import com.fs.company.domain.*;
@@ -67,6 +69,7 @@ import com.fs.im.config.ImTypeConfig;
 import com.fs.im.service.OpenIMService;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
 import com.fs.hisStore.domain.FsUserBillScrm;
+import com.fs.hisStore.domain.FsUserScrm;
 import com.fs.hisStore.enums.BillDetailEnum;
 import com.fs.hisStore.mapper.FsStoreOrderScrmMapper;
 import com.fs.hisStore.service.IFsUserBillScrmService;
@@ -78,6 +81,7 @@ import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
 import com.fs.store.domain.FsUserCourseCount;
 import com.fs.store.mapper.FsUserCourseCountMapper;
+import com.fs.store.param.h5.FsUserPageListExportParam;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.param.h5.UserStatisticsCommonParam;
 import com.fs.store.service.cache.IFsUserCourseCountCacheService;
@@ -194,6 +198,8 @@ public class FsUserServiceImpl implements IFsUserService {
     private FsPatientMapper fsPatientMapper;
     @Autowired
     private FsCourseWatchLogMapper courseWatchLogMapper;
+    @Autowired
+    FsExportTaskMapper fsExportTaskMapper;
 
     @Autowired
     private FsUserCompanyUserMapper fsUserCompanyUserMapper;
@@ -1265,11 +1271,11 @@ public class FsUserServiceImpl implements IFsUserService {
                 if (tuiUser != null) {
                     FsUser tuiUserMap = new FsUser();
                     tuiUserMap.setUserId(tuiUser.getUserId());
-                    tuiUserMap.setNowMoney(tuiUser.getNowMoney().add(new BigDecimal(bill.getNumber())));
-                    tuiUserMap.setBrokeragePrice(tuiUser.getBrokeragePrice().subtract(new BigDecimal(bill.getNumber())));
+                    tuiUserMap.setNowMoney(tuiUser.getNowMoney().add(bill.getMoney()));
+                    tuiUserMap.setBrokeragePrice(tuiUser.getBrokeragePrice().subtract(bill.getMoney()));
                     fsUserMapper.updateFsUser(tuiUserMap);
-                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(), 0, BillDetailEnum.TYPE_6.getDesc(), bill.getNumber().doubleValue(), tuiUserMap.getBrokeragePrice().doubleValue(), "订单分佣金", order.getId().toString(), bill.getTuiUserId());
-                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_1.getValue(), 1, BillDetailEnum.TYPE_5.getDesc(), bill.getNumber().doubleValue(), tuiUserMap.getNowMoney().doubleValue(), "订单分佣金", order.getId().toString(), bill.getTuiUserId());
+                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(), 0, BillDetailEnum.TYPE_6.getDesc(), bill.getMoney(), tuiUserMap.getBrokeragePrice(), "订单分佣金", order.getId().toString(), bill.getTuiUserId());
+                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_1.getValue(), 1, BillDetailEnum.TYPE_5.getDesc(), bill.getMoney(), tuiUserMap.getNowMoney(), "订单分佣金", order.getId().toString(), bill.getTuiUserId());
                 }
             }
         }
@@ -1661,22 +1667,142 @@ public class FsUserServiceImpl implements IFsUserService {
         return new HisFsUserVO();
     }
 
-//    @Override
-//    @Transactional(rollbackFor = Exception.class)
-//    public Boolean bindUserToSales(Long userId, Long salesId) {
-//        FsUser fsUser = fsUserMapper.selectFsUserById(userId);
-//        if (fsUser == null) {
-//            throw new CustomException("用户不存在");
-//        }
-//        if(fsUser.getInvitedBySalesId() != null){
-//            if(!fsUser.getInvitedBySalesId().equals(salesId)){
-//                throw new CustomException("该用户已从其他销售的链接下载");
-//            }else {
-//                throw new CustomException("已发送给app下载链接给该用户");
-//            }
-//        }
-//        fsUser.setInvitedBySalesId(salesId);
-//        return fsUserMapper.updateFsUser(fsUser)>0;
-//    }
+    @Override
+    public List<FsUser> selectFsUserListByPhone(String phone) {
+        return fsUserMapper.selectFsUserListByPhone(phone);
+    }
+
+    /**
+     * 项目会员导出数据条数查询
+     */
+    @Override
+    public Long selectProjectUserExportCount(FsUserPageListExportParam param) {
+        return fsUserMapper.selectProjectUserExportCount(param);
+    }
+
+    /**
+     * 项目会员导出
+     */
+    @Override
+    public void exportProjectUserData(FsUserPageListExportParam param) {
+        // 找出下级销售
+        String companyUserId = param.getCompanyUserId();
+        if (StringUtils.isNotBlank(companyUserId)) {
+            Long companyUser = Long.parseLong(companyUserId);
+            Set<Long> userIds = companyUserCacheService.selectUserAllCompanyUserId(companyUser);
+            if (userIds != null || userIds.size() <= 1) {
+                if (param.getIsAdmin() != null && param.getIsAdmin()) {
+                    List<CompanyUser> companyUsers = companyUserMapper.selectCompanyUserByCompanyId(param.getCompanyId());
+                    userIds = companyUsers.stream().map(CompanyUser::getUserId).collect(Collectors.toSet());
+                }
+            }
+            param.setCompanyUserIds(userIds);
+        }
+
+        List<FsUserPageListExportVO> fsUserPageListVOS = fsUserMapper.FsUserPageListExportVO(param);
+        Map<Long, CompanyTag> tagMap = companyTagCacheService.queryAllTagMap();
+        //获取会员的最新的看课状态和最后看课时间
+        Set<Long> userIds = fsUserPageListVOS.stream().map(FsUserPageListExportVO::getUserId).collect(Collectors.toSet());
+        List<FsUserLastCount> fsUserCourseCounts = Collections.emptyList();
+        if (!userIds.isEmpty()) {
+            fsUserCourseCounts = fsUserCourseCountMapper.selectUserLastCount(userIds);
+        }
+        Map<Long, FsUserLastCount> countMap = fsUserCourseCounts.stream().collect(Collectors.toMap(FsUserLastCount::getUserId, Function.identity()));
+
+        List<UserProjectPair> pairs = fsUserPageListVOS.stream()
+                .map(user -> new UserProjectPair(user.getUserId(), user.getProjectId()))
+                .collect(Collectors.toList());
+        Map<Object, Date> watchTimeMap = new HashMap<>();
+        if(CollectionUtils.isNotEmpty(pairs)){
+            List<Map<String, Object>> mapList = fsUserMapper.selectLastWatchTimeByUserAndProject(pairs);
+            if (!CollectionUtils.isEmpty(mapList)) {
+                watchTimeMap = mapList.stream()
+                        .collect(HashMap::new,
+                                (map, item) -> map.put(
+                                        item.get("userProjectKey"),
+                                        (Date) item.get("lastWatchTime")
+                                ),
+                                HashMap::putAll
+                        );
+            }
+        }
+        for (FsUserPageListExportVO item : fsUserPageListVOS) {
+            if (item.getCompanyUserId() != null) {
+                String companyUserName = companyUserCacheService.selectCompanyUserNameUserById(item.getCompanyUserId());
+                if (companyUserName != null) {
+                    item.setCompanyUserNickName(companyUserName);
+                }
+            }
+            if (item.getPhone() != null) {
+                item.setPhone(ParseUtils.parsePhone(item.getPhone()));
+            }
+            if (item.getStatus() != null) {
+                String userStatus = DictUtils.getDictLabel("user_status", String.valueOf(item.getStatus()));
+                if (StringUtils.isNotBlank(userStatus)) {
+                    item.setStatusText(userStatus);
+                }
+            }
+            if (item.getUserId() != null) {
+                FsUserCourseCount byUserId = fsUserCourseCountCacheService.findByUserId(item.getUserId());
+                if (byUserId != null) {
+                    item.setWatchCourseCount(byUserId.getWatchCourseCount());
+                    item.setMissCourseCount(byUserId.getMissCourseCount());
+                    item.setMissCourseStatus(byUserId.getMissCourseStatus());
+                    if (StringUtils.isNotEmpty(byUserId.getPartCourseCount())) {
+                        item.setPartCourseCount(new BigDecimal(byUserId.getPartCourseCount()).longValue());
+                    }
+//                    item.setCourseCountStatus(byUserId.getStatus());
+                    item.setStopWatchDays(byUserId.getStopWatchDays());
+                    item.setCompleteWatchDate(byUserId.getCompleteWatchDate());
+                    item.setLastWatchDate(watchTimeMap.getOrDefault(item.getUserId() + "_" + item.getProjectId(),null));
+                }
+                FsUserLastCount fsUserCourseCount = countMap.get(item.getUserId());
+                if (fsUserCourseCount != null) {
+                    item.setCourseCountStatus(fsUserCourseCount.getStatus());
+                }
+                String userTagByUserId = companyTagCacheService
+                        .findUserTagByUserId(item.getUserId(), item.getCompanyUserId());
+                if (StringUtils.isNotEmpty(userTagByUserId)) {
+                    String[] split = userTagByUserId.split(",");
+                    Set<String> tagNames = new HashSet<>();
+                    for (String tag : split) {
+                        if (StringUtils.isNotBlank(tag)) {
+                            Long tagL = Long.parseLong(tag);
+                            CompanyTag companyTag = tagMap.get(tagL);
+                            if (companyTag != null) {
+                                tagNames.add(companyTag.getTag());
+                            }
+                        }
+                    }
+                    item.setTagIds(userTagByUserId);
+                    item.setTag(String.join(",", tagNames));
+                }
+
+                // 是否宠粉
+//                Integer isRepeat = qwExternalContactCacheService.selectQwIsRepeat(item.getUserId());
+//                if(isRepeat != null) {
+//                    item.setIsRepeat(isRepeat);
+//                }
+            }
+
+            // 解密
+            if (item.getPhone() != null && item.getPhone() != "") {
+                if (item.getPhone().length() > 11) {
+                    item.setPhone(decryptPhoneMk(item.getPhone()));
+                } else {
+                    item.setPhone(item.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                }
+            }
+
+        }
+
+        ExcelUtil<FsUserPageListExportVO> util = new ExcelUtil<>(FsUserPageListExportVO.class);
+        AjaxResult result =  util.exportExcel(fsUserPageListVOS, "项目会员");
+        FsExportTask task=fsExportTaskMapper.selectFsExportTaskByTaskId(param.getTaskId());
+        task.setFinishTime(new Date());
+        task.setStatus(1);
+        task.setFileUrl(result.get("msg").toString());
+        fsExportTaskMapper.updateFsExportTask(task);
+    }
 
 }

+ 1 - 1
fs-service/src/main/java/com/fs/his/vo/FsCourseReportVO.java

@@ -76,7 +76,7 @@ public class FsCourseReportVO {
     /**
      * 红包领取数
      */
-    @Excel(name = "红包领取数")
+    @Excel(name = "红包领取数")
     private  Integer packetUserCount;
 
     /**

+ 32 - 0
fs-service/src/main/java/com/fs/his/vo/FsIntegralOrderExcelVO.java

@@ -0,0 +1,32 @@
+package com.fs.his.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class FsIntegralOrderExcelVO {
+    @Excel(name = "药品订单号",required = true)
+    private String orderCode;
+
+    @Excel(name = "订单状态",dictType = "sys_integral_order_status")
+    private String status;
+
+//    @Excel(name = "物流代收金额")
+//    private BigDecimal payRemain;
+
+    /** 物流状态 */
+    @Excel(name = "物流状态",dictType = "sys_store_order_delivery_status")
+    private String deliveryStatus;
+
+    /** 物流跟踪状态 */
+    @Excel(name = "物流跟踪状态",dictType = "sys_delivery_type")
+    private String deliveryType;
+
+    /** shou */
+    @Excel(name = "收货人电话")
+    private String userPhone;
+    /** 详情地址 */
+    @Excel(name = "详情地址(例:广东省 韶关市 仁化县 亨特中心22楼)使用空格分割")
+    private String userAddress;
+
+}

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

@@ -25,4 +25,7 @@ public class StoreConfig implements Serializable {
     private Integer createSalesOrderType; // 订单改价方式 1 商品改价 2总价改价
     private Boolean isBrushOrders;//是否开启刷单按钮
     private Integer orderAttribution; // 下单归属 1 多销售 2单销售
+    private Boolean isWeChatShipping;//是否开启微信发货
+    private Boolean scanCodeDiscountEnabled;//是否开启扫码立减金
+    private BigDecimal scanCodeDiscountAmount;//扫码立减金额
 }

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

@@ -351,4 +351,6 @@ public class FsStoreOrderScrm extends BaseEntity
     private BigDecimal billPrice;
     private String erpPhone;
 
+    @TableField(exist = false)
+    private String bankTransactionId;
 }

+ 27 - 12
fs-service/src/main/java/com/fs/hisStore/domain/FsUserBillScrm.java

@@ -4,9 +4,11 @@ import com.baomidou.mybatisplus.annotation.TableName;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 
+import java.math.BigDecimal;
+
 /**
  * 用户账单对象 fs_user_bill
- * 
+ *
  * @author fs
  * @date 2022-04-30
  */
@@ -16,7 +18,7 @@ public class FsUserBillScrm extends BaseEntity
     private static final long serialVersionUID = 1L;
 
     /** 用户账单id */
-    private Long id;
+    private Long billId;
 
     /** 用户uid */
     @Excel(name = "用户uid")
@@ -44,11 +46,14 @@ public class FsUserBillScrm extends BaseEntity
 
     /** 明细数字 */
     @Excel(name = "明细数字")
-    private Double number;
+    private BigDecimal number;
+
+    @Excel(name = "金额")
+    private BigDecimal money;
 
     /** 剩余 */
     @Excel(name = "剩余")
-    private Double balance;
+    private BigDecimal balance;
 
     /** 0 = 带确定 1 = 有效 -1 = 无效 */
     @Excel(name = "0 = 带确定 1 = 有效 -1 = 无效")
@@ -56,6 +61,15 @@ public class FsUserBillScrm extends BaseEntity
 
     private Long tuiUserId;
 
+
+    public BigDecimal getMoney() {
+        return money;
+    }
+
+    public void setMoney(BigDecimal money) {
+        this.money = money;
+    }
+
     public Long getTuiUserId() {
         return tuiUserId;
     }
@@ -68,12 +82,13 @@ public class FsUserBillScrm extends BaseEntity
         return serialVersionUID;
     }
 
-    public Long getId() {
-        return id;
+    public Long getBillId() {
+        return billId;
     }
 
-    public void setId(Long id) {
-        this.id = id;
+    public FsUserBillScrm setBillId(Long billId) {
+        this.billId = billId;
+        return this;
     }
 
     public Long getUserId() {
@@ -124,19 +139,19 @@ public class FsUserBillScrm extends BaseEntity
         this.title = title;
     }
 
-    public Double getNumber() {
+    public BigDecimal getNumber() {
         return number;
     }
 
-    public void setNumber(Double number) {
+    public void setNumber(BigDecimal number) {
         this.number = number;
     }
 
-    public Double getBalance() {
+    public BigDecimal getBalance() {
         return balance;
     }
 
-    public void setBalance(Double balance) {
+    public void setBalance(BigDecimal balance) {
         this.balance = balance;
     }
 

+ 36 - 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;
 
     /**
@@ -178,6 +182,19 @@ public class FsUserScrm extends BaseEntity
      * **/
     private Long qwUserId;
 
+    /**
+     * 小程序appId,多个用逗号分隔
+     */
+    private String appId;
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
     public void setNickName(String nickname)
     {
         if(StringUtils.isNotEmpty(nickname)){
@@ -215,6 +232,16 @@ public class FsUserScrm extends BaseEntity
     private String companyName;
     private String rank;
     private String react;
+    /** 绑定的公司用户ID */
+    private Long bindCompanyUserId;
+
+    public Long getBindCompanyUserId() {
+        return bindCompanyUserId;
+    }
+
+    public void setBindCompanyUserId(Long bindCompanyUserId) {
+        this.bindCompanyUserId = bindCompanyUserId;
+    }
 
     /**
      * 项目ID
@@ -816,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;
+    }
 }

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

@@ -15,6 +15,7 @@ public enum OrderInfoEnum {
     STATUS_1(1,"待发货"),
     STATUS_2(2,"待收货"),
     STATUS_3(3,"已完成"),
+    STATUS_4(4,"待评价"),
 
     PAY_STATUS_0(0,"未支付"),
     PAY_STATUS_1(1,"已支付"),

+ 2 - 1
fs-service/src/main/java/com/fs/hisStore/enums/ShipperCodeEnum.java

@@ -6,7 +6,8 @@ import lombok.Getter;
 @Getter
 @AllArgsConstructor
 public enum  ShipperCodeEnum {
-    SF("SF","顺丰速运");
+    SF("SF","顺丰速运"),
+    ZTO("ZTO","中通快递");
 
     private String value;
     private String desc;

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

@@ -509,9 +509,9 @@ public interface FsStoreOrderScrmMapper
     @Select("select * from fs_store_order_scrm where extend_order_id=#{extendOrderId}")
     FsStoreOrderScrm selectFsStoreOrderByExtendOrderId(String extendOrderId);
     @Select({"<script> " +
-            "select b.number as tui_price,b.bill_type,b.create_time,o.id as order_id,o.order_code,o.pay_price,u.nickname,u.avatar from fs_user_bill b inner join  fs_store_order_scrm o on b.business_id=o.id left join fs_user u on u.user_id=o.user_id  " +
+            "select b.money as tui_price,b.bill_type,b.create_time,o.id as order_id,o.order_code,o.pay_price,u.nickname,u.avatar from fs_user_bill b inner join  fs_store_order_scrm o on b.business_id=o.id left join fs_user u on u.user_id=o.user_id  " +
             "where b.user_id=#{userId} and b.category='brokerage_price'   " +
-            " order by b.id desc"+
+            " order by b.bill_id desc"+
             "</script>"})
     List<FsStoreOrderTuiVO> selectFsStoreOrderTuiListVO(String userId);
     @Select({"<script> " +
@@ -1378,4 +1378,21 @@ public interface FsStoreOrderScrmMapper
 
     @Select("select * from fs_store_order_scrm where  `status`=2 and (extend_order_id is not null and extend_order_id != '') and (delivery_id is not null and delivery_id != '')")
     List<FsStoreOrderScrm> selectShippedOrder();
+    /**
+     * 获取发货单数据
+     * @param maps 查询参数
+     * @return list
+     * **/
+    List<FsStoreOrderDeliveryNoteExportVO> getDeliveryNote(@Param("maps") FsStoreOrderParam maps);
+    /**
+     * 获取订单用户信息
+     * @param list 订单号
+     * @return lsit
+     * **/
+    List<FsStoreOrderCodeOpenIdVo> selectOrderCodeOpenIdInOrderCode(@Param("list") List<String> list);
+    /**
+     * 获取未结算订单
+     * **/
+    List<FsStoreOrderScrm> getUnsettledOrder();
+
 }

+ 28 - 1
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,7 +21,9 @@ 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;
 import java.util.Map;
 
@@ -124,6 +127,13 @@ public interface FsUserScrmMapper
     @Select("select * from fs_user where phone=#{phone}")
     FsUserScrm selectFsUserByPhone(String phone);
 
+    /**
+     * 根据手机号码精确查询用户列表(完全匹配)
+     * @param phone 手机号码
+     * @return 用户列表
+     */
+    List<FsUserScrm> selectFsUserListByPhoneExact(String phone);
+
 
     @Select("select  b.total_amount,b.last_buy_time,p.pay_money as number,p.payment_id,p.pay_time," +
             " u.* FROM fs_user u LEFT JOIN  (" +
@@ -158,7 +168,7 @@ public interface FsUserScrmMapper
             "</script>"})
     List<FsCompanyUserListQueryVO> selectFsCompanyUserListQuery(@Param("maps") FsUserScrm fsUser);
     @Select({"<script> " +
-            "select u.user_id,u.phone,u.avatar,u.nickname,ifnull((select sum(b.number) from fs_user_bill b where b.category='brokerage_price' and b.bill_type=1  and b.tui_user_id=u.user_id and b.user_id=#{userId} ),0)-ifnull((select sum(b.number) from fs_user_bill b where b.category='brokerage_price' and b.bill_type=0 and b.tui_user_id=u.user_id and b.user_id=#{userId}  ),0) as tui_money ,u.create_time from fs_user u " +
+            "select u.user_id,u.phone,u.avatar,u.nickname,ifnull((select sum(b.money) from fs_user_bill b where b.category='brokerage_price' and b.bill_type=1  and b.tui_user_id=u.user_id and b.user_id=#{userId} ),0)-ifnull((select sum(b.money) from fs_user_bill b where b.category='brokerage_price' and b.bill_type=0 and b.tui_user_id=u.user_id and b.user_id=#{userId}  ),0) as tui_money ,u.create_time from fs_user u " +
             "where u.spread_user_id=#{userId} " +
             "order by tui_money desc " +
             "</script>"})
@@ -319,4 +329,21 @@ public interface FsUserScrmMapper
     Map<String, Long> countCourseDetailsNew(UserStatisticsCommonParam param);
     @Select("select * from fs_user where mp_open_id=#{openId}")
     FsUserScrm selectFsUserByOpenId(String openId);
+
+    /**
+     * 更新用户下单次数和累计成交总额
+     * @param userId 用户ID
+     * @param amount 成交金额
+     */
+    @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);
 }

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

@@ -8,4 +8,5 @@ import lombok.Setter;
 public class FsStoreConfirmPackageIdOrderParam {
     private Long packageId;
     private Long couponUserId;
+    private String createOrderKey;
 }

+ 6 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStorePackageOrderCreateParam.java

@@ -6,6 +6,7 @@ import lombok.Data;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
 import java.io.Serializable;
+import java.math.BigDecimal;
 
 @Data
 public class FsStorePackageOrderCreateParam implements Serializable
@@ -22,5 +23,10 @@ public class FsStorePackageOrderCreateParam implements Serializable
     private Long packageId;
     private Long companyUserId;
     private Long couponUserId;
+    @ApiModelProperty(value = "修改后的价格")
+    private BigDecimal payAmount;
+
+    @ApiModelProperty(value = "是否是套餐包制单",example  = "0")
+    private Integer isPackage;
 
 }

+ 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;
+}

+ 26 - 1
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java

@@ -39,6 +39,10 @@ import com.fs.his.vo.FsPrescribeVO;
 public interface IFsStoreOrderScrmService
 {
 
+    /**
+     * 同步物流信息到微信
+     */
+    void syncExpressToWx();
     /**
      * 查询订单
      *
@@ -181,7 +185,7 @@ public interface IFsStoreOrderScrmService
 
     R addUserCart(long userId, String createOrderKey);
 
-    R updateSalseOrderMoney(String createOrderKey, BigDecimal money,BigDecimal payAmount,Integer payType);
+    R updateSalseOrderMoney(String createOrderKey, BigDecimal money,BigDecimal payAmount,Integer payType, Integer isPackage);
 
     Integer selectFsStoreOrderCountByType(Long companyId, long userId, int type);
 
@@ -320,4 +324,25 @@ public interface IFsStoreOrderScrmService
     R dfOrderResult(String body);
 
     R receiveWaybillPush(String body);
+
+    /**
+     * 获取发货单数据
+     * @param param 查询条件
+     * @return list
+     * **/
+    List<FsStoreOrderDeliveryNoteExportVO> getDeliveryNote(FsStoreOrderParam param);
+
+    R importDeliveryNoteExpress(List<FsOrderDeliveryNoteDTO> dtoList, String miniAppId);
+
+    void refreshOrderSettlementStatus();
+
+    /**
+     * 套餐包制单
+     * @param companyUser 销售
+     * @param packageId 套餐包id
+     * @param orderType 订单类型
+     * @param orderMedium 媒体类型
+     * @return
+     */
+    R createPackageSalesOrder(CompanyUser companyUser, String packageId, Integer orderType, Integer orderMedium);
 }

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

@@ -9,7 +9,7 @@ import com.fs.hisStore.vo.FsUserBillVO;
 
 /**
  * 用户账单Service接口
- * 
+ *
  * @author fs
  * @date 2022-04-03
  */
@@ -17,7 +17,7 @@ public interface IFsUserBillScrmService
 {
     /**
      * 查询用户账单
-     * 
+     *
      * @param id 用户账单ID
      * @return 用户账单
      */
@@ -25,7 +25,7 @@ public interface IFsUserBillScrmService
 
     /**
      * 查询用户账单列表
-     * 
+     *
      * @param fsUserBill 用户账单
      * @return 用户账单集合
      */
@@ -33,7 +33,7 @@ public interface IFsUserBillScrmService
 
     /**
      * 新增用户账单
-     * 
+     *
      * @param fsUserBill 用户账单
      * @return 结果
      */
@@ -41,7 +41,7 @@ public interface IFsUserBillScrmService
 
     /**
      * 修改用户账单
-     * 
+     *
      * @param fsUserBill 用户账单
      * @return 结果
      */
@@ -49,7 +49,7 @@ public interface IFsUserBillScrmService
 
     /**
      * 批量删除用户账单
-     * 
+     *
      * @param ids 需要删除的用户账单ID
      * @return 结果
      */
@@ -57,13 +57,13 @@ public interface IFsUserBillScrmService
 
     /**
      * 删除用户账单信息
-     * 
+     *
      * @param id 用户账单ID
      * @return 结果
      */
     public int deleteFsUserBillById(Long id);
 
-    void addBill(Long uid,String cate,Integer billType, String title, double number, double balance, String remark,String busId,Long tuiUserId);
+    void addBill(Long uid,String cate,Integer billType, String title, BigDecimal number, BigDecimal balance, String remark,String busId,Long tuiUserId);
 
 
     List<FsUserBillScrm> selectFsUserBillListQuery(FsUserBillQueryParam param);

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

@@ -1,11 +1,18 @@
 package com.fs.hisStore.service;
 
+import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.param.LoginMaWxParam;
 import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
 import com.fs.course.vo.newfs.FsCourseAnalysisVO;
+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;
 import com.fs.hisStore.domain.FsStoreOrderScrm;
@@ -22,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;
 
@@ -104,6 +112,13 @@ public interface IFsUserScrmService
 
     FsUserScrm selectFsUserByPhone(String phone);
 
+    /**
+     * 根据手机号码精确查询用户列表(完全匹配)
+     * @param phone 手机号码
+     * @return 用户列表
+     */
+    List<FsUserScrm> selectFsUserListByPhoneExact(String phone);
+
     TableDataInfo selectCusListPage(SelectCusListPageParam param);
 
     List<FsCompanyUserListQueryVO> selectFsCompanyUserListQuery(FsUserScrm fsUser);
@@ -147,6 +162,8 @@ public interface IFsUserScrmService
 
     UserListPageVO getUserNumber(Long userId);
 
+    UserDetailsVO getUserDetails(Long userId, Long fsUserId, String dateTag, Long userCompanyId);
+
     /**
      * 查询重粉用户是否存在
      * @param userIds
@@ -270,4 +287,14 @@ public interface IFsUserScrmService
     List<FSUserVO> selectFsUserVOListByProject(FsUserScrm fsUser);
 
     FsUserScrm selectFsUserByOpenId(String openid);
+
+    void addTuiLiveMoney(LiveOrder order, List<FsStoreProductAttrValueScrm> productAttrValues);
+
+    void subLiveTuiMoney(LiveOrder liveOrder);
+
+    void handleFsUserWx(FsUserScrm user, LoginMaWxParam param, WxMaJscode2SessionResult session);
+
+    void incPayMoney(BigDecimal payMoney, Long userId);
+
+    List<UserScrmVo> selectUserVOList(String phone);
 }

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

@@ -1,6 +1,8 @@
 package com.fs.hisStore.service.impl;
 
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*;
+import cn.binarywang.wx.miniapp.bean.shop.response.WxMaOrderShippingInfoGetResponse;
 import cn.hutool.core.date.DateTime;
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.hutool.core.net.URLDecoder;
@@ -46,6 +48,7 @@ import com.fs.core.config.WxMaConfiguration;
 import com.fs.core.config.WxPayProperties;
 import com.fs.core.utils.OrderCodeUtils;
 import com.fs.course.dto.FsOrderDeliveryNoteDTO;
+import com.fs.course.dto.OrderOpenIdTransDTO;
 import com.fs.erp.domain.*;
 import com.fs.erp.dto.*;
 import com.fs.erp.dto.df.*;
@@ -102,6 +105,12 @@ import com.fs.hisStore.enums.*;
 import com.fs.hisStore.service.*;
 import com.fs.system.service.ISysConfigService;
 import com.fs.wx.miniapp.config.WxMaProperties;
+import com.fs.wx.order.domain.FsWxExpressTask;
+import com.fs.wx.order.dto.*;
+import com.fs.wx.order.mapper.FsWxExpressTaskMapper;
+import com.fs.wx.order.service.ExpressToWxHolder;
+import com.fs.wx.order.service.ExpressToWxService;
+import com.fs.wx.order.service.ShippingService;
 import com.fs.ybPay.domain.OrderResult;
 import com.fs.ybPay.domain.RefundResult;
 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
@@ -112,6 +121,7 @@ import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
 import com.github.binarywang.wxpay.config.WxPayConfig;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.WxPayService;
+import com.google.common.base.Joiner;
 import lombok.Synchronized;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
@@ -141,6 +151,7 @@ import java.sql.Timestamp;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
@@ -360,6 +371,13 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
 
     @Autowired
     private FsStoreOrderDfMapper fsStoreOrderDfMapper;
+    @Autowired
+    private ShippingService shippingService;
+    @Autowired
+    private FsWxExpressTaskMapper fsWxExpressTaskMapper;
+
+    @Autowired
+    private IFsStoreCartScrmService fsStoreCartScrmService;
 
     @PostConstruct
     public void initErpServiceMap() {
@@ -371,6 +389,82 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         erpServiceMap.put(5, jSTOrderService);     // 聚水潭
         erpServiceMap.put(6, k9OrderService);      // K9
     }
+    @Override
+    public void syncExpressToWx() {
+        List<FsWxExpressTask> fsWxExpressTasks = fsWxExpressTaskMapper.selectPendingData();
+        if (CollectionUtils.isEmpty(fsWxExpressTasks)) {
+            logger.info("当前没有待同步的数据!已取消");
+            return;
+        }
+
+        for (FsWxExpressTask fsWxExpressTask : fsWxExpressTasks) {
+
+            try{
+                UploadShippingInfoRequest request = new UploadShippingInfoRequest();
+
+                OrderKey orderKey = new OrderKey();
+                orderKey.setOrderNumberType(2);
+
+
+                FsUserScrm fsUser = userService.selectFsUserByUserId(fsWxExpressTask.getUserId());
+
+                // 目前支持商城订单和直播订单
+                ExpressToWxService service = ExpressToWxHolder.findBest(fsWxExpressTask.getType(),fsWxExpressTask.getOrderCode());
+                Asserts.notNull(service,"订单类型不被支持!");
+
+
+                orderKey.setTransactionId(service.getTransactionId());
+
+
+                String userPhone = service.getUserPhone();
+                String orderGoodsInfo = service.getOrderGoodsInfo();
+
+
+                Payer payer = new Payer();
+                if(StringUtils.isNotBlank(fsUser.getMaOpenId())){
+                    payer.setOpenid(fsUser.getMaOpenId());
+                }
+                request.setPayer(payer);
+                request.setOrderKey(orderKey);
+
+                request.setLogisticsType(1);
+                request.setDeliveryMode(1);
+
+                request.setShippingList(Collections.singletonList(ShippingItem.builder()
+                        .itemDesc(orderGoodsInfo)
+                        .expressCompany(service.getExpressCompany())
+                        .trackingNo(service.getExpressNo())
+                        .contact(Contact.builder().consignorContact(userPhone).build())
+                        .build()));
+
+                OffsetDateTime now = OffsetDateTime.now();
+                DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+                String formattedTimestamp = now.format(formatter);
+                request.setUploadTime(formattedTimestamp);
+
+
+                request.setAppid(fsWxExpressTask.getAppid());
+                WeChatApiResponse response = shippingService.uploadShippingInfo(request);
+                if(ObjectUtil.equal(response.getErrcode(),0)){
+                    fsWxExpressTask.setStatus(2);
+                } else {
+                    fsWxExpressTask.setRetryCount(fsWxExpressTask.getRetryCount() +1);
+                    fsWxExpressTask.setStatus(3);
+                    fsWxExpressTask.setData(JSON.toJSONString(request));
+                    fsWxExpressTask.setRequestBody(JSON.toJSONString(request));
+                    fsWxExpressTask.setResponseBody(JSON.toJSONString(response));
+                }
+            }catch (Exception e){
+                logger.info("该单 {} 推送到物流失败!",fsWxExpressTask);
+                fsWxExpressTask.setRetryCount(fsWxExpressTask.getRetryCount() +1);
+                fsWxExpressTask.setStatus(3);
+
+
+            }
+        }
+        fsWxExpressTaskMapper.batchUpdate(fsWxExpressTasks);
+
+    }
     /**
      * 查询订单
      *
@@ -905,7 +999,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             }
             //使用了积分扣积分
             if (dto.getUsedIntegral() > 0) {
-                this.decIntegral(userId, dto.getUsedIntegral(), dto.getDeductionPrice().doubleValue(), storeOrder.getId().toString());
+                this.decIntegral(userId, dto.getUsedIntegral(), dto.getDeductionPrice(), storeOrder.getId().toString());
             }
             //减库存加销量
             this.deStockIncSale(carts);
@@ -1109,11 +1203,11 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     /**
      * 积分抵扣
      */
-    private void decIntegral(Long uid, double usedIntegral, double deductionPrice, String busId) {
+    private void decIntegral(Long uid, double usedIntegral, BigDecimal deductionPrice, String busId) {
         userService.decIntegral(uid, usedIntegral);
         FsUserScrm user = userService.selectFsUserById(uid);
         //积分记录
-        billService.addBill(uid, BillDetailEnum.CATEGORY_2.getValue(), 0, BillDetailEnum.TYPE_1.getDesc(), usedIntegral, user.getIntegral().doubleValue(),
+        billService.addBill(uid, BillDetailEnum.CATEGORY_2.getValue(), 0, BillDetailEnum.TYPE_1.getDesc(), new BigDecimal(usedIntegral) ,new BigDecimal(user.getIntegral()),
                 "购买商品使用" + usedIntegral + "积分抵扣" + deductionPrice + "元", busId, 0l);
 
     }
@@ -1266,7 +1360,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     OrderLogEnum.DELIVERY_GOODS.getDesc());
             //订阅物流回调
             String lastFourNumber = "";
-            if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())) {
+            if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())  || order.getDeliverySn().equals(ShipperCodeEnum.ZTO.getValue())) {
                 lastFourNumber = order.getUserPhone();
                 if (lastFourNumber.length() == 11) {
                     lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
@@ -1283,6 +1377,20 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     .templateType(TemplateListenEnum.TYPE_2.getValue())
                     .build();
             publisher.publishEvent(new TemplateEvent(this, templateBean));
+
+            List<FsStorePaymentScrm> fsStorePayments = fsStorePaymentMapper.selectFsStorePaymentByOrderId(order.getId());
+            FsStorePaymentScrm fsStorePayment = fsStorePayments.get(0);
+            FsWxExpressTask fsWxExpressTask = new FsWxExpressTask();
+            fsWxExpressTask.setUserId(order.getUserId());
+            fsWxExpressTask.setStatus(0);
+            fsWxExpressTask.setRetryCount(0);
+            fsWxExpressTask.setCreateTime(LocalDateTime.now());
+            fsWxExpressTask.setUpdateTime(LocalDateTime.now());
+            fsWxExpressTask.setOrderCode(order.getOrderCode());
+            fsWxExpressTask.setExpressCompany(express.getCode());
+            fsWxExpressTask.setExpressNo(deliveryId);
+            fsWxExpressTask.setAppid(fsStorePayment.getAppId());
+            fsWxExpressTaskMapper.insert(fsWxExpressTask);
         }
     }
 
@@ -1301,7 +1409,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                         OrderLogEnum.DELIVERY_GOODS.getDesc());
                 //订阅物流回调
                 String lastFourNumber = "";
-                if (express.getCode().equals(ShipperCodeEnum.SF.getValue())) {
+                if (express.getCode().equals(ShipperCodeEnum.SF.getValue())  || express.getCode().equals(ShipperCodeEnum.ZTO.getValue())) {
                     lastFourNumber = order.getUserPhone();
                     if (lastFourNumber.length() == 11) {
                         lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
@@ -1360,7 +1468,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     public R getExpress(FsStoreOrderScrm order) {
         //顺丰轨迹查询处理
         String lastFourNumber = "";
-        if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())) {
+        if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue()) || order.getDeliverySn().equals(ShipperCodeEnum.ZTO.getValue())) {
             lastFourNumber = order.getUserPhone();
             if (lastFourNumber.length() == 11) {
                 lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
@@ -1450,14 +1558,24 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     public R confirmPackageOrder(long uid, FsStoreConfirmPackageIdOrderParam param) {
         FsUserAddressScrm address = userAddressMapper.selectFsUserAddressByDefaultAddress(uid);
         FsStoreProductPackageScrm storeProductPackage = productPackageService.selectFsStoreProductPackageById(param.getPackageId());
-        String uuid = IdUtil.randomUUID();
-        BigDecimal totalMoney = storeProductPackage.getPayMoney();
+        // 由于套餐制单前面有生成oderkey,并且要取修改的价格,所以这里判断,如果有传就用传的orderkey,如果没有就生成(代表走的是直接购买)
+        String uuid;
+        BigDecimal totalMoney;
+        if(StringUtils.isEmpty(param.getCreateOrderKey())){
+            //直接购买
+            uuid = IdUtil.randomUUID();
+            totalMoney = storeProductPackage.getPayMoney();
+        } else {
+            // 套餐制单
+            uuid = param.getCreateOrderKey();
+            totalMoney = redisCache.getCacheObject("createOrderMoney:" + param.getCreateOrderKey());
+        }
+
         if (param.getCouponUserId() != null) {
             FsStoreCouponUserScrm couponUser = couponUserService.selectFsStoreCouponUserById(param.getCouponUserId());
             if (couponUser != null && couponUser.getStatus() == 0) {
                 if (couponUser.getUseMinPrice().compareTo(storeProductPackage.getPayMoney()) == -1) {
-                    //
-                    totalMoney = totalMoney.subtract(couponUser.getCouponPrice());
+                    totalMoney = totalMoney != null ? totalMoney.subtract(couponUser.getCouponPrice()) : BigDecimal.ZERO.subtract(couponUser.getCouponPrice());
                 }
             }
         }
@@ -1493,29 +1611,37 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             List<StorePackageProductDTO> goodsList = JSONUtil.toList(jsonArray, StorePackageProductDTO.class);
             //检测库存
             Integer totalNum = 0;
-            List<FsStoreCartQueryVO> carts = new ArrayList<>();
-            for (StorePackageProductDTO goods : goodsList) {
-                FsStoreProductAttrValueScrm attrValue = attrValueService.selectFsStoreProductAttrValueById(goods.getId());
-                if (attrValue != null && attrValue.getProductId() != null) {
-                    FsStoreProductScrm product = storeProductService.selectFsStoreProductById(attrValue.getProductId());
-                    if (product != null) {
-                        totalNum += goods.getCount();
-                        FsStoreCartQueryVO vo = new FsStoreCartQueryVO();
-                        vo.setProductId(attrValue.getProductId());
-                        vo.setProductAttrValueId(goods.getId());
-                        vo.setCartNum(goods.getCount());
-                        vo.setProductName(product.getProductName());
-                        vo.setProductAttrName(attrValue.getSku());
-                        vo.setProductImage(product.getImage());
-                        vo.setBarCode(attrValue.getBarCode());
-                        vo.setGroupBarCode(attrValue.getGroupBarCode());
-                        vo.setPrice(product.getPrice());
-                        vo.setProductType(product.getProductType());
-                        carts.add(vo);
+            List<FsStoreCartQueryVO> carts = redisCache.getCacheObject("orderCarts:" + param.getOrderKey());
+            String joinCartIds = null;
+            if(carts == null || carts.isEmpty()){
+                carts = new ArrayList<>();
+                for (StorePackageProductDTO goods : goodsList) {
+                    FsStoreProductAttrValueScrm attrValue = attrValueService.selectFsStoreProductAttrValueById(goods.getId());
+                    if (attrValue != null && attrValue.getProductId() != null) {
+                        FsStoreProductScrm product = storeProductService.selectFsStoreProductById(attrValue.getProductId());
+                        if (product != null) {
+                            totalNum += goods.getCount();
+                            FsStoreCartQueryVO vo = new FsStoreCartQueryVO();
+                            vo.setProductId(attrValue.getProductId());
+                            vo.setProductAttrValueId(goods.getId());
+                            vo.setCartNum(goods.getCount());
+                            vo.setProductName(product.getProductName());
+                            vo.setProductAttrName(attrValue.getSku());
+                            vo.setProductImage(product.getImage());
+                            vo.setBarCode(attrValue.getBarCode());
+                            vo.setGroupBarCode(attrValue.getGroupBarCode());
+                            vo.setPrice(product.getPrice());
+                            vo.setProductType(product.getProductType());
+                            carts.add(vo);
 
+                        }
                     }
+                    cartService.checkProductStock(attrValue.getProductId(), attrValue.getId());
                 }
-                cartService.checkProductStock(attrValue.getProductId(), attrValue.getId());
+            } else {
+                // 如果cartIds不为空,则表示是套餐制单购买,非套餐制单是没有购物车信息的。
+                List<Long> cartIds = carts.stream().map(FsStoreCartQueryVO::getId).collect(Collectors.toList());
+                joinCartIds = Joiner.on(",").join(cartIds);
             }
             //生成分布式唯一值
             String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr();
@@ -1524,6 +1650,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             //组合数据
             CompanyUser user = companyUserService.selectCompanyUserById(param.getCompanyUserId());
             FsStoreOrderScrm storeOrder = new FsStoreOrderScrm();
+            storeOrder.setCartId(joinCartIds);
             //修改默认仓库
 
             storeOrder.setStoreHouseCode("CK01");
@@ -1532,7 +1659,13 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 storeOrder.setCompanyId(user.getCompanyId());
                 storeOrder.setDeptId(user.getDeptId());
             }
-            BigDecimal totalMoney = storeProductPackage.getPayMoney();
+            BigDecimal totalMoney;
+            if(param.getIsPackage() != null && param.getIsPackage() == 1){
+                totalMoney = redisCache.getCacheObject("createOrderMoney:" + param.getOrderKey());
+            } else {
+                totalMoney = storeProductPackage.getPayMoney();
+            }
+
             //优惠券处理
             if (param.getCouponUserId() != null) {
                 FsStoreCouponUserScrm couponUser = couponUserService.selectFsStoreCouponUserById(param.getCouponUserId());
@@ -1597,6 +1730,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             //减库存加销量
             this.deStockIncSale(carts);
             //保存购物车商品信息
+            List<FsStoreOrderItemScrm> listOrderItem = new ArrayList<>();
             for (FsStoreCartQueryVO vo : carts) {
                 FsStoreCartDTO fsStoreCartDTO = new FsStoreCartDTO();
                 fsStoreCartDTO.setProductId(vo.getProductId());
@@ -1624,7 +1758,13 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     item.setIsPrescribe(1);
                 }
                 fsStoreOrderItemMapper.insertFsStoreOrderItem(item);
+                listOrderItem.add(item);
+            }
+            if (!listOrderItem.isEmpty()) {
+                storeOrder.setItemJson(JSONUtil.toJsonStr(listOrderItem));
             }
+            fsStoreOrderMapper.updateFsStoreOrder(storeOrder);
+
             //删除缓存
             redisCache.deleteObject("orderKey:" + param.getOrderKey());
             //添加记录
@@ -1867,6 +2007,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);
@@ -2266,7 +2407,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()))) {
 
@@ -2378,7 +2523,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     public R syncExpress(FsStoreOrderExpressEditParam param) {
         FsStoreOrderScrm order = fsStoreOrderMapper.selectFsStoreOrderById(param.getOrderId());
         String lastFourNumber = "";
-        if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())) {
+        if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue()) || order.getDeliverySn().equals(ShipperCodeEnum.ZTO.getValue())) {
             lastFourNumber = order.getUserPhone();
             if (lastFourNumber.length() == 11) {
                 lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
@@ -2489,7 +2634,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     }
 
     @Override
-    public R updateSalseOrderMoney(String createOrderKey, BigDecimal money, BigDecimal payAmount,Integer payType) {
+    public R updateSalseOrderMoney(String createOrderKey, BigDecimal money, BigDecimal payAmount,Integer payType, Integer isPackage) {
         //货到付款自定义金额
         if (payAmount == null) {
             String configJson = configService.selectConfigByKey("store.config");
@@ -2513,8 +2658,13 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         }
         List<FsStoreCartQueryVO> carts = redisCache.getCacheObject("orderCarts:" + createOrderKey);
         BigDecimal totalMoney = BigDecimal.ZERO;
-        for (FsStoreCartQueryVO vo : carts) {
-            totalMoney = totalMoney.add(vo.getPrice().multiply(new BigDecimal(vo.getCartNum().toString())));
+        if(isPackage == 0){
+            for (FsStoreCartQueryVO vo : carts) {
+                totalMoney = totalMoney.add(vo.getPrice().multiply(new BigDecimal(vo.getCartNum().toString())));
+            }
+        } else {
+            //套餐制单
+            totalMoney = carts.get(0).getPrice();
         }
         if (money.compareTo(totalMoney) == 1) {
             throw new CustomException("价格不能大于商品总价", 501);
@@ -2782,8 +2932,8 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         userService.updateFsUser(user);
         //增加流水
         billService.addBill(user.getUserId(), BillDetailEnum.CATEGORY_2.getValue(), 1, BillDetailEnum.TYPE_5.getDesc(),
-                order.getUseIntegral().doubleValue(),
-                newIntegral.doubleValue(),
+                order.getUseIntegral(),
+                newIntegral,
                 "购买商品失败,回退积分" + order.getUseIntegral(), order.getId().toString(), 0l);
         //更新订单回退积分
         FsStoreOrderScrm storeOrder = new FsStoreOrderScrm();
@@ -2821,9 +2971,8 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         BigDecimal payIntegral = this.getOrderSumPrice(cartInfo, "payIntegral");//获取订单需要的积分
 
         //如果设置满包邮0 表示全局包邮,如果设置大于0表示满这价格包邮,否则走运费模板算法
-        if (storeFreePostage.compareTo(BigDecimal.ZERO) != 0 && totalPrice.compareTo(storeFreePostage) <= 0) {
-            storePostage = this.handlePostage(cartInfo, userAddress);
-        }
+        storePostage = this.handlePostage(cartInfo, userAddress);
+
 
         FsStoreOrderPriceDTO priceGroupDTO = new FsStoreOrderPriceDTO();
         priceGroupDTO.setStorePostage(storePostage);
@@ -2897,10 +3046,10 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     TemplateDTO templateDTO = TemplateDTO.builder()
                             .number(num)
                             .price(price)
-                            .first(shippingTemplatesRegion.getFirst().doubleValue())
-                            .firstPrice(shippingTemplatesRegion.getFirstPrice())
-                            .continues(shippingTemplatesRegion.getContinues().doubleValue())
-                            .continuePrice(shippingTemplatesRegion.getContinuePrice())
+                            .first(ObjectUtil.isNotEmpty(shippingTemplatesRegion)?shippingTemplatesRegion.getFirst().doubleValue():0.0)
+                            .firstPrice(ObjectUtil.isNotEmpty(shippingTemplatesRegion)?shippingTemplatesRegion.getFirstPrice():new BigDecimal(0.0))
+                            .continues(ObjectUtil.isNotEmpty(shippingTemplatesRegion)?shippingTemplatesRegion.getContinues().doubleValue():0.0)
+                            .continuePrice(ObjectUtil.isNotEmpty(shippingTemplatesRegion)?shippingTemplatesRegion.getContinuePrice():new BigDecimal(0.0))
                             .tempId(tempId)
                             .cityId(cityId)
                             .build();
@@ -3070,7 +3219,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                             if ((!dto.isSuccess() && dto.getReason().equals("三天无轨迹")) || (!dto.isSuccess() && dto.getReason().equals("七天内无轨迹变化"))) {
                                 //订阅物流回调
                                 String lastFourNumber = "";
-                                if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())) {
+                                if (order.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())  || order.getDeliverySn().equals(ShipperCodeEnum.ZTO.getValue())) {
                                     lastFourNumber = order.getUserPhone();
                                     if (lastFourNumber.length() == 11) {
                                         lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
@@ -3426,7 +3575,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 FsStoreScrm store = fsStoreMapper.selectFsStoreByStoreId(o.getStoreId());
                 int i = fsStoreOrderMapper.updateFsStoreOrderByOrderCode(fsStoreOrder);
                 String lastFourNumber = "";
-                if (fsStoreOrder.getDeliveryCode().equals(com.fs.his.enums.ShipperCodeEnum.SF.getValue())) {
+                if (fsStoreOrder.getDeliveryCode().equals(com.fs.his.enums.ShipperCodeEnum.SF.getValue())  || fsStoreOrder.getDeliveryCode().equals(ShipperCodeEnum.ZTO.getValue())) {
                     if (store.getSendPhone() != null) {
                         lastFourNumber = store.getSendPhone();
                     } else {
@@ -3526,7 +3675,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         fsStoreOrderLogsMapper.insertFsStoreOrderLogs(Logs);
 
         String lastFourNumber = "";
-        if (order.getDeliveryCode().equals(com.fs.his.enums.ShipperCodeEnum.SF.getValue())) {
+        if (order.getDeliveryCode().equals(com.fs.his.enums.ShipperCodeEnum.SF.getValue())  || order.getDeliveryCode().equals(ShipperCodeEnum.ZTO.getValue())) {
             lastFourNumber = order.getUserPhone();
             if (lastFourNumber.length() == 11) {
                 lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
@@ -3854,7 +4003,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                     FsStoreOrderScrm order = fsStoreOrderMapper.selectFsStoreOrderByOrderCode(dto.getOrderNumber());
                     //订阅物流回调
                     String lastFourNumber = "";
-                    if (dto.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())) {
+                    if (dto.getDeliverySn().equals(ShipperCodeEnum.SF.getValue())  || order.getDeliverySn().equals(ShipperCodeEnum.ZTO.getValue())) {
                         lastFourNumber = order.getUserPhone();
                         if (lastFourNumber.length() == 11) {
                             lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
@@ -3944,6 +4093,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) );
                 }
@@ -3968,6 +4128,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 storePayment.setUserId(user.getUserId());
                 storePayment.setBusinessOrderId(order.getId().toString());
                 storePayment.setOrderId(order.getId());
+                storePayment.setAppId(fsPayConfig.getAppId() == null ? "" : fsPayConfig.getAppId());
                 fsStorePaymentMapper.insertFsStorePayment(storePayment);
 
                 if (fsPayConfig.getType().equals("hf")){
@@ -4063,6 +4224,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 user.setStatus(1);
                 user.setMaOpenId(session.getOpenid());
                 user.setUnionId(session.getUnionid());
+                user.setAppId(properties.getConfigs().get(0).getAppid());
                 user.setIsWeixinAuth(0);
                 user.setLastIp(ip);
                 user.setCreateTime(new Date());
@@ -4181,6 +4343,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                 user.setStatus(1);
                 user.setMaOpenId(session.getOpenid());
                 user.setUnionId(session.getUnionid());
+                user.setAppId(properties.getConfigs().get(0).getAppid());
                 user.setIsWeixinAuth(0);
                 user.setLastIp(ip);
                 user.setCreateTime(new Date());
@@ -4440,6 +4603,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) );
                 }
@@ -4710,6 +4884,308 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         return null;
     }
 
+    @Override
+    public List<FsStoreOrderDeliveryNoteExportVO> getDeliveryNote(FsStoreOrderParam param) {
+        return fsStoreOrderMapper.getDeliveryNote(param);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R importDeliveryNoteExpress(List<FsOrderDeliveryNoteDTO> dtoList, String appId) {
+        try {
+            StringBuilder builder = new StringBuilder();
+            //获取商城配置
+            String json = configService.selectConfigByKey("store.config");
+            StoreConfig config = JSONUtil.toBean(json, StoreConfig.class);
+
+            List<FsOrderDeliveryNoteDTO> successList = new ArrayList<>(dtoList.size());
+            //提前获取所有必要数据
+            Map<String, String> expressDeliveryMap = buildExpressDeliveryMap();
+            //提取所有有效订单号
+            List<String> orderCodeList = new ArrayList<>(dtoList.size());
+            for (int i = 0; i < dtoList.size(); i++) {
+                FsOrderDeliveryNoteDTO dto = dtoList.get(i);
+                if (StringUtils.isEmpty(dto.getOrderNumber())) {
+                    builder.append("数据第").append(i + 2).append("行系统订单为空!").append(System.lineSeparator());
+                }
+                if (StringUtils.isEmpty(dto.getLogisticsCompany())) {
+                    builder.append("数据第").append(i + 2).append("行物流公司为空!").append(System.lineSeparator());
+                }
+                if (StringUtils.isEmpty(dto.getDeliveryId())) {
+                    builder.append("数据第").append(i + 2).append("行快递单号为空!").append(System.lineSeparator());
+                } else {
+                    //处理订单ID信息
+                    String originalOrderNumber = dto.getOrderNumber();
+                    String processedOrderNumber = extractNumbers(originalOrderNumber);
+                    dto.setOrderNumber(processedOrderNumber);
+                    orderCodeList.add(dto.getOrderNumber());
+                }
+            }
+
+            //批量查询订单信息
+            if (orderCodeList.isEmpty()) {
+                return R.ok(builder.toString());
+            }
+            List<FsStoreOrderCodeOpenIdVo> orderCodeOpenIdVoList = fsStoreOrderMapper.selectOrderCodeOpenIdInOrderCode(orderCodeList);
+            Map<String, OrderOpenIdTransDTO> orderMap = new HashMap<>(orderCodeOpenIdVoList.size());
+            Map<String, List<FsStoreOrderCodeOpenIdVo>> orderDetailsMap = new HashMap<>(orderCodeOpenIdVoList.size());
+
+            for (FsStoreOrderCodeOpenIdVo vo : orderCodeOpenIdVoList) {
+                orderMap.computeIfAbsent(vo.getId(), k -> {
+                    OrderOpenIdTransDTO dto = new OrderOpenIdTransDTO();
+                    dto.setOpenId(vo.getOpenId());
+                    dto.setTransactionId(vo.getOutTransId());
+                    return dto;
+                });
+
+                orderDetailsMap
+                        .computeIfAbsent(vo.getId(), k -> new ArrayList<>())
+                        .add(vo);
+            }
+            final WxMaService wxService = WxMaConfiguration.getMaService(appId);
+            String uploadTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"))
+                    .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+
+            for (int i = 0; i < dtoList.size(); i++) {
+                FsOrderDeliveryNoteDTO dto = dtoList.get(i);
+                int rowNum = i + 2;
+                if (StringUtils.isEmpty(dto.getOrderNumber())) {
+                    continue;
+                }
+                if (StringUtils.isEmpty(dto.getDeliveryId())) {
+                    builder.append("数据第").append(rowNum).append("行快递单号为空!")
+                            .append(System.lineSeparator());
+                    continue;
+                }
+
+                if (StringUtils.isEmpty(dto.getLogisticsCompany())) {
+                    builder.append("数据第").append(rowNum).append("行快递公司编号为空!")
+                            .append(System.lineSeparator());
+                    continue;
+                }
+//                if (dto.getDeliveryStatus() == null) {
+//                    dto.setDeliveryStatus(0);
+//                }
+//                if (ObjectUtil.isNotNull(dto.getDeliveryTime())) {
+//                    dto.setDeliveryTime(parseCstToDateOnlyString(dto.getDeliveryTime()));
+//                }
+//
+//                if (ObjectUtil.isNotNull(dto.getDeliveryPayTime()) &&
+//                        !dto.getDeliveryPayTime().isEmpty()) {
+//                    dto.setDeliveryPayTime(parseCstToDateOnlyString(dto.getDeliveryPayTime()));
+//                }
+                // 验证快递公司
+                String deliverySn = expressDeliveryMap.get(dto.getLogisticsCompany());
+                if (deliverySn == null) {
+                    builder.append("数据第").append(rowNum).append("行订单号为")
+                            .append(dto.getOrderNumber()).append("物流公司名称异常")
+                            .append(System.lineSeparator());
+                    continue;
+                }
+                dto.setDeliverySn(deliverySn);
+
+                // 检查订单是否存在
+                String orderNumber = dto.getOrderNumber();
+                OrderOpenIdTransDTO orderInfo = orderMap.get(orderNumber);
+                if (orderInfo == null) {
+                    builder.append("数据第").append(rowNum).append("行订单号")
+                            .append(orderNumber).append("不存在").append(System.lineSeparator());
+                    continue;
+                }
+                //验证是否开启微信发货
+                if (config.getIsWeChatShipping() != null && config.getIsWeChatShipping()) {
+                    // 上传物流信息到微信
+                    List<FsStoreOrderCodeOpenIdVo> orderDetails = orderDetailsMap.get(orderNumber);
+                    if (uploadShippingInfoToWechat(wxService, orderInfo, orderDetails, dto, uploadTime)) {
+                        successList.add(dto);
+                    } else {
+                        builder.append("数据第").append(rowNum).append("行订单号为")
+                                .append(orderNumber).append("上传微信失败").append(System.lineSeparator());
+                    }
+                } else {
+                    successList.add(dto);
+                }
+            }
+
+            //批量更新数据
+            if (!successList.isEmpty()) {
+                batchUpdateDeliveryNotes(successList);
+            }
+
+            return R.ok(builder.toString().equals("") ? "操作成功!" : builder.toString());
+        } catch (Exception e) {
+            log.error("导入发货单快递信息失败", e);
+            return R.error("导入失败:" + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
+    public void refreshOrderSettlementStatus() {
+        try {
+            // 判断是否对接微信发货
+            String json = configService.selectConfigByKey("store.config");
+            StoreConfig config = JSONUtil.toBean(json, StoreConfig.class);
+            logger.info("进入微信结算订单定时任务--------------->{}", "start");
+            if (config != null && config.getIsWeChatShipping() != null && config.getIsWeChatShipping()) {
+                // 获取未结算订单
+                List<FsStoreOrderScrm> orderScrmList = fsStoreOrderMapper.getUnsettledOrder();
+
+//                String payConfig = configService.selectConfigByKey("store.pay");
+                String payConfig = configService.selectConfigByKey("his.pay");
+                JSONObject js = JSON.parseObject(payConfig);
+                String appId = js.getString("appId");
+
+                if (ObjectUtil.isNotNull(appId) && !appId.isEmpty()) {
+                    final WxMaService wxService = WxMaConfiguration.getMaService(appId);
+
+                    if (!orderScrmList.isEmpty()) {
+                        for (FsStoreOrderScrm order : orderScrmList) {
+                            WxMaOrderShippingInfoGetRequest request = new WxMaOrderShippingInfoGetRequest();
+                            request.setTransactionId(order.getBankTransactionId());
+                            WxMaOrderShippingInfoGetResponse response;
+
+                            try {
+                                response = wxService.getWxMaOrderShippingService().get(request);
+
+                                if (response.getErrCode().equals(0)) {
+                                    // 订单状态枚举:(1) 待发货;(2) 已发货;(3) 确认收货;(4) 交易完成;(5) 已退款
+                                    if (response.getOrder().getOrderState().equals(3) || response.getOrder().getOrderState().equals(4)) {
+                                        if (order.getStatus() == OrderInfoEnum.STATUS_2.getValue()) {
+                                            this.finishOrder(order.getId());
+                                        }
+                                    }
+                                    logger.info("请求信息------------------------》{}", response);
+                                }
+
+                            } catch (WxErrorException e) {
+                                logger.info("异常信息------------------------》{}", e.getMessage());
+                                continue;
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.getStackTrace();
+        } finally {
+            logger.info("进入微信结算订单定时任务--------------->{}", "end");
+        }
+    }
+
+    private void batchUpdateDeliveryNotes(List<FsOrderDeliveryNoteDTO> list) {
+        int batchSize = 500;
+        int total = list.size();
+        int batches = (total + batchSize - 1) / batchSize;
+        for (int i = 0; i < batches; i++) {
+            int start = i * batchSize;
+            int end = Math.min(start + batchSize, total);
+            List<FsOrderDeliveryNoteDTO> subList = list.subList(start, end);
+            fsStoreOrderMapper.batchUpdateInOrderCode(subList);
+        }
+    }
+
+    private boolean uploadShippingInfoToWechat(WxMaService wxService,
+                                               OrderOpenIdTransDTO orderInfo,
+                                               List<FsStoreOrderCodeOpenIdVo> orderDetails,
+                                               FsOrderDeliveryNoteDTO dto,
+                                               String uploadTime) {
+        try {
+            WxMaOrderShippingInfoUploadRequest request = new WxMaOrderShippingInfoUploadRequest();
+            OrderKeyBean orderKeyBean = new OrderKeyBean();
+            orderKeyBean.setOrderNumberType(2);
+            orderKeyBean.setTransactionId(orderInfo.getTransactionId());
+            request.setOrderKey(orderKeyBean);
+            request.setDeliveryMode(1);
+            request.setLogisticsType(1);
+            List<ShippingListBean> shippingList = new ArrayList<>(orderDetails.size());
+            ShippingListBean shippingListBean = null;
+            for (FsStoreOrderCodeOpenIdVo detail : orderDetails) {
+                if (shippingListBean == null) {
+                    shippingListBean = new ShippingListBean();
+                    shippingListBean.setTrackingNo(dto.getDeliveryId());
+                    shippingListBean.setExpressCompany(dto.getDeliverySn());
+                    JSONObject js = JSON.parseObject(detail.getJsonInfo());
+                    shippingListBean.setItemDesc(js.getString("productName"));
+                    ContactBean contactBean = new ContactBean();
+                    contactBean.setReceiverContact(detail.getPhone());
+                    shippingListBean.setContact(contactBean);
+                } else {
+                    //拼接
+//                    JSONObject js = JSON.parseObject(detail.getJsonInfo());
+//                    shippingListBean.setItemDesc(shippingListBean.getItemDesc()+"-"+js.getString("productName"));
+                    break;
+                }
+            }
+            shippingList.add(shippingListBean);
+            request.setShippingList(shippingList);
+            request.setUploadTime(uploadTime);
+            // 设置支付者信息
+            PayerBean payerBean = new PayerBean();
+            payerBean.setOpenid(orderInfo.getOpenId());
+            request.setPayer(payerBean);
+
+            // 上传物流信息
+            return wxService.getWxMaOrderShippingService().upload(request).getErrCode() == 0;
+        } catch (Exception e) {
+            log.error("上传物流信息到微信失败,订单号: {}", dto.getOrderNumber(), e);
+            return false;
+        }
+    }
+
+    @Override
+    public R createPackageSalesOrder(CompanyUser companyUser, String packageId, Integer orderType, Integer orderMedium) {
+        FsStoreProductPackageScrm storeProductPackage = productPackageService.selectFsStoreProductPackageById(Long.parseLong(packageId));
+        if(storeProductPackage == null){
+            return R.error("商品套餐不存在");
+        }
+
+        /**
+         * 由于套餐包制单前端页面没有走购物车,因为为了保证后续可以使用之前商品制单的改价接口、创建订单接口、支付等接口,
+         * 也为了保持结构一致,因此这里手动添加购物车
+         */
+        // 获取套餐包的商品信息,然后添加购物车
+        JSONArray jsonArray = JSONUtil.parseArray(storeProductPackage.getProducts());
+        List<StorePackageProductDTO> goodsList = JSONUtil.toList(jsonArray, StorePackageProductDTO.class);
+        String cartIds = "";
+        for (StorePackageProductDTO goods : goodsList) {
+            FsStoreCartParam fsStoreCartParam = new FsStoreCartParam();
+            FsStoreProductAttrValueScrm attrValue = attrValueService.selectFsStoreProductAttrValueById(goods.getId());
+            if (attrValue != null && attrValue.getProductId() != null) {
+                    fsStoreCartParam.setProductId(attrValue.getProductId());
+                    fsStoreCartParam.setCartNum(goods.getCount());
+                    fsStoreCartParam.setIsBuy(1);
+                    fsStoreCartParam.setAttrValueId(goods.getId());
+                R r = fsStoreCartScrmService.addCart(companyUser.getUserId(), fsStoreCartParam);
+                Object id = r.get("id");
+                if(id != null){
+                    cartIds += id + ",";
+                }
+            }
+
+        }
+
+        List<FsStoreCartQueryVO> carts = cartMapper.selectFsStoreCartListByIds(cartIds);
+        String uuid = IdUtil.randomUUID();
+        redisCache.setCacheObject("createOrderKey:" + uuid, companyUser.getCompanyId() + "-" + companyUser.getUserId(), 24, TimeUnit.HOURS);
+
+        // 这里的carts是购物车信息,价格取的套餐包价格
+        for (FsStoreCartQueryVO vo : carts) {
+            vo.setPrice(storeProductPackage.getPayMoney());
+        }
+        redisCache.setCacheObject("orderCarts:" + uuid, carts, 24, TimeUnit.HOURS);
+        if (orderType != null || orderMedium != null) {
+            FsStoreOrderScrm fsStoreOrder = new FsStoreOrderScrm();
+            fsStoreOrder.setOrderMedium(orderMedium);
+            fsStoreOrder.setOrderType(orderType);
+            redisCache.setCacheObject("orderInfo:" + uuid, fsStoreOrder, 24, TimeUnit.HOURS);
+        }
+
+        redisCache.setCacheObject("createOrderMoney:" + uuid, storeProductPackage.getPayMoney(), 24, TimeUnit.HOURS);
+        // 根据前端要求,为了前端方便取值,所以返回的参数key与商品制单的接口保持一致
+        return R.ok().put("orderKey", uuid);
+    }
+
     private static final DateTimeFormatter CST_FORMATTER = DateTimeFormatter
             .ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US)
             .withZone(ZoneId.of("Asia/Shanghai"));
@@ -4746,9 +5222,23 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         map.put("ZTO", "中通");
         map.put("JD", "京东");
         map.put("DBL", "德邦");
+        map.put("YTO", "圆通");
         return map;
     }
 
+    //高性能截取订单ID
+    private static String extractNumbers(String str) {
+        if (str == null || str.isEmpty()) {
+            return "";
+        }
+        int start = 0;
+        int len = str.length();
+        while (start < len && !Character.isDigit(str.charAt(start))) {
+            start++;
+        }
+        return start < len ? str.substring(start) : "";
+    }
+
 
     @Override
     public FsStoreOrderAmountScrmStatsVo selectFsStoreOrderAmountScrmStats(FsStoreOrderAmountScrmStatsQueryDto queryDto) {

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

@@ -3,6 +3,7 @@ package com.fs.hisStore.service.impl;
 import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
+import java.util.Optional;
 
 import cn.hutool.json.JSONUtil;
 import com.fs.common.utils.DateUtils;
@@ -55,7 +56,11 @@ public class FsUserBillScrmServiceImpl implements IFsUserBillScrmService
     @Override
     public List<FsUserBillScrm> selectFsUserBillList(FsUserBillScrm fsUserBill)
     {
-        return fsUserBillMapper.selectFsUserBillList(fsUserBill);
+        List<FsUserBillScrm> bills=fsUserBillMapper.selectFsUserBillList(fsUserBill);
+        Optional.ofNullable( bills).ifPresent(list -> list.forEach(bill -> {
+            bill.setNumber(bill.getMoney());
+        }));
+        return bills;
     }
 
     /**
@@ -109,13 +114,14 @@ public class FsUserBillScrmServiceImpl implements IFsUserBillScrmService
     }
 
     @Override
-    public void addBill(Long uid, String cate,Integer billType,String title, double number, double balance, String renark,String busId,Long tuiUserId) {
+    public void addBill(Long uid, String cate,Integer billType,String title, BigDecimal money, BigDecimal balance, String renark,String busId,Long tuiUserId) {
         FsUserBillScrm bill=new FsUserBillScrm();
         bill.setCategory(cate);
         bill.setBillType(billType);
         bill.setUserId(uid);
         bill.setTitle(title);
-        bill.setNumber(number);
+//        bill.setNumber(number);
+        bill.setMoney(money);
         bill.setBalance(balance);
         bill.setRemark(renark);
         bill.setBusinessId(busId);

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

@@ -139,7 +139,7 @@ public class FsUserExtractScrmServiceImpl implements IFsUserExtractScrmService
         fsUserExtractMapper.insertFsUserExtract(extract);
         userMapper.updateFsUser(user);
         //插入流水
-        billService.addBill(user.getUserId(), BillDetailEnum.CATEGORY_1.getValue(),0,BillDetailEnum.TYPE_6.getDesc(),param.getExtractPrice().doubleValue(),user.getNowMoney().doubleValue(),"提现申请",extract.getId().toString(),0l);
+        billService.addBill(user.getUserId(), BillDetailEnum.CATEGORY_1.getValue(),0,BillDetailEnum.TYPE_6.getDesc(),param.getExtractPrice(),user.getNowMoney(),"提现申请",extract.getId().toString(),0l);
 
         return R.ok("提交成功,等待后台审核");
     }

+ 120 - 14
fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserScrmServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.hisStore.service.impl;
 
+import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
@@ -8,6 +9,7 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.param.LoginMaWxParam;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.DictUtils;
 import com.fs.common.utils.ParseUtils;
@@ -30,7 +32,13 @@ import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
 import com.fs.course.vo.newfs.FsCourseAnalysisVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
+import com.fs.his.domain.FsUser;
+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;
 import com.fs.hisStore.domain.*;
@@ -50,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;
@@ -245,6 +249,11 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
         return fsUserMapper.selectFsUserByPhone(phone);
     }
 
+    @Override
+    public List<FsUserScrm> selectFsUserListByPhoneExact(String phone) {
+        return fsUserMapper.selectFsUserListByPhoneExact(phone);
+    }
+
     @Override
     public TableDataInfo selectCusListPage(SelectCusListPageParam param) {
         Asserts.check(ObjectUtils.isNotNull(param.getPageNum()), "页数不能为空");
@@ -328,7 +337,7 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
                     tuiUserMap.setUserId(tuiUser.getUserId());
                     tuiUserMap.setBrokeragePrice(tuiUser.getBrokeragePrice().add(brokerage));
                     fsUserMapper.updateFsUser(tuiUserMap);
-                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(),1,BillDetailEnum.TYPE_5.getDesc(),brokerage.doubleValue(),tuiUserMap.getBrokeragePrice().doubleValue(),"订单返佣金",order.getId().toString(),order.getUserId());
+                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(),1,BillDetailEnum.TYPE_5.getDesc(),brokerage,tuiUserMap.getBrokeragePrice(),"订单返佣金",order.getId().toString(),order.getUserId());
                 }
             }
             //二级返
@@ -340,7 +349,7 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
                         tuiUserTwoMap.setUserId(tuiUserTwo.getUserId());
                         tuiUserTwoMap.setBrokeragePrice(tuiUserTwo.getBrokeragePrice().add(brokerageTwo));
                         fsUserMapper.updateFsUser(tuiUserTwoMap);
-                        billService.addBill(tuiUserTwoMap.getUserId(), CATEGORY_3.getValue(),1,BillDetailEnum.TYPE_5.getDesc(),brokerageTwo.doubleValue(),tuiUserTwoMap.getBrokeragePrice().doubleValue(),"订单返佣金",order.getId().toString(),tuiUser.getUserId());
+                        billService.addBill(tuiUserTwoMap.getUserId(), CATEGORY_3.getValue(),1,BillDetailEnum.TYPE_5.getDesc(),brokerageTwo,tuiUserTwoMap.getBrokeragePrice(),"订单返佣金",order.getId().toString(),tuiUser.getUserId());
                     }
                     //三级
                     FsUserScrm tuiUserThree= fsUserMapper.selectFsUserById(tuiUserTwo.getSpreadUserId());
@@ -350,7 +359,7 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
                             tuiUserThreeMap.setUserId(tuiUserThree.getUserId());
                             tuiUserThreeMap.setBrokeragePrice(tuiUserThree.getBrokeragePrice().add(brokerageThree));
                             fsUserMapper.updateFsUser(tuiUserThreeMap);
-                            billService.addBill(tuiUserThreeMap.getUserId(), CATEGORY_3.getValue(),1,BillDetailEnum.TYPE_5.getDesc(),brokerageThree.doubleValue(),tuiUserThreeMap.getBrokeragePrice().doubleValue(),"订单返佣金",order.getId().toString(),tuiUser.getUserId());
+                            billService.addBill(tuiUserThreeMap.getUserId(), CATEGORY_3.getValue(),1,BillDetailEnum.TYPE_5.getDesc(),brokerageThree,tuiUserThreeMap.getBrokeragePrice(),"订单返佣金",order.getId().toString(),tuiUser.getUserId());
                         }
 
                     }
@@ -359,7 +368,7 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
             }
         }
         catch (Exception e){
-
+            log.error("分佣异常",e);
         }
     }
 
@@ -377,9 +386,9 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
                 if(tuiUser!=null){
                     FsUserScrm tuiUserMap=new FsUserScrm();
                     tuiUserMap.setUserId(tuiUser.getUserId());
-                    tuiUserMap.setBrokeragePrice(tuiUser.getBrokeragePrice().subtract(new BigDecimal(bill.getNumber())));
+                    tuiUserMap.setBrokeragePrice(tuiUser.getBrokeragePrice().subtract(bill.getMoney()));
                     fsUserMapper.updateFsUser(tuiUserMap);
-                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(),0,BillDetailEnum.TYPE_6.getDesc(),bill.getNumber().doubleValue(),tuiUserMap.getBrokeragePrice().doubleValue(),"订单退佣金",order.getId().toString(),bill.getTuiUserId());
+                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(),0,BillDetailEnum.TYPE_6.getDesc(),bill.getMoney(),tuiUserMap.getBrokeragePrice(),"订单退佣金",order.getId().toString(),bill.getTuiUserId());
                 }
             }
         }
@@ -400,11 +409,11 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
                 if(tuiUser!=null){
                     FsUserScrm tuiUserMap=new FsUserScrm();
                     tuiUserMap.setUserId(tuiUser.getUserId());
-                    tuiUserMap.setNowMoney(tuiUser.getNowMoney().add(new BigDecimal(bill.getNumber())));
-                    tuiUserMap.setBrokeragePrice(tuiUser.getBrokeragePrice().subtract(new BigDecimal(bill.getNumber())));
+                    tuiUserMap.setNowMoney(tuiUser.getNowMoney().add(bill.getMoney()));
+                    tuiUserMap.setBrokeragePrice(tuiUser.getBrokeragePrice().subtract(bill.getMoney()));
                     fsUserMapper.updateFsUser(tuiUserMap);
-                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(),0,BillDetailEnum.TYPE_6.getDesc(),bill.getNumber().doubleValue(),tuiUserMap.getBrokeragePrice().doubleValue(),"订单分佣金",order.getId().toString(),bill.getTuiUserId());
-                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_1.getValue(),1,BillDetailEnum.TYPE_5.getDesc(),bill.getNumber().doubleValue(),tuiUserMap.getNowMoney().doubleValue(),"订单分佣金",order.getId().toString(),bill.getTuiUserId());
+                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(),0,BillDetailEnum.TYPE_6.getDesc(),bill.getMoney(),tuiUserMap.getBrokeragePrice(),"订单分佣金",order.getId().toString(),bill.getTuiUserId());
+                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_1.getValue(),1,BillDetailEnum.TYPE_5.getDesc(),bill.getMoney(),tuiUserMap.getNowMoney(),"订单分佣金",order.getId().toString(),bill.getTuiUserId());
                 }
             }
         }
@@ -653,6 +662,30 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
         return pageVO;
     }
 
+    @Override
+    public UserDetailsVO getUserDetails(Long userId, Long fsUserId, String dateTag, Long userCompanyId) {
+        UserDetailsVO countWatchCourse = fsUserMapper.getCountWatchCourse(userId, fsUserId, dateTag,userCompanyId);
+        FsUserCompanyUserScrm fsUserCompanyUser = userCompanyUserService.selectFsUserCompanyUserById(userCompanyId);
+
+        UserDetailsVO countAnswer = fsUserMapper.getCountAnswer(fsUserCompanyUser.getCompanyUserId(), fsUserId, dateTag);
+        UserDetailsVO countRedPacket = fsUserMapper.getCountRedPacket(fsUserCompanyUser.getCompanyUserId(), fsUserId, dateTag);
+        UserDetailsVO vo = new UserDetailsVO();
+        if (countWatchCourse != null){
+            BeanUtils.copyProperties(countWatchCourse, vo);
+        }
+        if (countRedPacket != null) {
+            vo.setAnswerRedPacketTime(countRedPacket.getAnswerRedPacketTime());
+            vo.setAnswerRedPacketAmount(countRedPacket.getAnswerRedPacketAmount());
+        } else {
+            vo.setAnswerRedPacketAmount(BigDecimal.ZERO);
+        }
+        if (countAnswer != null) {
+            vo.setAnswerTime(countAnswer.getAnswerTime());
+            vo.setAnswerRightTime(countAnswer.getAnswerRightTime());
+        }
+        return vo;
+    }
+
     @Override
     public Integer selectFsUserByUserIds(String[] userIds, Long companyUserId) {
         return fsUserMapper.selectFsUserByUserIds(userIds, companyUserId);
@@ -970,6 +1003,7 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
         // 逻辑调整:会员与销售的绑定关系通过中间表关联 /20250625 17:13
         FsUserCompanyUserScrm userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(fsUser.getUserId(), param.getProjectId());
         if (Objects.nonNull(userCompanyUser) && !userCompanyUser.getCompanyUserId().equals(param.getCompanyUserId())){
+            log.error("进入::FsUserCompanyUserScrm 该用户fsUser={},companyUser={},param={}",fsUser,userCompanyUser,param);
             return ResponseResult.fail(500,"该用户("+fsUser.getUserId() + ")已成为其他销售会员");
         }
 
@@ -1049,6 +1083,46 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
         return fsUserMapper.selectFsUserByOpenId(openid);
     }
 
+    @Override
+    @Transactional
+    public void addTuiLiveMoney(LiveOrder order, List<FsStoreProductAttrValueScrm> productAttrValues) {
+        BigDecimal brokerage=BigDecimal.ZERO;
+        //返佣金
+        FsUserScrm user= fsUserMapper.selectFsUserById(order.getCompanyUserId());
+        for (FsStoreProductAttrValueScrm productAttrValue : productAttrValues) {
+            brokerage=brokerage.add(productAttrValue.getBrokerage().multiply(new BigDecimal(order.getTotalNum())));
+        }
+        if(brokerage.compareTo(BigDecimal.ZERO)==1){
+            FsUserScrm tuiUserMap=new FsUserScrm();
+            tuiUserMap.setUserId(user.getUserId());
+            tuiUserMap.setBrokeragePrice(user.getBrokeragePrice().add(brokerage));
+            fsUserMapper.updateFsUser(tuiUserMap);
+            billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(),1,BillDetailEnum.TYPE_5.getDesc(),brokerage,tuiUserMap.getBrokeragePrice(),"订单返佣金",order.getOrderId().toString(),order.getCompanyUserId());
+        }
+    }
+
+    @Override
+    @Transactional
+    public void subLiveTuiMoney(LiveOrder liveOrder) {
+        FsUserBillScrm map=new FsUserBillScrm();
+        map.setCategory(CATEGORY_3.getValue());
+        map.setBusinessId(liveOrder.getOrderId().toString());
+        map.setBillType(1);
+        List<FsUserBillScrm> list=billService.selectFsUserBillList(map);
+        if(list!=null&&list.size()>0){
+            for(FsUserBillScrm bill:list){
+                FsUserScrm tuiUser= fsUserMapper.selectFsUserByIdForUpdate(bill.getUserId());
+                if(tuiUser!=null){
+                    FsUserScrm tuiUserMap=new FsUserScrm();
+                    tuiUserMap.setUserId(tuiUser.getUserId());
+                    tuiUserMap.setBrokeragePrice(tuiUser.getBrokeragePrice().subtract(bill.getNumber()));
+                    fsUserMapper.updateFsUser(tuiUserMap);
+                    billService.addBill(tuiUserMap.getUserId(), CATEGORY_3.getValue(),0,BillDetailEnum.TYPE_6.getDesc(),bill.getNumber(),tuiUserMap.getBrokeragePrice(),"订单退佣金",liveOrder.getOrderId().toString(),bill.getTuiUserId());
+                }
+            }
+        }
+    }
+
     /**
      * 查询会员选项列表
      * @param params    参数
@@ -1058,4 +1132,36 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
     public List<OptionsVO> selectUserListByMap(Map<String, Object> params) {
         return fsUserMapper.selectUserListByMap(params);
     }
+
+
+    @Autowired
+    private IFsUserWxService fsUserWxService;
+
+    @Override
+    public void handleFsUserWx(FsUserScrm user, LoginMaWxParam param, WxMaJscode2SessionResult session) {
+        try {
+            FsUserWx fsUserWx = new FsUserWx();
+            fsUserWx.setType(1);
+            fsUserWx.setFsUserId(user.getUserId());
+            fsUserWx.setAppId(param.getAppId());
+            fsUserWx.setOpenId(session.getOpenid());
+            fsUserWx.setUnionId(session.getUnionid() != null ? session.getUnionid() : null);
+            fsUserWx.setCreateTime(new Date());
+            fsUserWx.setUpdateTime(new Date());
+            fsUserWxService.saveOrUpdateByUniqueKey(fsUserWx);
+            log.info("zyp \n 【更新或插入用户与小程序{}的绑定关系】:{}", param.getAppId(), user.getUserId());
+        } catch (Exception e) {
+            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);
+    }
 }

+ 38 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderCodeOpenIdVo.java

@@ -0,0 +1,38 @@
+package com.fs.hisStore.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class FsStoreOrderCodeOpenIdVo implements Serializable {
+//    /**
+//     * 订单编码
+//     * **/
+//    private String orderCode;
+
+    /**
+     * 订单id
+     * **/
+    private String id;
+
+    /**
+     * 用户openId
+     * **/
+    private String openId;
+
+    /**
+     * 用户手机号
+     * **/
+    private String phone;
+
+    /**
+     * 商品详情信息
+     * **/
+    private String jsonInfo;
+
+    /**
+     * 交易单号
+     * **/
+    private String outTransId;
+}

+ 84 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderDeliveryNoteExportVO.java

@@ -0,0 +1,84 @@
+package com.fs.hisStore.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 发货单导出(商城订单)
+ * **/
+@Data
+public class FsStoreOrderDeliveryNoteExportVO implements Serializable {
+    //系统订单号
+    @Excel(name = "原始单号",width = 20,sort = 1)
+    private String orderNumber;
+
+    @Excel(name = "收件人",width = 10,sort = 2)
+    private String recipient;
+
+    @Excel(name = "收件人手机",width = 20,sort = 3)
+    private String recipientPhone;
+
+    @Excel(name = "收件人电话",width = 20,sort = 4)
+    private String recipientTelephone;
+
+    //具体到某个街道和小区
+    @Excel(name = "收件人详细地址",width = 30,sort = 5)
+    private String recipientAddress;
+
+//    //省市区
+//    @Excel(name = "收货地址",width = 20,sort = 6)
+//    private String receivingAddress;
+
+    //编号和数量:662551*2
+    @Excel(name = "组合编号及数量",width = 20,sort = 7)
+    private String number;
+
+    //名称和数量:商品名称*2
+    @Excel(name = "组合名称及数量",width = 20,sort = 8)
+    private String nameAndNumber;
+
+    @Excel(name = "代收金额",width = 10,sort = 9)
+    private BigDecimal collectionAmount;
+
+    @Excel(name = "物流公司",width = 10,sort = 10)
+    private String logisticsCompany;
+
+    @Excel(name = "物流产品",width = 10,sort = 11)
+    private String logisticsProduct;
+
+    @Excel(name = "物流付款方式",width = 15,sort = 12)
+    private String logisticsPayMethod;
+
+    @Excel(name = "包裹数",width = 10,sort = 13)
+    private Long packageNum;
+
+    @Excel(name = "寄件人",width = 10,sort = 14)
+    private String sender;
+
+    @Excel(name = "寄件人手机",width = 20,sort = 15)
+    private String senderPhone;
+
+    @Excel(name = "寄件人电话",width = 20,sort = 16)
+    private String senderTelephone;
+
+    @Excel(name = "寄件公司",width = 10,sort = 17)
+    private String senderCompany;
+
+    //具体到某个街道和小区
+    @Excel(name = "寄件人详细地址",width = 30,sort = 18)
+    private String senderAddress;
+
+    @Excel(name = "出库仓库",width = 10,sort = 19)
+    private String outboundWarehouse;
+
+    @Excel(name = "订单付款方式",width = 10,sort = 20)
+    private String payMethod;
+
+    @Excel(name = "订单备注",width = 20,sort = 21)
+    private String orderNotes;
+
+    private String keyword;
+}

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

@@ -60,6 +60,8 @@ public class FsStoreProductListQueryVO implements Serializable
     private String storeIds;
 
     private Long storeId;
+    private Long companyId;
+    private Long companyUserId;
 
 
 

+ 11 - 0
fs-service/src/main/java/com/fs/hisStore/vo/UserScrmVo.java

@@ -0,0 +1,11 @@
+package com.fs.hisStore.vo;
+
+import lombok.Data;
+
+@Data
+public class UserScrmVo {
+    Long id;
+    String name;
+    String position;
+    String certificateCode;
+}

+ 5 - 0
fs-service/src/main/java/com/fs/im/service/OpenIMService.java

@@ -84,4 +84,9 @@ public interface OpenIMService {
 
     OpenImResponseDTO sendInquiryUtil(String sendID, String recvID, Integer contentType, String payloadData,String inquiryName,String type,Long companyId,Long companyUserId,String doctorId);
 
+
+    OpenImResponseDTO sendUserInformation(Long userId,Long doctorId,Long userInformationId);
+
+    OpenImResponseDTO doctorSendMsgToUser(Long userId,Long doctorId);
+
 }

+ 54 - 2
fs-service/src/main/java/com/fs/im/service/impl/OpenIMServiceImpl.java

@@ -1099,7 +1099,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         FsUserCourse fsUserCourse = fsUserCourseMapper.selectFsUserCourseByCourseId(batchSendCourseDTO.getCourseId());
         Long project = fsUserCourse != null ? fsUserCourse.getProject() : null;
         long planSendTimeStamp;
-        if(batchSendCourseDTO.getSendType() == 1 && batchSendCourseDTO.getSendTime() != null && batchSendCourseDTO.getSendTime().compareTo(new Date()) > 0){
+        if(Objects.nonNull(batchSendCourseDTO.getSendType()) &&batchSendCourseDTO.getSendType() == 1 && batchSendCourseDTO.getSendTime() != null && batchSendCourseDTO.getSendTime().compareTo(new Date()) > 0){
             planSendTimeStamp = batchSendCourseDTO.getSendTime().getTime();
         } else {
             planSendTimeStamp = System.currentTimeMillis();
@@ -1109,7 +1109,7 @@ public class OpenIMServiceImpl implements OpenIMService {
         OpenImBatchMsgDTO openImBatchMsgDTO = makeOpenImBatchMsgDTO(batchSendCourseDTO, courseUrl, objectMapper, userIds, planSendTimeStamp, "发课");
 
         int sendType;
-        if(batchSendCourseDTO.getSendType() == 1 && batchSendCourseDTO.getSendTime() != null && batchSendCourseDTO.getSendTime().compareTo(new Date()) > 0) {
+        if(Objects.nonNull(batchSendCourseDTO.getSendType())&&batchSendCourseDTO.getSendType() == 1 && batchSendCourseDTO.getSendTime() != null && batchSendCourseDTO.getSendTime().compareTo(new Date()) > 0) {
             sendType = 1; //定时
         } else {
             sendType = 2; //实时
@@ -1575,4 +1575,56 @@ public class OpenIMServiceImpl implements OpenIMService {
             return null;
         }
     }
+
+    //发送用户信息采集数据
+    @Override
+    public OpenImResponseDTO sendUserInformation(Long userId,Long doctorId,Long userInformationId) {
+        try {
+            accountCheck("U"+userId,"1");
+        OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+        ObjectMapper objectMapper = new ObjectMapper();
+        openImMsgDTO.setSendID("U"+userId);
+        openImMsgDTO.setRecvID("D"+doctorId);
+        //110为im的自定义消息类型
+        openImMsgDTO.setContentType(110);
+        openImMsgDTO.setSenderPlatformID(5);
+        openImMsgDTO.setSessionType(1);
+        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+        //content.setContent(ext);
+        PayloadDTO payload = new PayloadDTO();
+        payload.setDescription("userInformation");
+        payload.setData("userInformation");
+        PayloadDTO.Extension extension = new PayloadDTO.Extension();
+
+        extension.setDoctorId(doctorId);
+        extension.setUserInformationId(userInformationId);
+        payload.setExtension(extension);
+        OpenImMsgDTO.ImData imData = new OpenImMsgDTO.ImData();
+
+        imData.setPayload(payload);
+
+        String imJson = objectMapper.writeValueAsString(imData);
+        content.setData(imJson);
+        openImMsgDTO.setContent(content);
+        return openIMSendMsg(openImMsgDTO);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    @Override
+    public OpenImResponseDTO doctorSendMsgToUser(Long userId, Long doctorId) {
+        OpenImMsgDTO openImMsgDTO = new OpenImMsgDTO();
+        ObjectMapper objectMapper = new ObjectMapper();
+        openImMsgDTO.setRecvID("U"+userId);
+        openImMsgDTO.setSendID("D"+doctorId);
+        openImMsgDTO.setContentType(101);
+        openImMsgDTO.setSenderPlatformID(5);
+        openImMsgDTO.setSessionType(1);
+        OpenImMsgDTO.Content content = new OpenImMsgDTO.Content();
+        content.setContent("您的用户信息已确认");
+        openImMsgDTO.setContent(content);
+        return openIMSendMsg(openImMsgDTO);
+    }
 }

+ 26 - 4
fs-service/src/main/java/com/fs/ipad/IpadSendUtils.java

@@ -3,6 +3,7 @@ package com.fs.ipad;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisCacheT;
 import com.fs.common.exception.base.BaseException;
@@ -17,6 +18,7 @@ import com.fs.qw.domain.QwGroupChat;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwGroupChatMapper;
+import com.fs.qw.mapper.QwUserMapper;
 import com.fs.sop.domain.QwSop;
 import com.fs.sop.domain.SopUserLogs;
 import com.fs.sop.mapper.QwSopMapper;
@@ -53,8 +55,10 @@ public class IpadSendUtils {
     private final QwSopMapper qwSopMapper;
     private final QwExternalContactMapper qwExternalContactMapper;
     private final SopUserLogsMapper sopUserLogsMapper;
+    private final QwUserMapper qwUserMapper;
     private final RedisCache redisCacheUrl;
     private final String FILE_KEY = "ipad:upload:";
+    private final String USER_KEY = "ipad:user:";
 
     /**
      * 发送卡片消息
@@ -241,11 +245,22 @@ public class IpadSendUtils {
      * @return 返回的userid
      */
     private Long chatIds(BaseVo vo){
+        // 实时查询群聊信息
+        QwGroupChat qwGroupChat = qwGroupChatMapper.selectQwGroupChatByChatId(vo.getExId());
+        if(qwGroupChat == null){
+            throw new BaseException("未找到群聊,检查SOP任务数据");
+        }
+        if(qwGroupChat.getRoomid() != null){
+            return qwGroupChat.getRoomid();
+        }
+        // 找到对应的企业微信
+        QwUser qwUser = qwUserMapper.selectQwUserById(vo.getQwUserId());
         WxWorkChatIdToRoomIdDTO tdo = new WxWorkChatIdToRoomIdDTO();
-        tdo.setChatid(vo.getExId());
-        tdo.setCorpid(vo.getCorpId());
-        tdo.setUuid(vo.getUuid());
-        WxWorkResponseDTO<WxWorkChatIdToRoomIdResp> result = wxWorkService.ChatIdToRoomId(tdo, vo.getServerId());
+        // 重新组装数据
+        tdo.setChatid(qwGroupChat.getChatId());
+//        tdo.setCorpid(vo.getCorpId());
+        tdo.setUuid(qwUser.getUid());
+        WxWorkResponseDTO<WxWorkChatIdToRoomIdResp> result = wxWorkService.ChatIdToRoomId(tdo, qwUser.getServerId());
         if(result.getErrcode() != 0){
             throw new BaseException(result.getErrmsg());
         }
@@ -254,6 +269,13 @@ public class IpadSendUtils {
             log.error("未找到群聊数据,请求数据:{},返回数据:{}", JSON.toJSONString(tdo), JSON.toJSONString(result));
             throw new BaseException("未找到群聊:" + vo.getId());
         }
+        try {
+            qwGroupChat.setRoomid(data.getRoom_id());
+            qwGroupChatMapper.updateQwGroupChat(qwGroupChat);
+            redisCache.setCacheObject(USER_KEY + vo.getExId(), data.getRoom_id().toString());
+        }catch (Exception e){
+            log.error("存储群ID失败", e);
+        }
         return data.getRoom_id();
     }
 

+ 2 - 1
fs-service/src/main/java/com/fs/ipad/vo/BaseVo.java

@@ -15,7 +15,7 @@ public class BaseVo{
     private String corpId;
     private String corpCode;
     private boolean isRoom;
-
+    private Long qwUserId;
 
     public void setBase(BaseVo vo){
         this.uuid = vo.getUuid();
@@ -24,5 +24,6 @@ public class BaseVo{
         this.corpCode = vo.getCorpCode();
         this.exId = vo.getExId();
         this.isRoom = vo.isRoom();
+        this.qwUserId = vo.qwUserId;
     }
 }

+ 16 - 1
fs-service/src/main/java/com/fs/live/domain/LiveOrder.java

@@ -1,5 +1,6 @@
 package com.fs.live.domain;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
@@ -335,6 +336,20 @@ public class LiveOrder extends BaseEntity {
 
     /** 优惠券金额 */
     private BigDecimal couponPrice;
-
+    // 卓美新增筛选条件
+    private String productName;
+    private String productSpec;
+    private String productNum;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTimeStart;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTimeEnd;
+
+    private Integer price;
+    private Integer cost;
+    @TableField(exist = false)
+    private String bankTransactionId;
+    @TableField(exist = false)
+    private Long attrValueId;
 
 }

+ 2 - 1
fs-service/src/main/java/com/fs/live/domain/LiveOrderPayment.java

@@ -116,5 +116,6 @@ public class LiveOrderPayment extends BaseEntity{
     @Excel(name = "是否分账")
     private Integer isShare;
 
-
+    //小程序appId(用于多汇付支付/退款)
+    private String appId;
 }

+ 85 - 0
fs-service/src/main/java/com/fs/live/dto/LiveOrderCustomerExportDTO.java

@@ -0,0 +1,85 @@
+package com.fs.live.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 直播订单客户导出DTO
+ * 
+ * @author fs
+ * @date 2025-11-13
+ */
+@Data
+public class LiveOrderCustomerExportDTO {
+
+    /** 销售id */
+    @Excel(name = "销售ID")
+    private Long companyUserId;
+
+    /** 绑定销售昵称 */
+    @Excel(name = "销售昵称")
+    private String companyUserNickName;
+
+    /** 客户编码 */
+    @Excel(name = "客户编码")
+    private String customerCode;
+
+    /** 会员等级 */
+    @Excel(name = "会员等级")
+    private Integer userLevel;
+
+    /** 销售绑定手机号 */
+    @Excel(name = "销售手机号")
+    private String companyUserPhone;
+
+    /** 销售创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "销售创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date companyUserCreateTime;
+
+    /** 客户id */
+    @Excel(name = "客户ID")
+    private Long userId;
+
+    /** 客户昵称 */
+    @Excel(name = "客户昵称")
+    private String nickName;
+
+    /** 客户绑定手机号 */
+    @Excel(name = "客户手机号")
+    private String userPhone;
+
+    /** 收货手机号 */
+    @Excel(name = "收货手机号")
+    private String receivePhone;
+
+    /** 累计成交笔数 */
+    @Excel(name = "累计成交笔数")
+    private Long totalOrderCount;
+
+    /** 累计成交总额 */
+    @Excel(name = "累计成交总额", cellType = Excel.ColumnType.NUMERIC)
+    private BigDecimal totalOrderAmount;
+
+    /** 最新绑定时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "最新绑定时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date latestBindTime;
+
+    /** 客户状态 */
+    @Excel(name = "客户状态", dictType = "user_status")
+    private Integer userStatus;
+
+    /** 所属店铺 */
+    @Excel(name = "所属店铺ID")
+    private Long storeId;
+
+    /** 所属店铺名称 */
+    @Excel(name = "所属店铺名称")
+    private String storeName;
+}
+

+ 4 - 1
fs-service/src/main/java/com/fs/live/dto/LiveOrderDeliveryNoteDTO.java

@@ -16,9 +16,12 @@ public class LiveOrderDeliveryNoteDTO {
     @Excel(name = "系统订单号(必填)",width = 20,sort = 1)
     private String orderNumber;
 
-    @Excel(name = "物流公司编号(必填)(SF:顺丰、EMS:邮政、ZTO:中通、JD:京东、DBL:德邦)",width = 30,sort = 2)
+//    @Excel(name = "物流公司编号(必填)(SF:顺丰、EMS:邮政、ZTO:中通、JD:京东、DBL:德邦)",width = 30,sort = 2)
     private String deliverySn;
 
+    @Excel(name = "物流公司(必填)(SF:顺丰、EMS:邮政、ZTO:中通、JD:京东、DBL:德邦)",width = 10,sort = 10)
+    private String logisticsCompany;
+
     private String deliveryName;
 
     @Excel(name = "快递单号(必填)",width = 20,sort = 3)

+ 7 - 4
fs-service/src/main/java/com/fs/live/mapper/LiveAfterSalesMapper.java

@@ -110,7 +110,7 @@ public interface LiveAfterSalesMapper {
     Map<Long, LiveAfterSales> findByOrderIds(@Param("orderIdList") List<Long> orderIdList);
 
     @Select({"<script> " +
-            "select *  from live_after_sales  " +
+            "select lo.order_code,las.*  from live_after_sales las left join live_order lo on lo.order_id = las.order_id " +
             "where 1=1 " +
             "<if test = 'maps.status != null and maps.status ==1   '> " +
             "and sales_status = 0 " +
@@ -119,15 +119,18 @@ public interface LiveAfterSalesMapper {
             "and sales_status = 3 " +
             "</if>" +
             "<if test = 'maps.userId != null    '> " +
-            "and user_id = #{maps.userId} " +
+            "and las.user_id = #{maps.userId} " +
             "</if>" +
-            "order by create_time desc "+
+            "order by las.create_time desc "+
             "</script>"})
-    List<LiveAfterSalesQueryVO> selectLiveAfterSalesListQuery(LiveAfterSalesQueryParam param);
+    List<LiveAfterSalesQueryVO> selectLiveAfterSalesListQuery(@Param("maps") LiveAfterSalesQueryParam param);
 
     @Select({"<script> " +
             "select ifnull(count(1),0) from live_after_sales   " +
             "where user_id=#{userId}  and sales_status =#{salesStatus} " +
             "</script>"})
     Integer selectLiveAfterSalesCount(@Param("userId")long userId,@Param("salesStatus") int salesStatus);
+
+    @Select(" select  * from  live_after_sales where order_id = #{orderId} and sales_status = 0 ")
+    LiveAfterSales getLiveAfterSalesByOrderId(@Param("orderId") Long orderId);
 }

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff