Explorar el Código

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

15376779826 hace 1 semana
padre
commit
dc7885e57e
Se han modificado 98 ficheros con 4680 adiciones y 265 borrados
  1. 13 0
      fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  2. 108 0
      fs-admin/src/main/java/com/fs/his/controller/FsQuestionAndAnswerController.java
  3. 4 0
      fs-admin/src/main/java/com/fs/his/controller/FsUserController.java
  4. 12 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java
  5. 6 0
      fs-admin/src/main/java/com/fs/hisStore/task/ExpressTask.java
  6. 27 2
      fs-admin/src/main/java/com/fs/live/controller/LiveOrderController.java
  7. 24 5
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java
  8. 12 0
      fs-company/src/main/java/com/fs/company/controller/live/LiveController.java
  9. 139 0
      fs-company/src/main/java/com/fs/company/controller/store/FsUserInformationCollectionController.java
  10. 120 0
      fs-company/src/main/java/com/fs/company/controller/store/FsUserInformationCollectionScheduleController.java
  11. 6 0
      fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java
  12. 22 0
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  13. 49 0
      fs-service/src/main/java/com/fs/course/domain/FsProjectAddressConfig.java
  14. 1 1
      fs-service/src/main/java/com/fs/course/domain/FsUserCoursePeriod.java
  15. 9 9
      fs-service/src/main/java/com/fs/course/dto/FsOrderDeliveryNoteDTO.java
  16. 92 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  17. 14 0
      fs-service/src/main/java/com/fs/course/mapper/FsProjectAddressConfigMapper.java
  18. 8 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java
  19. 12 0
      fs-service/src/main/java/com/fs/course/service/IFsProjectAddressConfigService.java
  20. 1 1
      fs-service/src/main/java/com/fs/course/service/IFsUserCoursePeriodService.java
  21. 23 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  22. 23 0
      fs-service/src/main/java/com/fs/course/service/impl/FsProjectAddressConfigServiceImpl.java
  23. 24 1
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
  24. 96 95
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  25. 1 1
      fs-service/src/main/java/com/fs/gtPush/service/impl/uniPush2ServiceImpl.java
  26. 42 0
      fs-service/src/main/java/com/fs/his/domain/DoctorMsg.java
  27. 47 0
      fs-service/src/main/java/com/fs/his/domain/FsDoctorConfirm.java
  28. 28 0
      fs-service/src/main/java/com/fs/his/domain/FsQuestionAndAnswer.java
  29. 15 0
      fs-service/src/main/java/com/fs/his/domain/FsUser.java
  30. 96 0
      fs-service/src/main/java/com/fs/his/domain/FsUserInformationCollection.java
  31. 71 0
      fs-service/src/main/java/com/fs/his/domain/FsUserInformationCollectionSchedule.java
  32. 79 0
      fs-service/src/main/java/com/fs/his/enums/PrescriptionTaskStepEnum.java
  33. 75 0
      fs-service/src/main/java/com/fs/his/mapper/DoctorMsgMapper.java
  34. 62 0
      fs-service/src/main/java/com/fs/his/mapper/FsDoctorConfirmMapper.java
  35. 67 0
      fs-service/src/main/java/com/fs/his/mapper/FsQuestionAndAnswerMapper.java
  36. 1 1
      fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderMapper.java
  37. 95 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserInformationCollectionMapper.java
  38. 69 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserInformationCollectionScheduleMapper.java
  39. 13 0
      fs-service/src/main/java/com/fs/his/param/CollectionInfoConfirmParam.java
  40. 16 0
      fs-service/src/main/java/com/fs/his/param/FsUserInformationCollectionListDParam.java
  41. 48 0
      fs-service/src/main/java/com/fs/his/param/FsUserInformationCollectionParam.java
  42. 64 0
      fs-service/src/main/java/com/fs/his/service/IFsDoctorConfirmService.java
  43. 66 0
      fs-service/src/main/java/com/fs/his/service/IFsQuestionAndAnswerService.java
  44. 70 0
      fs-service/src/main/java/com/fs/his/service/IFsUserInformationCollectionScheduleService.java
  45. 94 0
      fs-service/src/main/java/com/fs/his/service/IFsUserInformationCollectionService.java
  46. 95 0
      fs-service/src/main/java/com/fs/his/service/impl/FsDoctorConfirmServiceImpl.java
  47. 113 0
      fs-service/src/main/java/com/fs/his/service/impl/FsQuestionAndAnswerServiceImpl.java
  48. 105 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserInformationCollectionScheduleServiceImpl.java
  49. 902 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserInformationCollectionServiceImpl.java
  50. 32 0
      fs-service/src/main/java/com/fs/his/vo/AnswerVO.java
  51. 15 0
      fs-service/src/main/java/com/fs/his/vo/FsQuestionAndAnswerVO.java
  52. 79 0
      fs-service/src/main/java/com/fs/his/vo/FsUserInfoCollectionUVO.java
  53. 23 0
      fs-service/src/main/java/com/fs/his/vo/FsUserInformationCollectionListDVO.java
  54. 47 0
      fs-service/src/main/java/com/fs/his/vo/FsUserInformationCollectionVO.java
  55. 19 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsStoreProductScrm.java
  56. 1 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreConfirmPackageIdOrderParam.java
  57. 6 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStorePackageOrderCreateParam.java
  58. 20 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreProductAddEditParam.java
  59. 11 1
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java
  60. 2 0
      fs-service/src/main/java/com/fs/hisStore/service/IFsStoreProductScrmService.java
  61. 123 28
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  62. 15 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  63. 2 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreProductListQueryVO.java
  64. 3 0
      fs-service/src/main/java/com/fs/live/mapper/LiveMapper.java
  65. 4 4
      fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java
  66. 2 0
      fs-service/src/main/java/com/fs/live/service/ILiveService.java
  67. 3 3
      fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java
  68. 22 17
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  69. 54 49
      fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java
  70. 1 0
      fs-service/src/main/java/com/fs/live/vo/LiveDataListVo.java
  71. 1 0
      fs-service/src/main/java/com/fs/live/vo/LiveDataStatisticsVo.java
  72. 8 3
      fs-service/src/main/java/com/fs/live/vo/LiveOrderVoZm.java
  73. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  74. 10 1
      fs-service/src/main/java/com/fs/qw/service/impl/AsyncSopTestService.java
  75. 9 0
      fs-service/src/main/java/com/fs/qw/vo/QwSopCourseFinishTempSetting.java
  76. 3 0
      fs-service/src/main/java/com/fs/qw/vo/QwSopTempSetting.java
  77. 2 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  78. 4 25
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  79. 49 0
      fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java
  80. 5 3
      fs-service/src/main/java/com/fs/wx/order/service/ShippingService.java
  81. 4 4
      fs-service/src/main/java/com/fs/wx/order/service/WeChatAuthFactory.java
  82. 2 2
      fs-service/src/main/resources/application-druid-hst.yml
  83. 8 0
      fs-service/src/main/resources/mapper/course/FsProjectAddressConfigMapper.xml
  84. 85 0
      fs-service/src/main/resources/mapper/his/DoctorMsgMapper.xml
  85. 84 0
      fs-service/src/main/resources/mapper/his/FsDoctorConfirmMapper.xml
  86. 70 0
      fs-service/src/main/resources/mapper/his/FsQuestionAndAnswerMapper.xml
  87. 175 0
      fs-service/src/main/resources/mapper/his/FsUserInformationCollectionMapper.xml
  88. 224 0
      fs-service/src/main/resources/mapper/his/FsUserInformationCollectionScheduleMapper.xml
  89. 9 0
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  90. 1 1
      fs-service/src/main/resources/mapper/hisStore/FsStoreProductPackageScrmMapper.xml
  91. 24 2
      fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml
  92. 3 0
      fs-service/src/main/resources/mapper/live/LiveOrderMapper.xml
  93. 26 0
      fs-user-app/src/main/java/com/fs/app/controller/CommonController.java
  94. 100 0
      fs-user-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  95. 78 0
      fs-user-app/src/main/java/com/fs/app/controller/FsUserInformationCollectionController.java
  96. 4 0
      fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java
  97. 22 4
      fs-user-app/src/main/java/com/fs/app/controller/store/CompanyOrderScrmController.java
  98. 5 2
      fs-user-app/src/main/java/com/fs/framework/aspectj/UserOperationLogAspect.java

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

@@ -23,6 +23,7 @@ import com.fs.qw.param.QwWatchLogStatisticsListParam;
 import com.fs.qw.service.IQwWatchLogService;
 import com.fs.qw.vo.QwWatchLogAllStatisticsListVO;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
@@ -39,6 +40,9 @@ import java.util.List;
 @RequestMapping("/qw/course/courseWatchLog")
 public class QwFsCourseWatchLogController extends BaseController
 {
+    @Value("${cloud_host.company_name}")
+    private String signProjectName;
+
     @Autowired
     private IFsCourseWatchLogService fsCourseWatchLogService;
 
@@ -78,9 +82,18 @@ public class QwFsCourseWatchLogController extends BaseController
         }
         param.setSendType(2); //企微
         List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
+        if("济南联志健康".equals(signProjectName)){
+            FsCourseWatchLogStatisticsListVO totalData = fsCourseWatchLogService.getTotalDataAddItem(param);
+            list.add(totalData);
+        }
         return getDataTable(list);
     }
 
+    @GetMapping("/getSignProjectName")
+    public R getSignProjectName(){
+        return R.ok().put("signProjectName", signProjectName);
+    }
+
     @GetMapping("/statisticsListByCompany")
     public R statisticsListByCompany(FsCourseWatchLogStatisticsListParam param)
     {

+ 108 - 0
fs-admin/src/main/java/com/fs/his/controller/FsQuestionAndAnswerController.java

@@ -0,0 +1,108 @@
+package com.fs.his.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.his.domain.FsQuestionAndAnswer;
+import com.fs.his.service.IFsQuestionAndAnswerService;
+import com.fs.his.vo.OptionsVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 问答Controller
+ *
+ * @author fs
+ * @date 2025-09-29
+ */
+@RestController
+@RequestMapping("/his/answer")
+public class FsQuestionAndAnswerController extends BaseController
+{
+    @Autowired
+    private IFsQuestionAndAnswerService fsQuestionAndAnswerService;
+
+    /**
+     * 查询问答列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:answer:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsQuestionAndAnswer fsQuestionAndAnswer)
+    {
+        startPage();
+        List<FsQuestionAndAnswer> list = fsQuestionAndAnswerService.selectFsQuestionAndAnswerList(fsQuestionAndAnswer);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出问答列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:answer:export')")
+    @Log(title = "问答", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsQuestionAndAnswer fsQuestionAndAnswer)
+    {
+        List<FsQuestionAndAnswer> list = fsQuestionAndAnswerService.selectFsQuestionAndAnswerList(fsQuestionAndAnswer);
+        ExcelUtil<FsQuestionAndAnswer> util = new ExcelUtil<FsQuestionAndAnswer>(FsQuestionAndAnswer.class);
+        return util.exportExcel(list, "问答数据");
+    }
+
+    /**
+     * 获取问答详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('his:answer:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsQuestionAndAnswerService.selectFsQuestionAndAnswerById(id));
+    }
+
+    /**
+     * 新增问答
+     */
+    @PreAuthorize("@ss.hasPermi('his:answer:add')")
+    @Log(title = "问答", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsQuestionAndAnswer fsQuestionAndAnswer)
+    {
+        return toAjax(fsQuestionAndAnswerService.insertFsQuestionAndAnswer(fsQuestionAndAnswer));
+    }
+
+    /**
+     * 修改问答
+     */
+    @PreAuthorize("@ss.hasPermi('his:answer:edit')")
+    @Log(title = "问答", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsQuestionAndAnswer fsQuestionAndAnswer)
+    {
+        return toAjax(fsQuestionAndAnswerService.updateFsQuestionAndAnswer(fsQuestionAndAnswer));
+    }
+
+    /**
+     * 删除问答
+     */
+    @PreAuthorize("@ss.hasPermi('his:answer:remove')")
+    @Log(title = "问答", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsQuestionAndAnswerService.deleteFsQuestionAndAnswerByIds(ids));
+    }
+
+    /**
+     * 查询问答列表
+     */
+    @GetMapping("/allList")
+    public TableDataInfo getHospital()
+    {
+        List<OptionsVO> list = fsQuestionAndAnswerService.selectAllQuestionOptions();
+        return getDataTable(list);
+    }
+}

+ 4 - 0
fs-admin/src/main/java/com/fs/his/controller/FsUserController.java

@@ -190,6 +190,10 @@ public class FsUserController extends BaseController
         if(StringUtils.isNotEmpty(fsUser.getPhone())){
             fsUser.setPhone(encryptPhone(fsUser.getPhone()));
         }
+        if(StringUtils.isNotEmpty(fsUser.getCompanyUserIdMulti())){
+            String[] split = fsUser.getCompanyUserIdMulti().split(",");
+            fsUser.setCompanyUserIds(split);
+        }
 //        List<FsUserVO> list = fsUserService.selectFsUserVOListByProject(fsUser);
 
         // xgb sql执行太慢,优化修改

+ 12 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java

@@ -113,6 +113,18 @@ public class FsStoreProductScrmController extends BaseController
     }
 
 
+    /**
+     * 导出商品列表
+     */
+    @PreAuthorize("@ss.hasPermi('store:storeProduct:copy')")
+    @Log(title = "商品管理", businessType = BusinessType.INSERT, isStoreLog = true,logParam = {"商品","复制商品信息"})
+    @GetMapping("/copyStoreProduct")
+    public R copyStoreProduct(@RequestParam Long productId)
+    {
+        return fsStoreProductService.copyStoreProduct(productId);
+    }
+
+
     @Log(title = "商品管理", businessType = BusinessType.IMPORT,isStoreLog = true,logParam = {"商品","商品导入"})
     @PreAuthorize("@ss.hasPermi('store:storeProduct:import')")
     @PostMapping("/importData")

+ 6 - 0
fs-admin/src/main/java/com/fs/hisStore/task/ExpressTask.java

@@ -20,4 +20,10 @@ public class ExpressTask {
         fsStoreOrderScrmService.syncExpressToWx();
     }
 
+
+    //定时任务刷新订单结算状态
+    public void refreshOrderSettlementStatus(){
+        fsStoreOrderScrmService.refreshOrderSettlementStatus();
+    }
+
 }

+ 27 - 2
fs-admin/src/main/java/com/fs/live/controller/LiveOrderController.java

@@ -6,6 +6,7 @@ import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ParseUtils;
@@ -32,6 +33,7 @@ import com.fs.his.service.IFsExpressService;
 import com.fs.his.service.IFsUserService;
 import com.fs.hisStore.dto.StoreOrderExpressExportDTO;
 import com.fs.hisStore.param.*;
+import com.fs.hisStore.task.LiveTask;
 import com.fs.hisStore.vo.FsStoreOrderVO;
 import com.fs.live.domain.*;
 import com.fs.live.dto.LiveOrderCustomerExportDTO;
@@ -43,6 +45,7 @@ import com.fs.live.enums.LiveOrderCancleReason;
 import com.fs.live.param.LiveOrderScrmSetErpPhoneParam;
 import com.fs.live.service.*;
 import com.fs.live.vo.*;
+import com.fs.qw.utils.RSAUtils;
 import com.fs.store.domain.FsStoreDelivers;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
@@ -109,6 +112,11 @@ public class LiveOrderController extends BaseController
     @Autowired
     private ILiveOrderDfService liveOrderDfService;
 
+    @Autowired
+    private LiveTask liveTask;
+
+
+
 
     @GetMapping("/importTemplate")
     public AjaxResult importTemplate() {
@@ -153,6 +161,7 @@ public class LiveOrderController extends BaseController
     public TableDataInfo listZm(LiveOrder liveOrder)
     {
         startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         List<LiveOrderVoZm> list = liveOrderService.selectLiveOrderListZm(liveOrder);
         for (LiveOrderVoZm vo : list){
             vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
@@ -160,7 +169,15 @@ public class LiveOrderController extends BaseController
             vo.setUserBindPhone(ParseUtils.parsePhone(vo.getUserBindPhone()));
             vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
             vo.setCost(BigDecimal.ZERO);
-            vo.setCostPrice(BigDecimal.ZERO);
+            // 财务独特字段
+            if (loginUser.getPermissions().contains("live:liveOrder:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                vo.setCostPrice(vo.getCostPrice());
+                vo.setFPrice(BigDecimal.valueOf((long) vo.getCostPrice().intValue() * Integer.parseInt(vo.getTotalNum())));
+            } else {
+                vo.setCostPrice(BigDecimal.ZERO);
+                vo.setFPrice(BigDecimal.ZERO);
+            }
+
         }
         return getDataTable(list);
     }
@@ -174,13 +191,21 @@ public class LiveOrderController extends BaseController
     public AjaxResult exportZm(LiveOrder liveOrder)
     {
         List<LiveOrderVoZm> list = liveOrderService.selectLiveOrderListZm(liveOrder);
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         for (LiveOrderVoZm vo : list){
             vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
             vo.setCompanyUserPhone(ParseUtils.parsePhone(vo.getCompanyUserPhone()));
             vo.setUserBindPhone(ParseUtils.parsePhone(vo.getUserBindPhone()));
             vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
             vo.setCost(BigDecimal.ZERO);
-            vo.setCostPrice(BigDecimal.ZERO);
+            // 财务独特字段
+            if (loginUser.getPermissions().contains("live:liveOrder:finance") || loginUser.getPermissions().contains("*:*:*")) {
+                vo.setCostPrice(vo.getCostPrice());
+                vo.setFPrice(BigDecimal.valueOf((long) vo.getCostPrice().intValue() * Integer.parseInt(vo.getTotalNum())));
+            } else {
+                vo.setCostPrice(BigDecimal.ZERO);
+                vo.setFPrice(BigDecimal.ZERO);
+            }
         }
         ExcelUtil<LiveOrderVoZm> util = new ExcelUtil<LiveOrderVoZm>(LiveOrderVoZm.class);
         return util.exportExcel(list, "订单数据");

+ 24 - 5
fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java

@@ -10,6 +10,7 @@ import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.domain.FsUserCoursePeriodDays;
@@ -37,10 +38,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.time.LocalDate;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 
 /**
@@ -104,6 +102,20 @@ public class FsUserCoursePeriodController extends BaseController {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         fsUserCoursePeriod.setCompanyIdList(Collections.singletonList(loginUser.getCompany().getCompanyId()));
         List<FsUserCoursePeriodVO> list = fsUserCoursePeriodService.selectFsUserCoursePeriodPage(fsUserCoursePeriod);
+        // 使用Stream API的简洁写法
+        Long currentCompanyId = loginUser.getCompany().getCompanyId();
+        for (FsUserCoursePeriodVO vo : list) {
+            String isNeedRegisterMember = vo.getIsNeedRegisterMember();
+
+            if (StringUtils.isNotEmpty(isNeedRegisterMember)) {
+                boolean containsCurrentCompany = Arrays.stream(isNeedRegisterMember.split(","))
+                        .map(String::trim)
+                        .anyMatch(id -> id.equals(String.valueOf(currentCompanyId)));
+                vo.setIsNeedRegisterMember(containsCurrentCompany ? "1" : "0");
+            } else {
+                vo.setIsNeedRegisterMember("0");
+            }
+        }
         PageInfo<FsUserCoursePeriodVO> pageInfo = new PageInfo<>(list);
         Map<String, Object> result = new HashMap<>();
         result.put("rows", pageInfo.getList());
@@ -173,7 +185,14 @@ public class FsUserCoursePeriodController extends BaseController {
     @PutMapping("/editIsNeedRegisterMember")
     public AjaxResult editIsNeedRegisterMember(@RequestBody FsUserCoursePeriod fsUserCoursePeriod)
     {
-        return toAjax(fsUserCoursePeriodService.editIsNeedRegisterMember(fsUserCoursePeriod));
+        FsUserCoursePeriod fsUserCoursePeriod1 = fsUserCoursePeriodService.selectFsUserCoursePeriodById(fsUserCoursePeriod.getPeriodId());
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        if (ObjectUtil.isEmpty(fsUserCoursePeriod.getIsNeedRegisterMember())){
+            throw new CustomException("修改失败!");
+        }
+
+        return toAjax(fsUserCoursePeriodService.editIsNeedRegisterMember(fsUserCoursePeriod,loginUser.getCompany().getCompanyId().toString()));
+
     }
 
     /**

+ 12 - 0
fs-company/src/main/java/com/fs/company/controller/live/LiveController.java

@@ -51,6 +51,18 @@ public class LiveController extends BaseController
     @Autowired
     private ILiveCompanyCodeService liveCompanyCodeService;
 
+    /**
+     * 查询未结束直播间
+     */
+    @PreAuthorize("@ss.hasPermi('live:live:list')")
+    @GetMapping("/listToLiveNoEnd")
+    public TableDataInfo listToLiveNoEnd(Live live)
+    {
+        startPage();
+        List<Live> list = liveService.listToLiveNoEnd(live);
+        return getDataTable(list);
+    }
+
     /**
      * 查询直播列表
      */

+ 139 - 0
fs-company/src/main/java/com/fs/company/controller/store/FsUserInformationCollectionController.java

@@ -0,0 +1,139 @@
+package com.fs.company.controller.store;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyUserUser;
+import com.fs.company.service.ICompanyUserUserService;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.security.SecurityUtils;
+import com.fs.his.domain.FsUserInformationCollection;
+import com.fs.his.param.FsUserInformationCollectionParam;
+import com.fs.his.service.IFsUserInformationCollectionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 用户信息采集Controller
+ *
+ * @author fs
+ * @date 2025-10-14
+ */
+@RestController
+@RequestMapping("/store/collection")
+public class FsUserInformationCollectionController extends BaseController
+{
+    @Autowired
+    private IFsUserInformationCollectionService fsUserInformationCollectionService;
+    @Autowired
+    private ICompanyUserUserService companyUserUserService;
+
+    /**
+     * 查询用户信息采集列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:collection:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserInformationCollection fsUserInformationCollection)
+    {
+        startPage();
+        List<FsUserInformationCollection> list = fsUserInformationCollectionService.selectFsUserInformationCollectionList(fsUserInformationCollection);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出用户信息采集列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:collection:export')")
+    @Log(title = "用户信息采集", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserInformationCollection fsUserInformationCollection)
+    {
+        List<FsUserInformationCollection> list = fsUserInformationCollectionService.selectFsUserInformationCollectionList(fsUserInformationCollection);
+        ExcelUtil<FsUserInformationCollection> util = new ExcelUtil<FsUserInformationCollection>(FsUserInformationCollection.class);
+        return util.exportExcel(list, "用户信息采集数据");
+    }
+
+    /**
+     * 获取用户信息采集详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('his:collection:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsUserInformationCollectionService.selectFsUserInformationCollectionById(id));
+    }
+
+    /**
+     * 新增用户信息采集
+     */
+    @PreAuthorize("@ss.hasPermi('his:collection:add')")
+    @Log(title = "用户信息采集", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserInformationCollectionParam fsUserInformationCollection)
+    {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        Long companyUserId = loginUser.getUser().getUserId();
+        //查询用户是否绑定销售
+        Long userId = fsUserInformationCollection.getUserId();
+        if (userId == null || userId < 0){
+            return AjaxResult.error("请选择绑定用户");
+        }
+        CompanyUserUser companyUserUser = new CompanyUserUser();
+        companyUserUser.setCompanyUserId(companyUserId);
+        companyUserUser.setUserId(userId);
+        List<CompanyUserUser> companyUserUsers = companyUserUserService.selectCompanyUserUserList(companyUserUser);
+        if (companyUserUsers == null || companyUserUsers.isEmpty()){
+            return AjaxResult.error("用户未绑定该销售");
+        }
+        fsUserInformationCollection.setCompanyUserId(companyUserId);
+        return AjaxResult.success(fsUserInformationCollectionService.insertFsUserInformationCollection(fsUserInformationCollection));
+    }
+
+    /**
+     * 修改用户信息采集
+     */
+    @PreAuthorize("@ss.hasPermi('his:collection:edit')")
+    @Log(title = "用户信息采集", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserInformationCollectionParam fsUserInformationCollection)
+    {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        fsUserInformationCollection.setCompanyUserId(loginUser.getUser().getUserId());
+        return AjaxResult.success(fsUserInformationCollectionService.updateFsUserInformationCollection(fsUserInformationCollection));
+    }
+
+    /**
+     * 删除用户信息采集
+     */
+    @PreAuthorize("@ss.hasPermi('his:collection:remove')")
+    @Log(title = "用户信息采集", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsUserInformationCollectionService.deleteFsUserInformationCollectionByIds(ids));
+    }
+
+    @GetMapping("/getInfo")
+    public AjaxResult getInfo(FsUserInformationCollection fsUserInformationCollection){
+        return AjaxResult.success(fsUserInformationCollectionService.getInfo(fsUserInformationCollection));
+    }
+
+    /**
+     * 生成套餐包付款二维码
+     */
+    @PreAuthorize("@ss.hasPermi('his:collection:WxaCodeCollectionUnLimit')")
+    @GetMapping("/getWxaCodeCollectionUnLimit/{collectionId}")
+    public R getWxaCodeCollectionUnLimit(@PathVariable("collectionId") Long orderId)
+    {
+
+        return fsUserInformationCollectionService.getWxaCodePackageOrderUnLimit(orderId);
+
+    }
+}

+ 120 - 0
fs-company/src/main/java/com/fs/company/controller/store/FsUserInformationCollectionScheduleController.java

@@ -0,0 +1,120 @@
+package com.fs.company.controller.store;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.his.domain.FsUserInformationCollectionSchedule;
+import com.fs.his.enums.PrescriptionTaskStepEnum;
+import com.fs.his.service.IFsUserInformationCollectionScheduleService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+/**
+ * 用户信息采集进度Controller
+ *
+ * @author cgp
+ * @date 2025-11-18
+ */
+@RestController
+@RequestMapping("/store/collectionSchedule")
+public class FsUserInformationCollectionScheduleController extends BaseController
+{
+    @Autowired
+    private IFsUserInformationCollectionScheduleService fsUserInformationCollectionScheduleService;
+
+    /**
+     * 对于每条用户信息采集记录,只获取其最新的任务流程
+     */
+    @PreAuthorize("@ss.hasPermi('his:collectionSchedule:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule)
+    {
+        startPage();
+        List<FsUserInformationCollectionSchedule> list = fsUserInformationCollectionScheduleService.getUserRecordLatestTaskProcess(fsUserInformationCollectionSchedule);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出用户信息采集进度列表
+     */
+    @PreAuthorize("@ss.hasPermi('his:collectionSchedule:export')")
+    @Log(title = "用户信息采集进度", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule)
+    {
+        List<FsUserInformationCollectionSchedule> list = fsUserInformationCollectionScheduleService.selectFsUserInformationCollectionScheduleList(fsUserInformationCollectionSchedule);
+        ExcelUtil<FsUserInformationCollectionSchedule> util = new ExcelUtil<FsUserInformationCollectionSchedule>(FsUserInformationCollectionSchedule.class);
+        return util.exportExcel(list, "用户信息采集进度数据");
+    }
+
+    /**
+     * 获取用户信息采集进度详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('his:collectionSchedule:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsUserInformationCollectionScheduleService.selectFsUserInformationCollectionScheduleById(id));
+    }
+
+    /**
+     * 新增用户信息采集进度
+     */
+    @PreAuthorize("@ss.hasPermi('his:collectionSchedule:add')")
+    @Log(title = "用户信息采集进度", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule)
+    {
+        return toAjax(fsUserInformationCollectionScheduleService.insertFsUserInformationCollectionSchedule(fsUserInformationCollectionSchedule));
+    }
+
+    /**
+     * 修改用户信息采集进度
+     */
+    @PreAuthorize("@ss.hasPermi('his:collectionSchedule:edit')")
+    @Log(title = "用户信息采集进度", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule)
+    {
+        return toAjax(fsUserInformationCollectionScheduleService.updateFsUserInformationCollectionSchedule(fsUserInformationCollectionSchedule));
+    }
+
+    /**
+     * 删除用户信息采集进度
+     */
+    @PreAuthorize("@ss.hasPermi('his:collectionSchedule:remove')")
+    @Log(title = "用户信息采集进度", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsUserInformationCollectionScheduleService.deleteFsUserInformationCollectionScheduleByIds(ids));
+    }
+
+    /**
+     * 获取用户信息采集任务流程步骤枚举列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:collectionSchedule:list')")
+    @GetMapping("/steps")
+    public AjaxResult getTaskSteps() {
+        List<Map<String, Object>> steps = Arrays.stream(PrescriptionTaskStepEnum.values())
+                .map(enumItem -> {
+                    Map<String, Object> map = new HashMap<>();
+                    map.put("dictValue", enumItem.getCode());
+                    map.put("dictLabel", enumItem.getDesc());
+                    return map;
+                })
+                .collect(Collectors.toList());
+        return AjaxResult.success(steps);
+    }
+}

+ 6 - 0
fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -465,6 +465,12 @@ public class WebSocketServer {
     //错误时调用
     @OnError
     public void onError(Session session, Throwable throwable) {
+
+        try {
+            this.onClose(session);
+        } catch (Exception e) {
+            log.error("webSocket 错误 onError", e);
+        }
         if (throwable instanceof EOFException) {
             log.info("WebSocket连接被客户端正常关闭(EOF),sessionId: {}", session.getId());
         } else {

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

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fs.app.taskService.SopLogsTaskService;
+import com.fs.common.config.FSSysConfig;
 import com.fs.common.utils.PubFun;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.Company;
@@ -1097,6 +1098,27 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                         setting.setMiniprogramAppid("未找到匹配的公司的自定义小程序:"+companyId);
                     }
 
+                    break;
+                //直播小程序单独
+                case "12":
+                    String sortLiveLink;
+                    sortLiveLink = "/pages_course/living?companyId=" + companyId + "&companyUserId=" + companyUserId + "&liveId=" + setting.getLiveId();
+
+
+                    String miniprogramLiveTitle = setting.getMiniprogramTitle();
+                    int maxLiveLength = 17;
+                    setting.setMiniprogramTitle(miniprogramLiveTitle.length() > maxLiveLength ? miniprogramLiveTitle.substring(0, maxLiveLength) + "..." : miniprogramLiveTitle);
+                    String json = configService.selectConfigByKey("his.config");
+                    FSSysConfig sysConfig= JSON.parseObject(json,FSSysConfig.class);
+                    setting.setMiniprogramAppid(sysConfig.getAppId());
+                    setting.setMiniprogramPage(sortLiveLink);
+                    setting.setContentType("4");
+                    try {
+                        setting.setMiniprogramPicUrl(StringUtil.strIsNullOrEmpty(setting.getMiniprogramPicUrl()) ? "https://cos.his.cdwjyyh.com/fs/20250331/ec2b4e73be8048afbd526124a655ad56.png" : setting.getMiniprogramPicUrl());
+                    } catch (Exception e) {
+                        log.error("赋值-小程序封面地址失败-" + e);
+                    }
+
                     break;
                 default:
                     break;

+ 49 - 0
fs-service/src/main/java/com/fs/course/domain/FsProjectAddressConfig.java

@@ -0,0 +1,49 @@
+package com.fs.course.domain;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class FsProjectAddressConfig {
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 项目名称
+     */
+    private String name;
+
+    /**
+     * 唯一编码
+     */
+    private String code;
+
+    /**
+     * api接口地址
+     */
+    private String addressUrl;
+
+    /**
+     * 存储桶地址
+     */
+    private String bucketPath;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 修改时间
+     */
+    private LocalDateTime updateTime;
+
+    /**
+     * 发送类型 1个微 2企微
+     */
+    private Integer sendType;
+}

+ 1 - 1
fs-service/src/main/java/com/fs/course/domain/FsUserCoursePeriod.java

@@ -118,5 +118,5 @@ public class FsUserCoursePeriod
      */
     private Date periodLine;
     /** 是否需要单独注册会员,1-是,0-否(用于个微销售分享看课) */
-    private Integer isNeedRegisterMember;
+    private String isNeedRegisterMember;
 }

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

@@ -13,10 +13,10 @@ public class FsOrderDeliveryNoteDTO {
     /**
      * 系统订单号
      * **/
-    @Excel(name = "原始单号",width = 20,sort = 1)
+    @Excel(name = "系统订单号(必填)",width = 20,sort = 1)
     private String orderNumber;
 
-//    @Excel(name = "物流公司",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)
@@ -24,25 +24,25 @@ public class FsOrderDeliveryNoteDTO {
 
     private String deliveryName;
 
-    @Excel(name = "快递单号",width = 20,sort = 23)
+    @Excel(name = "快递单号(必填)",width = 20,sort = 3)
     private String deliveryId;
 
-//    @Excel(name = "物流状态(0:暂无信息、1:已揽收、2:在途中、3:签收、4:问题件)",width = 40,sort = 4)
+    @Excel(name = "物流状态(0:暂无信息、1:已揽收、2:在途中、3:签收、4:问题件)",width = 40,sort = 4)
     private Integer deliveryStatus;
 
-//    @Excel(name = "物流结算费用",width = 20,sort = 5)
+    @Excel(name = "物流结算费用",width = 20,sort = 5)
     private BigDecimal deliveryPayMoney;
 
-//    @Excel(name = "物流跟踪状态(311:快递柜或驿站签收、304:派件异常后最终签收、301:正常签收、211:已放入快递柜或驿站、202:派件中、201:到达派件城市、401:发货无信息、412:快递柜或驿站超时未取、407:退货未签收)",width = 40,sort = 6)
+    @Excel(name = "物流跟踪状态(311:快递柜或驿站签收、304:派件异常后最终签收、301:正常签收、211:已放入快递柜或驿站、202:派件中、201:到达派件城市、401:发货无信息、412:快递柜或驿站超时未取、407:退货未签收)",width = 40,sort = 6)
     private Integer deliveryType;
 
-//    @Excel(name = "物流结算状态(1:已结算、2:冻结、3:解冻、4:退回运费、5.调账)",width = 20,sort = 7)
+    @Excel(name = "物流结算状态(1:已结算、2:冻结、3:解冻、4:退回运费、5.调账)",width = 20,sort = 7)
     private Integer deliveryPayStatus;
 
-//    @Excel(name = "快递账单日期",width = 20,sort = 8)
+    @Excel(name = "快递账单日期",width = 20,sort = 8)
     private String deliveryTime;
 
-//    @Excel(name = "快递结算日期",width = 20,sort = 9)
+    @Excel(name = "快递结算日期",width = 20,sort = 9)
     private String deliveryPayTime;
 
 //    /**

+ 92 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -295,6 +295,98 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             "</script>"})
     List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO(FsCourseWatchLogStatisticsListParam param);
 
+    @Select({"<script> " +
+            " \tselect \n" +
+            "\t'总合计' as qw_user_name,\n" +
+            "\tsum(t1.type1) as type1,\n" +
+            "\tsum(t1.type2) as type2,\n" +
+            "\tsum(t1.type3) as type3,\n" +
+            "\tsum(t1.type4) as type4,\n" +
+            "\t (\n" +
+            "     sum(t1.type1)+\n" +
+            "     sum(t1.type2) +\n" +
+            "     sum(t1.type4)\n" +
+            "   ) AS on_line_num,\n" +
+            "\t sum(send_number) as send_number,\n" +
+            "\t sum(is_user_wait_number) as is_user_wait_number,\n" +
+            "\t sum(no_user_wait_number) as no_user_wait_number,\n" +
+            "\t sum(red_amount) as red_amount\n" +
+            "\tfrom  ( select t.* " +
+            "<if test= 'sendType != 1 '> " +
+            " ,concat(round(if(t.send_number=0,0,(t.on_line_num/t.send_number)*100),2),'%') on_line_rate" +
+            " ,concat(round(if(t.send_number=0,0,(t.type2/t.send_number)*100),2),'%') finished_rate" +
+            "</if> " +
+            "from (" +
+            "SELECT \n" +
+            "o.video_id,o.company_id,o.qw_user_id,DATE(o.create_time) create_time," +
+            "<if test= 'sendType != 1 '> " +
+            " qu.qw_user_name qw_user_name," +
+            "</if>\n" +
+            "<if test= 'sendType == 1 '> " +
+            " cu.nick_name qw_user_name," +
+            "</if>\n" +
+            "v.title videoName,uc.course_name,\n" +
+            "SUM(CASE WHEN o.log_type = '1' THEN 1 ELSE 0 END) AS type1,\n" +
+            "SUM(CASE WHEN o.log_type = '2' THEN 1 ELSE 0 END) AS type2,\n" +
+            "SUM(CASE WHEN o.log_type = '3' THEN 1 ELSE 0 END) AS type3,\n" +
+            "SUM(CASE WHEN o.log_type = '4' THEN 1 ELSE 0 END) AS type4,\n" +
+            "(\n" +
+            "  SUM(CASE WHEN o.log_type = '1' THEN 1 ELSE 0 END) +\n" +
+            "  SUM(CASE WHEN o.log_type = '2' THEN 1 ELSE 0 END) +\n" +
+            "  SUM(CASE WHEN o.log_type = '4' THEN 1 ELSE 0 END)\n" +
+            ") AS on_line_num " +
+            "<if test= 'sendType != 1 '> " +
+            " ,count(o.log_id) send_number" +
+            " ,sum(if((o.user_id is not null or o.user_id>0) and o.log_type=3,1,0)) is_user_wait_number" +
+            " ,sum(if((o.user_id is null or o.user_id=0) and o.log_type=3,1,0)) no_user_wait_number" +
+//            " ,sum(ifnull(fcr.amount,0)) red_amount" +
+            ",(SELECT SUM(amount) FROM fs_course_red_packet_log \n" +
+            "     WHERE user_id = o.user_id AND video_id = o.video_id) as red_amount " +
+            "</if> " +
+            "FROM fs_course_watch_log o " +
+            "<if test= 'sendType != 1 '> " +
+            " LEFT JOIN qw_user qu on qu.id=o.qw_user_id " +
+//            " LEFT JOIN fs_course_red_packet_log fcr on o.user_id = fcr.user_id and fcr.video_id = o.video_id" + //会有笛卡尔积问题
+            "</if>\n" +
+            "LEFT JOIN fs_user_course_video v on v.video_id=o.video_id \n" +
+            "LEFT JOIN fs_user_course uc on uc.course_id=v.course_id\n" +
+            "<if test= 'sendType == 1 '> " +
+            " LEFT JOIN company_user cu on cu.user_id=o.company_user_id\n" +
+            "</if>\n" +
+            "where o.company_id=#{companyId} " +
+            "<if test= 'sendType != null '> " +
+            "       and  send_type= #{sendType} " +
+            "</if>\n" +
+            "<if test= 'sTime != null '> " +
+            "       and DATE(o.create_time) &gt;= DATE(#{sTime})\n" +
+            "</if>\n" +
+            "<if test='eTime != null '> " +
+            "      and DATE(o.create_time) &lt;= DATE(#{eTime})\n" +
+            "</if>" +
+            "<if test ='sendType != 1 and nickName !=null and nickName!=\"\"'>\n" +
+            "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
+            "</if>" +
+            "<if test ='sendType == 1 and nickName !=null and nickName!=\"\"'>\n" +
+            "   and cu.nick_name like concat( #{nickName}, '%')\n" +
+            "</if>" +
+            "<if test ='courseId !=null'> " +
+            "     and o.course_id = #{courseId} " +
+            "</if>" +
+            "<if test ='videoId !=null'> " +
+            "     and o.video_id = #{videoId} " +
+            "</if>" +
+            "GROUP BY o.video_id," +
+            "<if test= 'sendType != 1 '> " +
+            " o.qw_user_id," +
+            "</if>\n" +
+            "<if test= 'sendType == 1 '> " +
+            " o.company_user_id," +
+            "</if>\n" +
+            "DATE(o.create_time)\n" +
+            "ORDER BY o.video_id ,DATE(o.create_time) " +
+            ") t ) t1 \n"+
+            "</script>"})
+    FsCourseWatchLogStatisticsListVO getTotalDataAddItem(FsCourseWatchLogStatisticsListParam param);
     @Select({"<script> " +
             "SELECT COUNT(*) FROM (" +
             "    SELECT 1 " +

+ 14 - 0
fs-service/src/main/java/com/fs/course/mapper/FsProjectAddressConfigMapper.java

@@ -0,0 +1,14 @@
+package com.fs.course.mapper;
+
+import com.fs.course.domain.FsProjectAddressConfig;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+public interface FsProjectAddressConfigMapper {
+
+    /**
+     * 根据code查询url
+     */
+    @Select("select * from fs_project_address_config where code = #{code}")
+    FsProjectAddressConfig selectDomainByCode(@Param("code") String code);
+}

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

@@ -88,6 +88,14 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
     void testFinishMsg();
 
     List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO(FsCourseWatchLogStatisticsListParam param);
+
+    /**
+     * 新增企微看课统计汇总行查询
+     * @param param
+     * @return
+     */
+    FsCourseWatchLogStatisticsListVO getTotalDataAddItem(FsCourseWatchLogStatisticsListParam param);
+
     List<FsCourseWatchLogStatisticsListByCompanyVO> selectFsCourseWatchLogStatisticsListByCompanyVO(FsCourseWatchLogStatisticsListParam param);
 
     void scheduleBatchUpdateToDatabase();

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

@@ -0,0 +1,12 @@
+package com.fs.course.service;
+
+
+import com.fs.course.domain.FsProjectAddressConfig;
+
+public interface IFsProjectAddressConfigService {
+
+    /**
+     * 根据code查询url
+     */
+    FsProjectAddressConfig selectDomainByCode(String code);
+}

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

@@ -102,7 +102,7 @@ public interface IFsUserCoursePeriodService
 
     List<Long> selectFsUserCoursePeriodListByPeriodId(List<Long> periodIds,Long companyId);
 
-    int editIsNeedRegisterMember(FsUserCoursePeriod fsUserCoursePeriod);
+    int editIsNeedRegisterMember(FsUserCoursePeriod fsUserCoursePeriod,String s);
 
     List<SysDictData> selectFsUserCoursePeriodListLabel(FsUserCoursePeriod fsUserCoursePeriod);
 }

+ 23 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -59,6 +59,8 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.Duration;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
@@ -764,6 +766,27 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         return fsCourseWatchLogMapper.selectFsCourseWatchLogStatisticsListVO(param);
     }
 
+
+    /**
+     * 新增企微看课统计汇总行查询
+     * @param param
+     * @return
+     */
+    public FsCourseWatchLogStatisticsListVO getTotalDataAddItem(FsCourseWatchLogStatisticsListParam param){
+        FsCourseWatchLogStatisticsListVO addItem =  fsCourseWatchLogMapper.getTotalDataAddItem(param);
+        if(null != addItem && null != addItem.getOnLineNum() && 0 != addItem.getOnLineNum() && null != addItem.getSendNumber()){
+            addItem.setOnLineRate(new BigDecimal(addItem.getOnLineNum())
+                    .multiply(new BigDecimal(100))
+                    .divide(new BigDecimal(addItem.getSendNumber()),2,BigDecimal.ROUND_HALF_UP)
+                     + "%");
+
+            addItem.setFinishedRate(new BigDecimal(addItem.getType2())
+                    .multiply(new BigDecimal(100))
+                    .divide(new BigDecimal(addItem.getSendNumber()),2,BigDecimal.ROUND_HALF_UP) + "%");
+        }
+        return addItem;
+    }
+
     @Override
     public List<FsCourseWatchLogStatisticsListByCompanyVO> selectFsCourseWatchLogStatisticsListByCompanyVO(FsCourseWatchLogStatisticsListParam param) {
         return fsCourseWatchLogMapper.selectFsCourseWatchLogStatisticsListByCompanyVO(param);

+ 23 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsProjectAddressConfigServiceImpl.java

@@ -0,0 +1,23 @@
+package com.fs.course.service.impl;
+
+import com.fs.course.domain.FsProjectAddressConfig;
+import com.fs.course.mapper.FsProjectAddressConfigMapper;
+import com.fs.course.service.IFsProjectAddressConfigService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+@Service
+public class FsProjectAddressConfigServiceImpl implements IFsProjectAddressConfigService {
+
+    @Resource
+    private FsProjectAddressConfigMapper fsProjectAddressConfigMapper;
+
+    /**
+     * 根据code查询url
+     */
+    @Override
+    public FsProjectAddressConfig selectDomainByCode(String code) {
+        return fsProjectAddressConfigMapper.selectDomainByCode(code);
+    }
+}

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

@@ -313,7 +313,30 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
     }
 
     @Override
-    public int editIsNeedRegisterMember(FsUserCoursePeriod fsUserCoursePeriod) {
+    public int editIsNeedRegisterMember(FsUserCoursePeriod fsUserCoursePeriod,String companyId) {
+        String currentValue = fsUserCoursePeriod.getIsNeedRegisterMember();
+
+        if (currentValue.equals("0")) {
+            // 移除指定的 companyId
+            String updatedValue = Arrays.stream(currentValue.split(","))
+                    .map(String::trim)
+                    .filter(id -> !id.equals(companyId))
+                    .collect(Collectors.joining(","));
+            fsUserCoursePeriod.setIsNeedRegisterMember(updatedValue);
+        } else {
+            // 添加 companyId,并过滤掉 "0"
+            String updatedValue = Arrays.stream(currentValue.split(","))
+                    .map(String::trim)
+                    .filter(id -> !id.equals("0")) // 清除 "0"
+                    .collect(Collectors.collectingAndThen(
+                            Collectors.toCollection(LinkedHashSet::new),
+                            set -> {
+                                set.add(companyId);
+                                return String.join(",", set);
+                            }
+                    ));
+            fsUserCoursePeriod.setIsNeedRegisterMember(updatedValue);
+        }
         return fsUserCoursePeriodMapper.updateFsUserCoursePeriod(fsUserCoursePeriod);
     }
 

+ 96 - 95
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -503,7 +503,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){
@@ -546,10 +546,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
 
 
-        //服务号授权的,缺mpOpenId的重新登录 linkType = 4为app看课 不需要mp
-        if (param.getLinkType() != 4 && 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){
@@ -604,26 +604,69 @@ 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("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);
+            } 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());
+            //没找到用户 || 找到的用户数量大于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 addCustomerService(param.getQwUserId(),msg);
         }
@@ -723,82 +766,36 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
                 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);
 
-//        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) {
@@ -974,7 +971,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());
@@ -2184,11 +2181,15 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
                 if((companyUser.getIsAllowedAllRegister() !=null && companyUser.getIsAllowedAllRegister() != 1)){
                     return ResponseResult.fail(504,"当前销售禁止绑定会员,请联系销售!");
                 }
+                // 使用 Stream API 检查是否包含 companyId
+                // 修正类型转换问题
                 if (companyUser.getIsNeedRegisterMember() == null || companyUser.getIsNeedRegisterMember() == 1
-                        ||(ObjectUtils.isNotEmpty(fsUserCoursePeriod)
-                        &&ObjectUtils.isNotEmpty(fsUserCoursePeriod.getIsNeedRegisterMember())
-                        &&fsUserCoursePeriod.getIsNeedRegisterMember().equals(1))){
-                    return ResponseResult.fail(504,"请联系销售发送邀请链接成为会员!");
+                        || (ObjectUtils.isNotEmpty(fsUserCoursePeriod)
+                        && ObjectUtils.isNotEmpty(fsUserCoursePeriod.getIsNeedRegisterMember())
+                        && Arrays.stream(fsUserCoursePeriod.getIsNeedRegisterMember().split(","))
+                        .map(String::trim)
+                        .anyMatch(id -> id.equals(String.valueOf(company.getCompanyId()))))) {
+                    return ResponseResult.fail(504, "请联系销售发送邀请链接成为会员!");
                 }
                 int defaultStatus = (company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1;
                 userCompanyUser = userCompanyUserService.bindRelationship(param.getUserId(), courseProject, companyUser.getCompanyId(), companyUser.getUserId(), defaultStatus);

+ 1 - 1
fs-service/src/main/java/com/fs/gtPush/service/impl/uniPush2ServiceImpl.java

@@ -46,7 +46,7 @@ public class uniPush2ServiceImpl implements uniPush2Service {
     public void pushSopAppLinkMsgByExternalIM(String cropId, String linkTile, String linkDescribe,String linkImageUrl, String link, Long companyUserId,Long fsUserId) throws JsonProcessingException {
 
         if (companyUserId!=null&&fsUserId!=null && fsUserId!=0){
-            openIMService.sendCourse(fsUserId,companyUserId,link,linkDescribe,linkImageUrl,cropId);
+            openIMService.sendCourse(fsUserId,companyUserId,link,linkTile,linkImageUrl,cropId);
         }
 
     }

+ 42 - 0
fs-service/src/main/java/com/fs/his/domain/DoctorMsg.java

@@ -0,0 +1,42 @@
+package com.fs.his.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 医生消息对象 doctor_msg
+ *
+ * @author fs
+ * @date 2025-11-04
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DoctorMsg extends BaseEntity{
+
+    /** $column.columnComment */
+    private Long id;
+
+    /** 类型:1医生开方 2药师处方审核 3信息采集建议 */
+    @Excel(name = "类型:1医生开方 2药师处方审核 3信息采集建议")
+    private Integer type;
+
+    /** 标题 */
+    @Excel(name = "标题")
+    private String title;
+
+    /** 内容 */
+    @Excel(name = "内容")
+    private String content;
+
+    /** 0未读 1已读 */
+    @Excel(name = "0未读 1已读")
+    private Integer isRead;
+
+    /** 医生id */
+    @Excel(name = "医生id")
+    private Long doctorId;
+
+
+}

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

@@ -0,0 +1,47 @@
+package com.fs.his.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 【请填写功能名称】对象 fs_doctor_confirm
+ *
+ * @author fs
+ * @date 2025-10-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsDoctorConfirm extends BaseEntity{
+
+    /** $column.columnComment */
+    private Long id;
+
+    /** 用户信息采集id */
+    @Excel(name = "用户信息采集id")
+    private Long collectionId;
+
+    /** 开始时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date startTime;
+
+    /** 结束时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date doctorEndTime;
+
+    /** 医生id */
+    @Excel(name = "医生id")
+    private Long doctorId;
+
+    /** 确认秒数 */
+    @Excel(name = "确认秒数")
+    private Long confirmSecond;
+
+
+}

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

@@ -0,0 +1,28 @@
+package com.fs.his.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 问答对象 fs_question_and_answer
+ *
+ * @author fs
+ * @date 2025-09-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsQuestionAndAnswer extends BaseEntity{
+
+    /** $column.columnComment */
+    private Long id;
+
+    private String questionName;
+
+    /** 问答数据 */
+    @Excel(name = "问答数据")
+    private String jsonInfo;
+
+
+}

+ 15 - 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;
@@ -209,6 +218,12 @@ public class FsUser extends BaseEntity
     @TableField(exist = false)
     private String nickname;
 
+    /**
+     * 昵称-精确查询
+     * **/
+    @TableField(exist = false)
+    private String nicknameExact;
+
     public String getNickname() {
         return nickname;
     }

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

@@ -0,0 +1,96 @@
+package com.fs.his.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 用户信息采集对象 fs_user_information_collection
+ *
+ * @author fs
+ * @date 2025-10-14
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsUserInformationCollection extends BaseEntity{
+
+    /** $column.columnComment */
+    private Long id;
+
+    /** 问答id */
+    @Excel(name = "问答id")
+    private Long questionId;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long userId;
+
+    /** 信息采集json数据 */
+    @Excel(name = "信息采集json数据")
+    private String jsonInfo;
+
+    /** 用户确认状态 0-未确认 1-已确认 */
+    @Excel(name = "用户确认状态 0-未确认 1-已确认")
+    private Integer userConfirm;
+
+    /** 医生确认状态 0-未确认 2-已确认 */
+    @Excel(name = "医生确认状态 0-未确认 2-已确认")
+    private Integer doctorConfirm;
+
+    // 医生id
+    private Long doctorId;
+
+    //销售id
+    private Long companyUserId;
+
+    //套餐包id
+    private Long packageId;
+
+    //支付类型 0-全款 1-物流代收
+    private Integer payType;
+
+    //代收金额
+    private BigDecimal amount;
+
+    //用户补充说明
+    private String userAdvice;
+
+    //医生建议
+    private String doctorAdvice;
+
+    //是否关联套餐包 0-不关联 1-关联
+    private Integer isPackage;
+
+    //套餐包订单号
+    private String packageOrderCode;
+
+    //用户第二次确认状态
+    private Integer userConfirm2;
+
+    //确认状态 1第一次确认 2第二次确认
+    private Integer status;
+
+    //医生确认时间
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date doctorConfirmTime;
+
+    //用户性别 0女 1男
+    private Integer sex;
+    //用户姓名
+    private String userName;
+    //用户电话后四位
+    private String userPhoneFour;
+
+    //是否过敏
+    private String allergy;
+    //备注
+    private String remark;
+
+    private Long patientId;
+
+}

+ 71 - 0
fs-service/src/main/java/com/fs/his/domain/FsUserInformationCollectionSchedule.java

@@ -0,0 +1,71 @@
+package com.fs.his.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 用户信息采集进度对象 fs_user_information_collection_schedule
+ *
+ * @author fs
+ * @date 2025-11-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsUserInformationCollectionSchedule extends BaseEntity{
+
+    /** 主键 */
+    private Long id;
+
+    /** 用户信息采集表id */
+    private Long collectionId;
+
+    /** 用户id */
+    private Long userId;
+
+    /** 客户姓名 */
+    private String userName;
+
+    /** 医生id */
+    private Long doctorId;
+
+    /** 约诊医生 */
+    private String doctorName;
+
+    /** 套餐包Id */
+    private Long packageId;
+
+    /** 挂载商品名称 */
+    private String packageName;
+
+    /** 当前流程节点:(1:待用户第一次确认、2:待开方、3:待药师审核、4:待建议、5:待用户二次确认、6:完成);其中带疗法模式有1,2,3,5,6;无疗法模式只有1,4,5,6 */
+    private Integer currentStep;
+
+    /** 整体任务状态:1-进行中,2-已完成,3-已中止 */
+    private Integer status;
+
+    /** 创建时间 */
+    private Date createTime;
+
+    /** 完成时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "完成时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date completedTime;
+
+    /** 中止时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "中止时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date terminatedTime;
+
+    /** 中止操作人id */
+    private Long terminatedBy;
+
+    /** 订单号 */
+    private String orderCode;
+
+
+}

+ 79 - 0
fs-service/src/main/java/com/fs/his/enums/PrescriptionTaskStepEnum.java

@@ -0,0 +1,79 @@
+package com.fs.his.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 用户信息采集任务流程步骤枚举
+ *
+ * 1 - 待用户第一次确认
+ * 2 - 待开方
+ * 3 - 待药师审核
+ * 4 - 待建议
+ * 5 - 待用户二次确认
+ */
+@Getter
+public enum PrescriptionTaskStepEnum {
+
+    WAITING_FIRST_CONFIRM(1, "待用户第一次确认"),
+    WAITING_PRESCRIBE(2, "待开方"),
+    WAITING_PHARMACIST_REVIEW(3, "待药师审核"),
+    WAITING_SUGGESTION(4, "待建议"),
+    WAITING_SECOND_CONFIRM(5, "待用户二次确认"),
+    WAITING_COMPLETED(6, "完成");
+
+    /** 步骤码(存入数据库的值) */
+    private final Integer code;
+
+    /** 步骤描述 */
+    private final String desc;
+
+    PrescriptionTaskStepEnum(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    /**
+     * 根据 code 获取枚举(用于从数据库值转为枚举)
+     *
+     * @param code 步骤码
+     * @return 对应的枚举,若无匹配则返回 null
+     */
+    public static PrescriptionTaskStepEnum fromCode(Integer code) {
+        if (code == null) {
+            return null;
+        }
+        for (PrescriptionTaskStepEnum step : values()) {
+            if (step.code.equals(code)) {
+                return step;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Jackson 反序列化时使用(支持 JSON 中直接传数字如 1 -> WAITING_FIRST_CONFIRM)
+     */
+    @JsonCreator
+    public static PrescriptionTaskStepEnum fromJsonValue(Object value) {
+        if (value instanceof Number) {
+            return fromCode(((Number) value).intValue());
+        } else if (value instanceof String) {
+            try {
+                return fromCode(Integer.parseInt((String) value));
+            } catch (NumberFormatException ignored) {
+                // ignore
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Jackson 序列化时输出 code 而不是 name(如输出 1 而不是 "WAITING_FIRST_CONFIRM")
+     */
+    @JsonValue
+    public Integer getCode() {
+        return this.code;
+    }
+}

+ 75 - 0
fs-service/src/main/java/com/fs/his/mapper/DoctorMsgMapper.java

@@ -0,0 +1,75 @@
+package com.fs.his.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.DoctorMsg;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+import java.util.List;
+
+/**
+ * 医生消息Mapper接口
+ *
+ * @author fs
+ * @date 2025-11-04
+ */
+public interface DoctorMsgMapper extends BaseMapper<DoctorMsg>{
+    /**
+     * 查询医生消息
+     *
+     * @param id 医生消息主键
+     * @return 医生消息
+     */
+    DoctorMsg selectDoctorMsgById(Long id);
+
+    /**
+     * 查询医生消息列表
+     *
+     * @param doctorMsg 医生消息
+     * @return 医生消息集合
+     */
+    List<DoctorMsg> selectDoctorMsgList(DoctorMsg doctorMsg);
+
+    /**
+     * 新增医生消息
+     *
+     * @param doctorMsg 医生消息
+     * @return 结果
+     */
+    int insertDoctorMsg(DoctorMsg doctorMsg);
+
+    /**
+     * 修改医生消息
+     *
+     * @param doctorMsg 医生消息
+     * @return 结果
+     */
+    int updateDoctorMsg(DoctorMsg doctorMsg);
+
+    /**
+     * 删除医生消息
+     *
+     * @param id 医生消息主键
+     * @return 结果
+     */
+    int deleteDoctorMsgById(Long id);
+
+    /**
+     * 批量删除医生消息
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteDoctorMsgByIds(Long[] ids);
+
+    //获取医生未读消息总数
+    @Select("select IFNULL(count( 0),0) from doctor_msg where doctor_id = #{doctorId} and is_read = 0 ")
+    Long selectDoctorMsgCountsByDoctorId(Long doctorId);
+
+    @Select("select IFNULL(count( type),0) from doctor_msg where doctor_id = #{doctorId} and is_read = 0 and type = #{type}")
+    Long selectDoctorMsgCountsByDoctorIdAndType(@Param("doctorId") Long doctorId, @Param("type") int type);
+
+    @Update("update  doctor_msg set is_read=1 where doctor_id = #{doctorId} ")
+    int setAllRead(Long doctorId);
+}

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

@@ -0,0 +1,62 @@
+package com.fs.his.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsDoctorConfirm;
+
+import java.util.List;
+
+/**
+ * 【请填写功能名称】Mapper接口
+ *
+ * @author fs
+ * @date 2025-10-18
+ */
+public interface FsDoctorConfirmMapper extends BaseMapper<FsDoctorConfirm>{
+    /**
+     * 查询【请填写功能名称】
+     *
+     * @param id 【请填写功能名称】主键
+     * @return 【请填写功能名称】
+     */
+    FsDoctorConfirm selectFsDoctorConfirmById(Long id);
+
+    /**
+     * 查询【请填写功能名称】列表
+     *
+     * @param fsDoctorConfirm 【请填写功能名称】
+     * @return 【请填写功能名称】集合
+     */
+    List<FsDoctorConfirm> selectFsDoctorConfirmList(FsDoctorConfirm fsDoctorConfirm);
+
+    /**
+     * 新增【请填写功能名称】
+     *
+     * @param fsDoctorConfirm 【请填写功能名称】
+     * @return 结果
+     */
+    int insertFsDoctorConfirm(FsDoctorConfirm fsDoctorConfirm);
+
+    /**
+     * 修改【请填写功能名称】
+     *
+     * @param fsDoctorConfirm 【请填写功能名称】
+     * @return 结果
+     */
+    int updateFsDoctorConfirm(FsDoctorConfirm fsDoctorConfirm);
+
+    /**
+     * 删除【请填写功能名称】
+     *
+     * @param id 【请填写功能名称】主键
+     * @return 结果
+     */
+    int deleteFsDoctorConfirmById(Long id);
+
+    /**
+     * 批量删除【请填写功能名称】
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsDoctorConfirmByIds(Long[] ids);
+}

+ 67 - 0
fs-service/src/main/java/com/fs/his/mapper/FsQuestionAndAnswerMapper.java

@@ -0,0 +1,67 @@
+package com.fs.his.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsQuestionAndAnswer;
+import com.fs.his.vo.OptionsVO;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * 问答Mapper接口
+ *
+ * @author fs
+ * @date 2025-09-29
+ */
+public interface FsQuestionAndAnswerMapper extends BaseMapper<FsQuestionAndAnswer>{
+    /**
+     * 查询问答
+     *
+     * @param id 问答主键
+     * @return 问答
+     */
+    FsQuestionAndAnswer selectFsQuestionAndAnswerById(Long id);
+
+    /**
+     * 查询问答列表
+     *
+     * @param fsQuestionAndAnswer 问答
+     * @return 问答集合
+     */
+    List<FsQuestionAndAnswer> selectFsQuestionAndAnswerList(FsQuestionAndAnswer fsQuestionAndAnswer);
+
+    /**
+     * 新增问答
+     *
+     * @param fsQuestionAndAnswer 问答
+     * @return 结果
+     */
+    int insertFsQuestionAndAnswer(FsQuestionAndAnswer fsQuestionAndAnswer);
+
+    /**
+     * 修改问答
+     *
+     * @param fsQuestionAndAnswer 问答
+     * @return 结果
+     */
+    int updateFsQuestionAndAnswer(FsQuestionAndAnswer fsQuestionAndAnswer);
+
+    /**
+     * 删除问答
+     *
+     * @param id 问答主键
+     * @return 结果
+     */
+    int deleteFsQuestionAndAnswerById(Long id);
+
+    /**
+     * 批量删除问答
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsQuestionAndAnswerByIds(Long[] ids);
+
+    @Select("select id dictValue,question_name dictLabel from fs_question_and_answer  ")
+    List<OptionsVO> selectAllQuestionOptions();
+}

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

@@ -375,7 +375,7 @@ public interface FsStoreOrderMapper
             "LEFT JOIN company c on c.company_id =so.company_id " +
             "LEFT JOIN company_user cu on cu.user_id=so.company_user_id " +
             "LEFT JOIN fs_doctor fd on so.follow_doctor_id =fd.doctor_id " +
-            "LEFT JOIN fs_patient pat ON pat.patient_id=p.patient_id  WHERE so.is_del=0  and order_id= #{orderId}")
+            "LEFT JOIN fs_patient pat ON pat.patient_id=p.patient_id  WHERE so.is_del=0  and so.order_id= #{orderId}")
     FsStoreOrderVO selectFsStoreOrderByOrderIdVO(@Param("orderId") Long orderId);
 
     @Update("update fs_store_order set status=-3 where order_id=#{orderId}")

+ 95 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserInformationCollectionMapper.java

@@ -0,0 +1,95 @@
+package com.fs.his.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsUserInformationCollection;
+import com.fs.his.param.FsUserInformationCollectionListDParam;
+import com.fs.his.vo.FsUserInformationCollectionListDVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * 用户信息采集Mapper接口
+ *
+ * @author fs
+ * @date 2025-10-14
+ */
+public interface FsUserInformationCollectionMapper extends BaseMapper<FsUserInformationCollection>{
+    /**
+     * 查询用户信息采集
+     *
+     * @param id 用户信息采集主键
+     * @return 用户信息采集
+     */
+    FsUserInformationCollection selectFsUserInformationCollectionById(Long id);
+
+    FsUserInformationCollection selectFsUserInformationCollectionByUserId(Long userId);
+
+    /**
+     * 查询用户信息采集列表
+     *
+     * @param fsUserInformationCollection 用户信息采集
+     * @return 用户信息采集集合
+     */
+    List<FsUserInformationCollection> selectFsUserInformationCollectionList(FsUserInformationCollection fsUserInformationCollection);
+
+    /**
+     * 新增用户信息采集
+     *
+     * @param fsUserInformationCollection 用户信息采集
+     * @return 结果
+     */
+    int insertFsUserInformationCollection(FsUserInformationCollection fsUserInformationCollection);
+
+    /**
+     * 修改用户信息采集
+     *
+     * @param fsUserInformationCollection 用户信息采集
+     * @return 结果
+     */
+    int updateFsUserInformationCollection(FsUserInformationCollection fsUserInformationCollection);
+
+    @Select("UPDATE fs_user_information_collection SET package_order_code = NULL WHERE id = #{id}")
+    void collectionOderCodeNULL(@Param("id") Long id);
+    /**
+     * 删除用户信息采集
+     *
+     * @param id 用户信息采集主键
+     * @return 结果
+     */
+    int deleteFsUserInformationCollectionById(Long id);
+
+    /**
+     * 批量删除用户信息采集
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsUserInformationCollectionByIds(Long[] ids);
+
+    FsUserInformationCollection selectByUserId(Long userId);
+    FsUserInformationCollection selectByOrderCode(String orderCode);
+
+    @Select({"<script> " +
+            " SELECT uic.*,u.nickname userName FROM fs_user_information_collection uic LEFT JOIN fs_user u ON uic.user_id = u.user_id " +
+            "WHERE doctor_id = #{maps.doctorId} and is_package = 0  and user_confirm = 1 " +
+            "<if test='maps.doctorConfirm != null' > and uic.doctor_confirm = #{maps.doctorConfirm}  </if>" +
+            "<if test='maps.userConfirm != null' > and uic.user_confirm = #{maps.userConfirm}  </if>" +
+            "<if test='maps.userName != null ' > and u.nickname like concat('%',#{maps.userName},'%')</if>" +
+            "ORDER BY create_time desc" +
+            "</script>"})
+    List<FsUserInformationCollectionListDVO> selectFsUserInformationCollectionListDVO(@Param("maps") FsUserInformationCollectionListDParam param);
+
+    /**
+     * 查询需要自动退款订单(用户支付超过24小时,未第二次确认的订单号)
+     */
+    @Select("SELECT id,package_order_code FROM fs_user_information_collection uic LEFT JOIN fs_package_order po ON uic.package_order_code = po.order_sn  WHERE uic.doctor_confirm_time <= DATE_SUB(NOW(), INTERVAL 24 HOUR) and  uic.package_order_code IS NOT NULL   AND  uic.user_confirm2 = 0 AND po.is_pay = 1")
+    List<FsUserInformationCollection> selectAutoRefundOrderCode();
+
+    /**
+     * 查询需要自动确认订单(医生确认超过2小时,未第二次确认的信息采集)
+     */
+    @Select("SELECT * FROM fs_user_information_collection WHERE is_package = 0 AND user_confirm = 1 AND doctor_confirm = 1 AND user_confirm2 = 0 AND doctor_confirm_time <= DATE_SUB(NOW(), INTERVAL 2 HOUR)")
+    List<FsUserInformationCollection> selectAutoConfirm();
+}

+ 69 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserInformationCollectionScheduleMapper.java

@@ -0,0 +1,69 @@
+package com.fs.his.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsUserInformationCollectionSchedule;
+
+import java.util.List;
+
+/**
+ * 用户信息采集进度Mapper接口
+ *
+ * @author fs
+ * @date 2025-11-18
+ */
+public interface FsUserInformationCollectionScheduleMapper extends BaseMapper<FsUserInformationCollectionSchedule>{
+    /**
+     * 查询用户信息采集进度
+     *
+     * @param id 用户信息采集进度主键
+     * @return 用户信息采集进度
+     */
+    FsUserInformationCollectionSchedule selectFsUserInformationCollectionScheduleById(Long id);
+
+    /**
+     * 查询用户信息采集进度列表
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 用户信息采集进度集合
+     */
+    List<FsUserInformationCollectionSchedule> selectFsUserInformationCollectionScheduleList(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule);
+
+    /**
+     * 对于每条用户信息采集记录,只获取其最新的任务流程
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 用户信息采集进度集合
+     */
+    List<FsUserInformationCollectionSchedule> getUserRecordLatestTaskProcess(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule);
+    /**
+     * 新增用户信息采集进度
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 结果
+     */
+    int insertFsUserInformationCollectionSchedule(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule);
+
+    /**
+     * 修改用户信息采集进度
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 结果
+     */
+    int updateFsUserInformationCollectionSchedule(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule);
+
+    /**
+     * 删除用户信息采集进度
+     *
+     * @param id 用户信息采集进度主键
+     * @return 结果
+     */
+    int deleteFsUserInformationCollectionScheduleById(Long id);
+
+    /**
+     * 批量删除用户信息采集进度
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsUserInformationCollectionScheduleByIds(Long[] ids);
+}

+ 13 - 0
fs-service/src/main/java/com/fs/his/param/CollectionInfoConfirmParam.java

@@ -0,0 +1,13 @@
+package com.fs.his.param;
+
+import lombok.Data;
+
+@Data
+public class CollectionInfoConfirmParam {
+    private Long id;
+
+    private Long userId;
+
+    //用户建议
+    private String userAdvice;
+}

+ 16 - 0
fs-service/src/main/java/com/fs/his/param/FsUserInformationCollectionListDParam.java

@@ -0,0 +1,16 @@
+package com.fs.his.param;
+
+import com.fs.hisStore.param.BaseParam;
+import lombok.Data;
+
+@Data
+public class FsUserInformationCollectionListDParam extends BaseParam {
+    private String userName;
+
+    private Long doctorId;
+
+    //医生确认状态 0未确认 1已确认
+    private Integer doctorConfirm;
+
+    private Integer userConfirm;
+}

+ 48 - 0
fs-service/src/main/java/com/fs/his/param/FsUserInformationCollectionParam.java

@@ -0,0 +1,48 @@
+package com.fs.his.param;
+
+import com.fs.his.vo.AnswerVO;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class FsUserInformationCollectionParam {
+
+    private Long id;
+
+    private Long questionId;
+
+    private List<AnswerVO> answers;
+
+    private Long userId;
+
+    //销售id
+    private Long companyUserId;
+
+    //是否关联套餐包 0-不关联 1-关联
+    private Integer isPackage;
+
+    //套餐包id
+    private Long packageId;
+
+    //支付类型 0-全款 1-物流代收
+    private Integer payType;
+
+    //代收金额
+    private BigDecimal amount;
+
+    //用户性别 0女 1男
+    private Integer sex;
+    //用户姓名
+    private String userName;
+    //用户电话后四位
+    private String userPhoneFour;
+
+    //是否过敏
+    private String allergy;
+    //备注
+    private String remark;
+
+    private Long patientId;
+}

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

@@ -0,0 +1,64 @@
+package com.fs.his.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.his.domain.FsDoctorConfirm;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 【请填写功能名称】Service接口
+ *
+ * @author fs
+ * @date 2025-10-18
+ */
+@Service
+public interface IFsDoctorConfirmService extends IService<FsDoctorConfirm>{
+    /**
+     * 查询【请填写功能名称】
+     *
+     * @param id 【请填写功能名称】主键
+     * @return 【请填写功能名称】
+     */
+    FsDoctorConfirm selectFsDoctorConfirmById(Long id);
+
+    /**
+     * 查询【请填写功能名称】列表
+     *
+     * @param fsDoctorConfirm 【请填写功能名称】
+     * @return 【请填写功能名称】集合
+     */
+    List<FsDoctorConfirm> selectFsDoctorConfirmList(FsDoctorConfirm fsDoctorConfirm);
+
+    /**
+     * 新增【请填写功能名称】
+     *
+     * @param fsDoctorConfirm 【请填写功能名称】
+     * @return 结果
+     */
+    int insertFsDoctorConfirm(FsDoctorConfirm fsDoctorConfirm);
+
+    /**
+     * 修改【请填写功能名称】
+     *
+     * @param fsDoctorConfirm 【请填写功能名称】
+     * @return 结果
+     */
+    int updateFsDoctorConfirm(FsDoctorConfirm fsDoctorConfirm);
+
+    /**
+     * 批量删除【请填写功能名称】
+     *
+     * @param ids 需要删除的【请填写功能名称】主键集合
+     * @return 结果
+     */
+    int deleteFsDoctorConfirmByIds(Long[] ids);
+
+    /**
+     * 删除【请填写功能名称】信息
+     *
+     * @param id 【请填写功能名称】主键
+     * @return 结果
+     */
+    int deleteFsDoctorConfirmById(Long id);
+}

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

@@ -0,0 +1,66 @@
+package com.fs.his.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.his.domain.FsQuestionAndAnswer;
+import com.fs.his.vo.FsQuestionAndAnswerVO;
+import com.fs.his.vo.OptionsVO;
+
+import java.util.List;
+
+/**
+ * 问答Service接口
+ *
+ * @author fs
+ * @date 2025-09-29
+ */
+public interface IFsQuestionAndAnswerService extends IService<FsQuestionAndAnswer>{
+    /**
+     * 查询问答
+     *
+     * @param id 问答主键
+     * @return 问答
+     */
+    FsQuestionAndAnswerVO selectFsQuestionAndAnswerById(Long id);
+
+    /**
+     * 查询问答列表
+     *
+     * @param fsQuestionAndAnswer 问答
+     * @return 问答集合
+     */
+    List<FsQuestionAndAnswer> selectFsQuestionAndAnswerList(FsQuestionAndAnswer fsQuestionAndAnswer);
+
+    /**
+     * 新增问答
+     *
+     * @param fsQuestionAndAnswer 问答
+     * @return 结果
+     */
+    int insertFsQuestionAndAnswer(FsQuestionAndAnswer fsQuestionAndAnswer);
+
+    /**
+     * 修改问答
+     *
+     * @param fsQuestionAndAnswer 问答
+     * @return 结果
+     */
+    int updateFsQuestionAndAnswer(FsQuestionAndAnswer fsQuestionAndAnswer);
+
+    /**
+     * 批量删除问答
+     *
+     * @param ids 需要删除的问答主键集合
+     * @return 结果
+     */
+    int deleteFsQuestionAndAnswerByIds(Long[] ids);
+
+    /**
+     * 删除问答信息
+     *
+     * @param id 问答主键
+     * @return 结果
+     */
+    int deleteFsQuestionAndAnswerById(Long id);
+
+    List<OptionsVO> selectAllQuestionOptions();
+}

+ 70 - 0
fs-service/src/main/java/com/fs/his/service/IFsUserInformationCollectionScheduleService.java

@@ -0,0 +1,70 @@
+package com.fs.his.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.his.domain.FsUserInformationCollectionSchedule;
+
+import java.util.List;
+
+/**
+ * 用户信息采集进度Service接口
+ *
+ * @author fs
+ * @date 2025-11-18
+ */
+public interface IFsUserInformationCollectionScheduleService extends IService<FsUserInformationCollectionSchedule>{
+    /**
+     * 查询用户信息采集进度
+     *
+     * @param id 用户信息采集进度主键
+     * @return 用户信息采集进度
+     */
+    FsUserInformationCollectionSchedule selectFsUserInformationCollectionScheduleById(Long id);
+
+    /**
+     * 查询用户信息采集进度列表
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 用户信息采集进度集合
+     */
+    List<FsUserInformationCollectionSchedule> getUserRecordLatestTaskProcess(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule);
+
+    /**
+     * 查询用户信息采集进度列表
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 用户信息采集进度集合
+     */
+    List<FsUserInformationCollectionSchedule> selectFsUserInformationCollectionScheduleList(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule);
+
+    /**
+     * 新增用户信息采集进度
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 结果
+     */
+    int insertFsUserInformationCollectionSchedule(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule);
+
+    /**
+     * 修改用户信息采集进度
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 结果
+     */
+    int updateFsUserInformationCollectionSchedule(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule);
+
+    /**
+     * 批量删除用户信息采集进度
+     *
+     * @param ids 需要删除的用户信息采集进度主键集合
+     * @return 结果
+     */
+    int deleteFsUserInformationCollectionScheduleByIds(Long[] ids);
+
+    /**
+     * 删除用户信息采集进度信息
+     *
+     * @param id 用户信息采集进度主键
+     * @return 结果
+     */
+    int deleteFsUserInformationCollectionScheduleById(Long id);
+}

+ 94 - 0
fs-service/src/main/java/com/fs/his/service/IFsUserInformationCollectionService.java

@@ -0,0 +1,94 @@
+package com.fs.his.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.his.domain.FsUserInformationCollection;
+import com.fs.his.param.CollectionInfoConfirmParam;
+import com.fs.his.param.FsUserInformationCollectionListDParam;
+import com.fs.his.param.FsUserInformationCollectionParam;
+import com.fs.his.vo.FsUserInfoCollectionUVO;
+import com.fs.his.vo.FsUserInformationCollectionListDVO;
+import com.fs.his.vo.FsUserInformationCollectionVO;
+
+import java.util.List;
+
+/**
+ * 用户信息采集Service接口
+ *
+ * @author fs
+ * @date 2025-10-14
+ */
+public interface IFsUserInformationCollectionService extends IService<FsUserInformationCollection>{
+    /**
+     * 查询用户信息采集
+     *
+     * @param id 用户信息采集主键
+     * @return 用户信息采集
+     */
+    FsUserInformationCollection selectFsUserInformationCollectionById(Long id);
+
+    /**
+     * 查询用户信息采集列表
+     *
+     * @param fsUserInformationCollection 用户信息采集
+     * @return 用户信息采集集合
+     */
+    List<FsUserInformationCollection> selectFsUserInformationCollectionList(FsUserInformationCollection fsUserInformationCollection);
+
+    /**
+     * 新增用户信息采集
+     *
+     * @param fsUserInformationCollection 用户信息采集
+     * @return 结果
+     */
+    Long insertFsUserInformationCollection(FsUserInformationCollectionParam fsUserInformationCollection);
+
+    /**
+     * 修改用户信息采集
+     *
+     * @param fsUserInformationCollection 用户信息采集
+     * @return 结果
+     */
+    Long updateFsUserInformationCollection(FsUserInformationCollectionParam fsUserInformationCollection);
+
+    /**
+     * 批量删除用户信息采集
+     *
+     * @param ids 需要删除的用户信息采集主键集合
+     * @return 结果
+     */
+    int deleteFsUserInformationCollectionByIds(Long[] ids);
+
+    /**
+     * 删除用户信息采集信息
+     *
+     * @param id 用户信息采集主键
+     * @return 结果
+     */
+    int deleteFsUserInformationCollectionById(Long id);
+
+
+    FsUserInformationCollectionVO getInfo(FsUserInformationCollection fsUserInformationCollection);
+
+    /**
+     * 查询医生端用户信息采集列表(只查询未关联套餐包的信息采集)
+     * @param param
+     * @return
+     */
+    List<FsUserInformationCollectionListDVO> selectFsUserInformationCollectionListDVO(FsUserInformationCollectionListDParam param);
+
+    FsUserInformationCollectionVO detail(Long id);
+
+    R getWxaCodePackageOrderUnLimit(Long collectionId);
+
+    R doctorConfirm(FsUserInformationCollection collection);
+
+    FsUserInfoCollectionUVO info(Long id, Long userId);
+
+    R userConfirm(CollectionInfoConfirmParam param);
+
+    //自动退款
+    void autoRefund(FsUserInformationCollection collection);
+
+    FsUserInformationCollectionVO getCollectionByUserId(Long userId);
+}

+ 95 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsDoctorConfirmServiceImpl.java

@@ -0,0 +1,95 @@
+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.FsDoctorConfirm;
+import com.fs.his.mapper.FsDoctorConfirmMapper;
+import com.fs.his.service.IFsDoctorConfirmService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+
+/**
+ * 【请填写功能名称】Service业务层处理
+ *
+ * @author fs
+ * @date 2025-10-18
+ */
+@Service
+public class FsDoctorConfirmServiceImpl extends ServiceImpl<FsDoctorConfirmMapper, FsDoctorConfirm> implements IFsDoctorConfirmService {
+
+    /**
+     * 查询【请填写功能名称】
+     *
+     * @param id 【请填写功能名称】主键
+     * @return 【请填写功能名称】
+     */
+    @Override
+    public FsDoctorConfirm selectFsDoctorConfirmById(Long id)
+    {
+        return baseMapper.selectFsDoctorConfirmById(id);
+    }
+
+    /**
+     * 查询【请填写功能名称】列表
+     *
+     * @param fsDoctorConfirm 【请填写功能名称】
+     * @return 【请填写功能名称】
+     */
+    @Override
+    public List<FsDoctorConfirm> selectFsDoctorConfirmList(FsDoctorConfirm fsDoctorConfirm)
+    {
+        return baseMapper.selectFsDoctorConfirmList(fsDoctorConfirm);
+    }
+
+    /**
+     * 新增【请填写功能名称】
+     *
+     * @param fsDoctorConfirm 【请填写功能名称】
+     * @return 结果
+     */
+    @Override
+    public int insertFsDoctorConfirm(FsDoctorConfirm fsDoctorConfirm)
+    {
+        fsDoctorConfirm.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertFsDoctorConfirm(fsDoctorConfirm);
+    }
+
+    /**
+     * 修改【请填写功能名称】
+     *
+     * @param fsDoctorConfirm 【请填写功能名称】
+     * @return 结果
+     */
+    @Override
+    public int updateFsDoctorConfirm(FsDoctorConfirm fsDoctorConfirm)
+    {
+        fsDoctorConfirm.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateFsDoctorConfirm(fsDoctorConfirm);
+    }
+
+    /**
+     * 批量删除【请填写功能名称】
+     *
+     * @param ids 需要删除的【请填写功能名称】主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsDoctorConfirmByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsDoctorConfirmByIds(ids);
+    }
+
+    /**
+     * 删除【请填写功能名称】信息
+     *
+     * @param id 【请填写功能名称】主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsDoctorConfirmById(Long id)
+    {
+        return baseMapper.deleteFsDoctorConfirmById(id);
+    }
+}

+ 113 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsQuestionAndAnswerServiceImpl.java

@@ -0,0 +1,113 @@
+package com.fs.his.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.DateUtils;
+import com.fs.his.domain.FsQuestionAndAnswer;
+import com.fs.his.mapper.FsQuestionAndAnswerMapper;
+import com.fs.his.service.IFsQuestionAndAnswerService;
+import com.fs.his.vo.AnswerVO;
+import com.fs.his.vo.FsQuestionAndAnswerVO;
+import com.fs.his.vo.OptionsVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 问答Service业务层处理
+ *
+ * @author fs
+ * @date 2025-09-29
+ */
+@Service
+public class FsQuestionAndAnswerServiceImpl extends ServiceImpl<FsQuestionAndAnswerMapper, FsQuestionAndAnswer> implements IFsQuestionAndAnswerService {
+
+
+    @Autowired
+    private FsQuestionAndAnswerMapper fsQuestionAndAnswerMapper;
+    /**
+     * 查询问答
+     *
+     * @param id 问答主键
+     * @return 问答
+     */
+    @Override
+    public FsQuestionAndAnswerVO selectFsQuestionAndAnswerById(Long id)
+    {
+        FsQuestionAndAnswer fsQuestionAndAnswer = baseMapper.selectFsQuestionAndAnswerById(id);
+        FsQuestionAndAnswerVO vo = new FsQuestionAndAnswerVO();
+        vo.setQuestionName(fsQuestionAndAnswer.getQuestionName());
+        List<AnswerVO> answerVOS = JSON.parseArray(fsQuestionAndAnswer.getJsonInfo(), AnswerVO.class);
+        vo.setAnswers(answerVOS);
+        vo.setId(fsQuestionAndAnswer.getId());
+        return vo;
+    }
+
+    /**
+     * 查询问答列表
+     *
+     * @param fsQuestionAndAnswer 问答
+     * @return 问答
+     */
+    @Override
+    public List<FsQuestionAndAnswer> selectFsQuestionAndAnswerList(FsQuestionAndAnswer fsQuestionAndAnswer)
+    {
+        return baseMapper.selectFsQuestionAndAnswerList(fsQuestionAndAnswer);
+    }
+
+    /**
+     * 新增问答
+     *
+     * @param fsQuestionAndAnswer 问答
+     * @return 结果
+     */
+    @Override
+    public int insertFsQuestionAndAnswer(FsQuestionAndAnswer fsQuestionAndAnswer)
+    {
+        fsQuestionAndAnswer.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertFsQuestionAndAnswer(fsQuestionAndAnswer);
+    }
+
+    /**
+     * 修改问答
+     *
+     * @param fsQuestionAndAnswer 问答
+     * @return 结果
+     */
+    @Override
+    public int updateFsQuestionAndAnswer(FsQuestionAndAnswer fsQuestionAndAnswer)
+    {
+        fsQuestionAndAnswer.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateFsQuestionAndAnswer(fsQuestionAndAnswer);
+    }
+
+    /**
+     * 批量删除问答
+     *
+     * @param ids 需要删除的问答主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsQuestionAndAnswerByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsQuestionAndAnswerByIds(ids);
+    }
+
+    /**
+     * 删除问答信息
+     *
+     * @param id 问答主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsQuestionAndAnswerById(Long id)
+    {
+        return baseMapper.deleteFsQuestionAndAnswerById(id);
+    }
+
+    @Override
+    public List<OptionsVO> selectAllQuestionOptions() {
+        return fsQuestionAndAnswerMapper.selectAllQuestionOptions();
+    }
+}

+ 105 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserInformationCollectionScheduleServiceImpl.java

@@ -0,0 +1,105 @@
+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.FsUserInformationCollectionSchedule;
+import com.fs.his.mapper.FsUserInformationCollectionScheduleMapper;
+import com.fs.his.service.IFsUserInformationCollectionScheduleService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 用户信息采集进度Service业务层处理
+ *
+ * @author fs
+ * @date 2025-11-18
+ */
+@Service
+public class FsUserInformationCollectionScheduleServiceImpl extends ServiceImpl<FsUserInformationCollectionScheduleMapper, FsUserInformationCollectionSchedule> implements IFsUserInformationCollectionScheduleService {
+
+    /**
+     * 查询用户信息采集进度
+     *
+     * @param id 用户信息采集进度主键
+     * @return 用户信息采集进度
+     */
+    @Override
+    public FsUserInformationCollectionSchedule selectFsUserInformationCollectionScheduleById(Long id)
+    {
+        return baseMapper.selectFsUserInformationCollectionScheduleById(id);
+    }
+
+    /**
+     * 对于每条用户信息采集记录,只获取其最新的任务流程
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 用户信息采集进度
+     */
+    @Override
+    public List<FsUserInformationCollectionSchedule> getUserRecordLatestTaskProcess(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule)
+    {
+        return baseMapper.getUserRecordLatestTaskProcess(fsUserInformationCollectionSchedule);
+    }
+
+    /**
+     * 查询用户信息采集进度列表
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 用户信息采集进度
+     */
+    @Override
+    public List<FsUserInformationCollectionSchedule> selectFsUserInformationCollectionScheduleList(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule)
+    {
+        return baseMapper.getUserRecordLatestTaskProcess(fsUserInformationCollectionSchedule);
+    }
+
+    /**
+     * 新增用户信息采集进度
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 结果
+     */
+    @Override
+    public int insertFsUserInformationCollectionSchedule(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule)
+    {
+        fsUserInformationCollectionSchedule.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertFsUserInformationCollectionSchedule(fsUserInformationCollectionSchedule);
+    }
+
+    /**
+     * 修改用户信息采集进度
+     *
+     * @param fsUserInformationCollectionSchedule 用户信息采集进度
+     * @return 结果
+     */
+    @Override
+    public int updateFsUserInformationCollectionSchedule(FsUserInformationCollectionSchedule fsUserInformationCollectionSchedule)
+    {
+        return baseMapper.updateFsUserInformationCollectionSchedule(fsUserInformationCollectionSchedule);
+    }
+
+    /**
+     * 批量删除用户信息采集进度
+     *
+     * @param ids 需要删除的用户信息采集进度主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserInformationCollectionScheduleByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsUserInformationCollectionScheduleByIds(ids);
+    }
+
+    /**
+     * 删除用户信息采集进度信息
+     *
+     * @param id 用户信息采集进度主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserInformationCollectionScheduleById(Long id)
+    {
+        return baseMapper.deleteFsUserInformationCollectionScheduleById(id);
+    }
+}

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

@@ -0,0 +1,902 @@
+package com.fs.his.service.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.company.service.ICompanyService;
+import com.fs.core.config.WxMaConfiguration;
+import com.fs.core.config.WxPayProperties;
+import com.fs.core.utils.OrderCodeUtils;
+import com.fs.his.config.FsSysConfig;
+import com.fs.his.domain.*;
+import com.fs.his.enums.FsStoreOrderStatusEnum;
+import com.fs.his.mapper.*;
+import com.fs.his.param.CollectionInfoConfirmParam;
+import com.fs.his.param.FsUserInformationCollectionListDParam;
+import com.fs.his.param.FsUserInformationCollectionParam;
+import com.fs.his.service.*;
+import com.fs.his.utils.ConfigUtil;
+import com.fs.his.vo.*;
+import com.fs.huifuPay.domain.HuiFuRefundResult;
+import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentDelaytransConfirmrefundRequest;
+import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayRefundRequest;
+import com.fs.huifuPay.service.HuiFuService;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import com.fs.system.oss.CloudStorageService;
+import com.fs.system.oss.OSSFactory;
+import com.fs.system.service.ISysConfigService;
+import com.fs.tzBankPay.TzBankService.TzBankService;
+import com.fs.tzBankPay.doman.RefundParam;
+import com.fs.tzBankPay.doman.RefundResult;
+import com.fs.tzBankPay.doman.TzBankResult;
+import com.fs.ybPay.dto.RefundDTO;
+import com.fs.ybPay.service.IPayService;
+import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
+import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult;
+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.collect.Lists;
+import com.google.gson.Gson;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 用户信息采集Service业务层处理
+ *
+ * @author fs
+ * @date 2025-10-14
+ */
+@Service
+public class FsUserInformationCollectionServiceImpl extends ServiceImpl<FsUserInformationCollectionMapper, FsUserInformationCollection> implements IFsUserInformationCollectionService {
+    Logger logger= LoggerFactory.getLogger(getClass());
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    SysConfigMapper sysConfigMapper;
+
+    @Autowired
+    private WxPayProperties wxPayProperties;
+
+    @Autowired
+    private WxPayService wxPayService;
+
+    @Autowired
+    IPayService payService;
+
+    @Autowired
+    private TzBankService tzBankService;
+
+    @Autowired
+    private HuiFuService huiFuService;
+
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
+
+    @Autowired
+    private FsDoctorMapper doctorMapper;
+
+    @Autowired
+    private IFsDoctorService iFsDoctorService;
+
+    @Autowired
+    private IFsQuestionAndAnswerService questionAndAnswerService;
+
+    @Autowired
+    private FsUserInformationCollectionMapper fsUserInformationCollectionMapper;
+
+    @Autowired
+    private ConfigUtil configUtil;
+
+    @Autowired
+    private IFsDoctorConfirmService doctorConfirmService;
+
+    @Autowired
+    RedisCache redisCache;
+
+    @Autowired
+    private FsPackageMapper packageMapper;
+
+    @Autowired
+    private IFsStoreOrderService storeOrderService;
+
+    @Autowired
+    private FsPackageOrderMapper packageOrderMapper;
+
+    @Autowired
+    private FsPrescribeMapper fsPrescribeMapper;
+
+    @Autowired
+    private IFsPrescribeService prescribeService;
+
+    @Autowired
+    private ICompanyService companyService;
+
+    @Autowired
+    private FsStorePaymentMapper fsStorePaymentMapper;
+
+    @Autowired
+    private FsStoreOrderLogsMapper fsStoreOrderLogsMapper;
+    @Autowired
+    private DoctorMsgMapper doctorMsgMapper;
+    /**
+     * 查询用户信息采集
+     *
+     * @param id 用户信息采集主键
+     * @return 用户信息采集
+     */
+    @Override
+    public FsUserInformationCollection selectFsUserInformationCollectionById(Long id)
+    {
+        return baseMapper.selectFsUserInformationCollectionById(id);
+    }
+
+    /**
+     * 查询用户信息采集列表
+     *
+     * @param fsUserInformationCollection 用户信息采集
+     * @return 用户信息采集
+     */
+    @Override
+    public List<FsUserInformationCollection> selectFsUserInformationCollectionList(FsUserInformationCollection fsUserInformationCollection)
+    {
+        return baseMapper.selectFsUserInformationCollectionList(fsUserInformationCollection);
+    }
+
+    /**
+     * 新增用户信息采集
+     *
+     * @param param 用户信息采集
+     * @return 结果
+     */
+    @Override
+    public Long insertFsUserInformationCollection(FsUserInformationCollectionParam param)
+    {
+
+        FsUserInformationCollection fsUserInformationCollection = getFsUserInformationCollection(param);
+        fsUserInformationCollection.setCreateTime(DateUtils.getNowDate());
+        baseMapper.insertFsUserInformationCollection(fsUserInformationCollection);
+        return fsUserInformationCollection.getId();
+    }
+
+    /**
+     * 修改用户信息采集
+     *
+     * @param param 用户信息采集
+     * @return 结果
+     */
+    @Override
+    public Long updateFsUserInformationCollection(FsUserInformationCollectionParam param)
+    {
+        if (param.getId() == null || param.getId() < 1) {
+            throw new CustomException("参数错误");
+        }
+        FsUserInformationCollection collection = fsUserInformationCollectionMapper.selectFsUserInformationCollectionById(param.getId());
+        if (collection == null) {
+            throw new CustomException("没有用户采集信息");
+        }
+        if (collection.getUserConfirm() == 1 && collection.getUserConfirm2() == 0) {
+            throw new CustomException("确认中,暂无法修改");
+        }
+        //清空订单号
+        fsUserInformationCollectionMapper.collectionOderCodeNULL(param.getId());
+        FsUserInformationCollection fsUserInformationCollection = getFsUserInformationCollection(param);
+        fsUserInformationCollection.setUserConfirm2(0);
+        fsUserInformationCollection.setUserConfirm(0);
+        fsUserInformationCollection.setDoctorConfirm(0);
+        fsUserInformationCollection.setStatus(1);
+        fsUserInformationCollection.setUpdateTime(DateUtils.getNowDate());
+        baseMapper.updateFsUserInformationCollection(fsUserInformationCollection);
+        return fsUserInformationCollection.getId();
+    }
+
+    /**
+     * 批量删除用户信息采集
+     *
+     * @param ids 需要删除的用户信息采集主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserInformationCollectionByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsUserInformationCollectionByIds(ids);
+    }
+
+    /**
+     * 删除用户信息采集信息
+     *
+     * @param id 用户信息采集主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserInformationCollectionById(Long id)
+    {
+        return baseMapper.deleteFsUserInformationCollectionById(id);
+    }
+
+    @Override
+    public FsUserInformationCollectionVO getInfo(FsUserInformationCollection map) {
+        FsUserInformationCollectionVO vo = new FsUserInformationCollectionVO();
+        Long userId = map.getUserId();
+        Long questionId = map.getQuestionId();
+//        FsUserInformationCollection fsUserInformationCollection = baseMapper.selectByUserId(userId);
+        FsUserInformationCollection fsUserInformationCollection = null;
+        if (map.getId() != null){
+            fsUserInformationCollection = baseMapper.selectFsUserInformationCollectionById(map.getId());
+        }
+        if (fsUserInformationCollection != null) {
+            //组装返回数据
+            vo.setQuestionId(fsUserInformationCollection.getQuestionId());
+            vo.setId(fsUserInformationCollection.getId());
+            vo.setAmount(fsUserInformationCollection.getAmount());
+            vo.setPackageId(fsUserInformationCollection.getPackageId());
+            vo.setPayType(fsUserInformationCollection.getPayType());
+            vo.setIsPackage(fsUserInformationCollection.getIsPackage());
+            vo.setSex(fsUserInformationCollection.getSex());
+            vo.setUserPhoneFour(fsUserInformationCollection.getUserPhoneFour());
+            vo.setUserName(fsUserInformationCollection.getUserName());
+            vo.setRemark(fsUserInformationCollection.getRemark());
+            vo.setAllergy(fsUserInformationCollection.getAllergy());
+            if (map.getQuestionId() == null) {
+                FsQuestionAndAnswerVO questionAndAnswerVO = questionAndAnswerService.selectFsQuestionAndAnswerById(fsUserInformationCollection.getQuestionId());
+                List<AnswerVO> answerVOS = JSON.parseArray(fsUserInformationCollection.getJsonInfo(), AnswerVO.class);
+                List<AnswerVO> collect = getAnswerVOs(answerVOS, questionAndAnswerVO.getAnswers());
+                vo.setAnswers(collect);
+            } else {
+                vo.setAnswers(JSON.parseArray(fsUserInformationCollection.getJsonInfo(), AnswerVO.class));
+            }
+        }
+        if (questionId != null) {
+            vo.setQuestionId(questionId);
+            //问答数据
+            FsQuestionAndAnswerVO questionAndAnswerVO = questionAndAnswerService.selectFsQuestionAndAnswerById(questionId);
+            if (questionAndAnswerVO != null) {
+                questionAndAnswerVO.getAnswers().forEach(answerVO -> {
+                    answerVO.setFlag(false);
+                });
+                if (fsUserInformationCollection != null) {
+                    vo.getAnswers().addAll(questionAndAnswerVO.getAnswers());
+                    vo.setAnswers(getAnswerVOs(vo.getAnswers(),questionAndAnswerVO.getAnswers()));
+                } else {
+                    //返回问答
+                    vo.setAnswers(questionAndAnswerVO.getAnswers());
+                }
+            }
+        }
+        if (ObjectUtil.isNotEmpty(vo.getAnswers())){
+            vo.getAnswers().forEach(answerVO -> {
+                if (answerVO.getSort() == null) {
+                    if (answerVO.getTitle().contains("糖尿病")) {
+                        answerVO.setSort(1);
+                    } else if (answerVO.getTitle().contains("心脑血管")) {
+                        answerVO.setSort(2);
+                    } else if (answerVO.getTitle().contains("风湿骨病")) {
+                        answerVO.setSort(3);
+                    } else if (answerVO.getTitle().contains("肝胆")) {
+                        answerVO.setSort(4);
+                    } else if (answerVO.getTitle().contains("其他")) {
+                        answerVO.setSort(5);
+                    } else if (answerVO.getTitle().contains("特殊")) {
+                        answerVO.setSort(6);
+                    }
+                }
+                if(answerVO.getFlag() == null) {
+                    answerVO.setFlag(false);
+                }
+                if (answerVO.getValue() == null) {
+                    answerVO.setValue(new ArrayList<>());
+                }
+            });
+            //有值且为true排前面
+//            List<AnswerVO> collect = vo.getAnswers().stream()
+//                    .sorted(Comparator.comparing(AnswerVO::getValue, Comparator.nullsFirst(Integer::compareTo)).reversed()).collect(Collectors.toList())
+//                    .stream()
+//                    .sorted((a,b)-> Boolean.compare(b.getFlag(),a.getFlag())).collect(Collectors.toList());
+            //List<AnswerVO> collect = vo.getAnswers().stream()
+            //        .sorted(Comparator.comparing(AnswerVO::getValue, Comparator.nullsFirst(List::indexOf)).reversed()).collect(Collectors.toList())
+            //        .stream()
+            //        .sorted((a,b)-> Boolean.compare(b.getFlag(),a.getFlag())).collect(Collectors.toList());
+            List<AnswerVO> collect = vo.getAnswers().stream()
+                    .sorted(Comparator.comparing(AnswerVO::getSort)).collect(Collectors.toList());
+
+            vo.setAnswers(collect);
+        }
+        return vo;
+    }
+
+    @Override
+    public List<FsUserInformationCollectionListDVO> selectFsUserInformationCollectionListDVO(FsUserInformationCollectionListDParam param) {
+        return fsUserInformationCollectionMapper.selectFsUserInformationCollectionListDVO(param);
+    }
+
+    @Override
+    public FsUserInformationCollectionVO detail(Long id) {
+        FsUserInformationCollectionVO vo = new FsUserInformationCollectionVO();
+        FsUserInformationCollection collection = fsUserInformationCollectionMapper.selectFsUserInformationCollectionById(id);
+        if (collection != null) {
+            vo.setAllergy(collection.getAllergy());
+            vo.setRemark(collection.getRemark());
+            vo.setAnswers(JSON.parseArray(collection.getJsonInfo(), AnswerVO.class));
+            List<AnswerVO> answers = vo.getAnswers();
+            if (!CollectionUtils.isEmpty(answers)){
+                List<AnswerVO> collect = answers
+                        .stream()
+                        .filter(answerVO -> !answerVO.getValue().isEmpty())
+                        .collect(Collectors.toList());
+                if (!CollectionUtils.isEmpty(collect)) {
+                    collect.forEach(answerVO -> {
+                        List<Integer> value = answerVO.getValue();
+                        List<AnswerVO.Options> options = answerVO.getOptions();
+                        List<AnswerVO.Options> filterOptions = Lists.newArrayList();
+                        for (AnswerVO.Options option : options) {
+                            if (value.contains(option.getValue())) {
+                                filterOptions.add(option);
+                            }
+                        }
+                        answerVO.setOptions(filterOptions);
+                    });
+                }
+                vo.setAnswers(collect);
+            }
+        }
+        return vo;
+    }
+
+    @Override
+    public R getWxaCodePackageOrderUnLimit(Long collectionId) {
+        logger.info("生成用户信息采集二维码");
+        FsSysConfig sysConfig = configUtil.getSysConfig();
+
+        final WxMaService wxMaService = WxMaConfiguration.getMaService(sysConfig.getAppid());
+
+        String scene="collectionId="+ collectionId ;
+        byte[] file;
+        try {
+            file = wxMaService.getQrcodeService().createWxaCodeUnlimitBytes(
+                    scene,
+                    "pages_order/confirmUserinfo",
+                    true,
+                    "release",//release trial
+                    430,
+                    true,
+                    null,
+                    false);
+
+            // 上传图片到存储桶
+            String suffix = ".png";
+            CloudStorageService storage = OSSFactory.build();
+            String url;
+            try {
+                url = storage.uploadSuffix(file, suffix);
+            }  catch (Exception e) {
+                // 记录错误日志
+                logger.error("生成图片失败:{}",e.getMessage(),e);
+                return R.error("生成图片失败");
+            }
+            // 返回成功信息
+            return R.ok().put("url",url);
+
+        } catch (WxErrorException e) {
+            logger.error(e.getMessage(), e);
+            return R.error("微信接口调用失败: " + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional
+    public R doctorConfirm(FsUserInformationCollection collection) {
+        FsUserInformationCollection fsUserInformationCollection = fsUserInformationCollectionMapper.selectFsUserInformationCollectionById(collection.getId());
+        if (fsUserInformationCollection == null) {
+            return R.error("没有用户采集信息");
+        }
+        if (fsUserInformationCollection.getUserConfirm() == 0){
+            return R.error("用户未确认");
+        }
+        if (fsUserInformationCollection.getDoctorConfirm() == 1){
+            return R.error("医生已确认");
+        }
+        List<AnswerVO> answerVOS = null;
+        //医生确认确认状态为true(用于封档)
+        if (StringUtils.isNotEmpty(fsUserInformationCollection.getJsonInfo()) ){
+            answerVOS = JSONArray.parseArray(fsUserInformationCollection.getJsonInfo(), AnswerVO.class);
+            answerVOS.forEach(answerVO -> {
+                answerVO.setFlag(true);
+            });
+        }
+        FsUserInformationCollection map = new FsUserInformationCollection();
+        map.setId(collection.getId());
+        map.setDoctorAdvice(collection.getDoctorAdvice());
+        map.setDoctorConfirm(1);
+        //医生确认时间
+        map.setDoctorConfirmTime(DateUtils.getNowDate());
+        if (answerVOS != null && !answerVOS.isEmpty()) {
+            map.setJsonInfo(JSON.toJSONString(answerVOS));
+        }
+//        if (fsUserInformationCollection.getIsPackage() == 1) {
+//            //插入生成处方签定时任务数据
+//            if(fsUserInformationCollection.getPackageOrderCode() != null) {
+//                //套餐包订单
+//                FsPackageOrder fsPackageOrder = packageOrderMapper.selectByOderCode(fsUserInformationCollection.getPackageOrderCode());
+//                if (fsPackageOrder != null && fsPackageOrder.getIsPay() == 1) {
+//                    //套餐包信息
+//                    FsPackage fsPackage = JSON.parseObject(fsPackageOrder.getPackageJson(), FsPackage.class);
+//
+//                    //套餐包为药品生成处方信息
+//                    if(fsPackage != null && (fsPackage.getProductType() == 1 || fsPackage.getProductType() == 2 || fsPackage.getProductType() == 3)) {
+//                        FsStoreOrder fsStoreOrder = storeOrderService.selectFsStoreOrderByOrderCode(fsUserInformationCollection.getPackageOrderCode());
+//                        if (fsStoreOrder != null && fsStoreOrder.getIsPay() == 1 ) {
+//                            if (fsStoreOrder.getPrescribeId() != null) {
+////                                FsPrescribeParam fsPrescribeParam = new FsPrescribeParam();
+////                                fsPrescribeParam.setPrescribeId(fsStoreOrder.getPrescribeId());
+////                                prescribeService.confirmPrescribe(fsPrescribeParam);
+//                                prescribeService.collectionPrescribe(fsStoreOrder.getPrescribeId());
+//                            }
+//                        } else {
+//                            throw new CustomException("生成处方信息中......");
+//                        }
+//                    }
+//                }
+//            }
+//        } else {
+//            //没有关联套餐包医生确认清空所有状态
+//            map.setUserConfirm2(1);
+//        }
+
+        if (fsUserInformationCollectionMapper.updateFsUserInformationCollection(map) > 0) {
+
+            //更新医生确认信息
+            FsDoctorConfirm doctorConfirm = redisCache.getCacheObject("doctorConfirm:" + collection.getId());
+            if(doctorConfirm == null) {
+                return R.error("没有医生确认信息");
+            }
+            doctorConfirm.setDoctorEndTime(DateUtils.getNowDate());
+            Long seconds = DateUtil.between(doctorConfirm.getStartTime(), doctorConfirm.getDoctorEndTime(), DateUnit.SECOND);
+            doctorConfirm.setConfirmSecond(seconds);
+            doctorConfirmService.updateFsDoctorConfirm(doctorConfirm);
+            redisCache.deleteObject("doctorConfirm:" + collection.getId());
+
+
+            return R.ok();
+        }
+        return R.error("医生确认失败");
+    }
+
+    @Override
+    public FsUserInfoCollectionUVO info(Long id, Long userId) {
+        FsUserInfoCollectionUVO vo = new FsUserInfoCollectionUVO();
+        FsUserInformationCollection collection = fsUserInformationCollectionMapper.selectFsUserInformationCollectionById(id);
+
+        if (collection != null) {
+
+//            if (!Objects.equals(collection.getUserId(), userId)) {
+//                return vo;
+//            }
+            if (collection.getIsPackage() == 1) {
+                vo.setPackageId(collection.getPackageId());
+                vo.setPayType(collection.getPayType());
+                vo.setAmount(collection.getAmount());
+                FsPackage fsPackage = new FsPackage();
+                FsPackage fsPackage1 = packageMapper.selectFsPackageByPackageId(collection.getPackageId());
+                if (fsPackage1 != null) {
+                    fsPackage = fsPackage1;
+                }
+                vo.setFsPackage(fsPackage);
+                CompanyUser companyUser = companyUserMapper.selectCompanyUserById(collection.getCompanyUserId());
+                if (companyUser == null) {
+                    throw new CustomException("销售不存在");
+                }
+                vo.setCompanyUserId(collection.getCompanyUserId());
+                vo.setCompanyId(companyUser.getCompanyId());
+                vo.setPackageOrderCode(collection.getPackageOrderCode());
+                FsPackageOrder fsPackageOrder = packageOrderMapper.selectFsPackageOrderByOrderSn(collection.getPackageOrderCode());
+                if(fsPackageOrder!=null) {
+                    vo.setOrderStatus(fsPackageOrder.getIsPay());
+                    vo.setOrderId(fsPackageOrder.getOrderId());
+                    vo.setPatientJson(fsPackageOrder.getPatientJson());
+                    if((fsPackage.getProductType() == 1 || fsPackage.getProductType() == 2 || fsPackage.getProductType() == 3) && fsPackageOrder.getIsPay() == 1) {
+                        //套餐包为药品且已支付的查询处方订单拿到处方签
+                        FsStoreOrder fsStoreOrder = storeOrderService.selectFsStoreOrderByOrderCode(collection.getPackageOrderCode());
+                        if (fsStoreOrder != null) {
+                            Long prescribeId = fsStoreOrder.getPrescribeId();
+                            FsPrescribe fsPrescribe = fsPrescribeMapper.selectFsPrescribeByPrescribeId(prescribeId);
+                            if (fsPrescribe != null) {
+                                vo.setPrescribeImgUrl(fsPrescribe.getPrescribeImgUrl());
+                                vo.setPrescribeStatus(fsPrescribe.getStatus());
+                            }
+                        } else {
+                            throw new CustomException("没有处方订单");
+                        }
+                    }
+                }
+            }
+            vo.setIsPackage(collection.getIsPackage());
+            vo.setId(collection.getId());
+            vo.setAnswers(JSON.parseArray(collection.getJsonInfo(), AnswerVO.class));
+            vo.setStatus(collection.getStatus());
+            vo.setUserConfirm(collection.getUserConfirm());
+            vo.setUserConfirm2(collection.getUserConfirm2());
+            vo.setDoctorConfirm(collection.getDoctorConfirm());
+            vo.setUserAdvice(collection.getUserAdvice());
+            vo.setDoctorAdvice(collection.getDoctorAdvice());
+            vo.setDoctor(doctorMapper.selectFsDoctorByDoctorId(collection.getDoctorId()));
+            vo.setAllergy(collection.getAllergy());
+            vo.setRemark(collection.getRemark());
+            vo.setSex(collection.getSex());
+            vo.setUserPhoneFour(collection.getUserPhoneFour());
+            vo.setUserName(collection.getUserName());
+        }
+        return vo;
+    }
+
+    @Override
+    @Transactional
+    public R userConfirm(CollectionInfoConfirmParam param) {
+
+        if (param.getId() == null) {
+            return R.error("参数错误");
+        }
+        FsUserInformationCollection collection = fsUserInformationCollectionMapper.selectFsUserInformationCollectionById(param.getId());
+        if (collection == null) {
+            return R.error("没有用户采集信息");
+        }
+        if (collection.getStatus() == 1 ){
+            if (collection.getUserConfirm() == 1){
+                return R.error("用户已确认");
+            }
+        }
+        if (!Objects.equals(collection.getUserId(), param.getUserId())) {
+            return R.error("用户信息不匹配,无法确认");
+        }
+        FsUserInformationCollection map = new FsUserInformationCollection();
+        map.setId(param.getId());
+        map.setUserConfirm(1);
+        map.setUserAdvice(param.getUserAdvice());
+        if (collection.getStatus() == 1) {
+            //用户第一次确认添加医生消息
+            DoctorMsg msg = new DoctorMsg();
+            String name = collection.getUserName() != null ? collection.getUserName() : "-";
+            //没套餐包的添加医生信息采集建议消息
+            msg.setTitle("用户采集信息建议");
+            msg.setType(3);
+            msg.setContent("患者:" + name + "的信息采集,前往建议");
+            msg.setDoctorId(collection.getDoctorId());
+            msg.setCreateTime(DateUtils.getNowDate());
+            //插入医生消息
+            doctorMsgMapper.insertDoctorMsg(msg);
+            //第一次确认
+            map.setStatus(2);
+        } else if (collection.getStatus() == 2) {
+            //第二次确认
+            map.setUserConfirm2(1);
+            if (collection.getUserConfirm2() == 0 && collection.getIsPackage() == 1) {
+                FsStoreOrder fsStoreOrder = storeOrderService.selectFsStoreOrderByOrderCode(collection.getPackageOrderCode());
+                if (fsStoreOrder != null) {
+                    FsStoreOrder editOrder   = new FsStoreOrder();
+                    editOrder.setOrderId(fsStoreOrder.getOrderId());
+//                    editOrder.setIsConfirm(1); todo
+                    storeOrderService.updateFsStoreOrder(editOrder);
+                } else {
+                    return R.error("没有找到订单");
+                }
+            }
+        }
+        if (fsUserInformationCollectionMapper.updateFsUserInformationCollection(map) > 0) {
+            //存入医生确认统计时间数据
+            FsDoctorConfirm doctorConfirm = new FsDoctorConfirm();
+            doctorConfirm.setCollectionId(collection.getId());
+            doctorConfirm.setDoctorId(collection.getDoctorId());
+            doctorConfirm.setStartTime(DateUtils.getNowDate());
+            doctorConfirmService.insertFsDoctorConfirm(doctorConfirm);
+            //医生确认时间存入缓存 医生确认后删除
+            redisCache.setCacheObject("doctorConfirm:" + collection.getId(), doctorConfirm);
+            return R.ok();
+        }
+        return R.error("用户确认失败");
+    }
+
+
+    @Override
+    @Transactional
+    public void autoRefund(FsUserInformationCollection collection) {
+
+        //清空订单号
+        fsUserInformationCollectionMapper.collectionOderCodeNULL(collection.getId());
+
+        //更新采集信息状态
+        FsUserInformationCollection map = new FsUserInformationCollection();
+        map.setId(collection.getId());
+        map.setUserConfirm2(0);
+        map.setStatus(1);
+        map.setUserConfirm(0);
+        map.setDoctorConfirm(0);
+        //更新用户信息采集状态
+        fsUserInformationCollectionMapper.updateFsUserInformationCollection(map);
+
+        //退款操作
+        FsStoreOrder fsStoreOrder = storeOrderService.selectFsStoreOrderByOrderCode(collection.getPackageOrderCode());
+        if (fsStoreOrder == null) {
+            throw new CustomException("没有处方订单");
+        }
+
+        //更新处方单信息
+        FsStoreOrder mapStoreOrder = new FsStoreOrder();
+        mapStoreOrder.setOrderId(fsStoreOrder.getOrderId());
+        mapStoreOrder.setStatus(-2);
+        mapStoreOrder.setRefundTime(DateUtils.getNowDate());
+        storeOrderService.updateFsStoreOrder(mapStoreOrder);
+
+        //订单日志
+        FsStoreOrderLogs Logs = new FsStoreOrderLogs();
+        Logs.setChangeMessage(FsStoreOrderStatusEnum.REFUND_STATUS_2.getDesc());
+        Logs.setOrderId(fsStoreOrder.getOrderId());
+        Logs.setChangeTime(new DateTime());
+        Logs.setChangeType(FsStoreOrderStatusEnum.REFUND_STATUS_2.getValue().toString());
+        fsStoreOrderLogsMapper.insertFsStoreOrderLogs(Logs);
+
+        String orderType = "store";
+        Long orderId = fsStoreOrder.getOrderId();
+        if (fsStoreOrder.getPackageOrderId() != null) {
+            orderType = "package";
+
+            FsPackageOrder fsPackageOrder = packageOrderMapper.selectFsPackageOrderByOrderSn(collection.getPackageOrderCode());
+            if (fsPackageOrder == null) {
+                throw new CustomException("没有套餐订单");
+            }
+            orderId = fsStoreOrder.getPackageOrderId();
+
+            //更新套餐单信息
+            FsPackageOrder mapPackageOrder = new FsPackageOrder();
+            mapPackageOrder.setOrderId(fsPackageOrder.getOrderId());
+            mapPackageOrder.setStatus(-2);
+            mapPackageOrder.setRefundStatus(2);
+            packageOrderMapper.updateFsPackageOrder(fsPackageOrder);
+        }
+
+        if (fsStoreOrder.getCompanyId() != null) {
+            companyService.refundCompanyMoney(fsStoreOrder);
+        }
+
+        //获取支付明细
+        List<FsStorePayment> payments = fsStorePaymentMapper.selectFsStorePaymentByPay(2, fsStoreOrder.getOrderId());
+        if (fsStoreOrder.getPackageOrderId() != null) {
+
+            payments = fsStorePaymentMapper.selectFsStorePaymentByPay(3, fsStoreOrder.getPackageOrderId());
+        }
+
+        if (payments != null && !payments.isEmpty()) {
+            FsStorePayment payment = payments.get(0);
+            String json = configService.selectConfigByKey("his.pay");
+            if (payment.getPayMode().equals("wx")) {
+                WxPayConfig payConfig = new WxPayConfig();
+                SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.pay");
+                FsPayConfig fsPayConfig = new Gson().fromJson(sysConfig.getConfigValue(), FsPayConfig.class);
+                payConfig.setAppId(fsPayConfig.getAppId());
+                payConfig.setMchId(fsPayConfig.getWxMchId());
+                payConfig.setMchKey(fsPayConfig.getWxMchKey());
+                payConfig.setKeyPath(wxPayProperties.getKeyPath());
+                payConfig.setSubAppId(org.apache.commons.lang3.StringUtils.trimToNull(null));
+                payConfig.setSubMchId(org.apache.commons.lang3.StringUtils.trimToNull(null));
+                wxPayService.setConfig(payConfig);
+                WxPayRefundRequest refundRequest = new WxPayRefundRequest();
+                refundRequest.setOutTradeNo(orderType + "-" + payment.getPayCode());
+                refundRequest.setOutRefundNo(orderType + "-" + payment.getPayCode());
+                refundRequest.setTotalFee(WxPayUnifiedOrderRequest.yuanToFen(payment.getPayMoney().toString()));
+                refundRequest.setRefundFee(WxPayUnifiedOrderRequest.yuanToFen(payment.getPayMoney().toString()));
+                try {
+                    WxPayRefundResult refundResult = wxPayService.refund(refundRequest);
+                    WxPayRefundQueryResult refundQueryResult = wxPayService.refundQuery("", refundResult.getOutTradeNo(), refundResult.getOutRefundNo(), refundResult.getRefundId());
+                    if (refundQueryResult != null && refundQueryResult.getResultCode().equals("SUCCESS")) {
+                        FsStorePayment paymentMap = new FsStorePayment();
+                        paymentMap.setPaymentId(payment.getPaymentId());
+                        paymentMap.setStatus(-1);
+                        paymentMap.setRefundTime(DateUtils.getNowDate());
+                        paymentMap.setRefundMoney(payment.getPayMoney());
+                        fsStorePaymentMapper.updateFsStorePayment(paymentMap);
+                    } else {
+                        throw new CustomException("退款请求失败" + refundQueryResult.getReturnMsg());
+                    }
+                } catch (WxPayException e) {
+                    throw new CustomException("退款请求失败" + e.getReturnMsg());
+                }
+            } else if (payment.getPayMode().equals("yb")) {
+                //易宝
+                RefundDTO refundDTO = new RefundDTO();
+                refundDTO.setRefundMoney(payment.getPayMoney().toString());
+                refundDTO.setLowRefundNo(orderType + "-" + payment.getPayCode());
+                refundDTO.setUpOrderId(payment.getTradeNo());
+                com.fs.ybPay.domain.RefundResult result = payService.refund(refundDTO);
+                logger.info("订单退款返回结果:退款订单id:" + orderId + result);
+                if (result.getState().equals("5")) {
+                    FsStorePayment paymentMap = new FsStorePayment();
+                    paymentMap.setPaymentId(payment.getPaymentId());
+                    paymentMap.setStatus(-1);
+                    paymentMap.setRefundTime(DateUtils.getNowDate());
+                    paymentMap.setRefundMoney(payment.getPayMoney());
+                    fsStorePaymentMapper.updateFsStorePayment(paymentMap);
+                } else {
+                    throw new CustomException("退款请求失败" + result.getMessage());
+                }
+            } else if (payment.getPayMode().equals("tz")) {
+                RefundParam tzBankResult = new RefundParam();
+                // 使用set方法为对象的字段赋值
+                //商户原支付订单号
+                tzBankResult.setOldPayOutOrderNo(orderType + payment.getPayCode());
+                // 商户退款订单号 对接平台系统里自己生成的退款订单号
+                tzBankResult.setRefundOrderNo(orderType + payment.getPayCode());
+                // 交易发送时间 yyyyMMddHHmmss
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+                tzBankResult.setTrxSendTime(sdf.format(new Date()));
+                // 退款金额
+                tzBankResult.setRefundOrdTransAmt(payment.getPayMoney().doubleValue());
+                TzBankResult<RefundResult> result = tzBankService.refund(tzBankResult);
+                logger.info("订单退款返回结果:退款订单id:" + orderId + result);
+                if (result.getBody().getRefundOrdStatus().equals("90") || result.getBody().getRefundOrdStatus().equals("60")) {
+                    FsStorePayment paymentMap = new FsStorePayment();
+                    paymentMap.setPaymentId(payment.getPaymentId());
+                    paymentMap.setStatus(-1);
+                    paymentMap.setRefundTime(DateUtils.getNowDate());
+                    paymentMap.setRefundMoney(payment.getPayMoney());
+                    fsStorePaymentMapper.updateFsStorePayment(paymentMap);
+                } else {
+                    throw new CustomException("退款请求失败" + result.getRetMsg());
+                }
+            } else if (payment.getPayMode().equals("hf")) {
+                if (payment.getIsShare() == 1 && payment.getShareStatus() == 1) {
+                    V2TradePaymentDelaytransConfirmrefundRequest confirmRefundRequest = new V2TradePaymentDelaytransConfirmrefundRequest();
+                    //Map<String, Object> extendInfoMap = new HashMap<>();
+                    String orderCode = OrderCodeUtils.getOrderSn();
+                    if (StringUtils.isEmpty(orderCode)) {
+                        throw new CustomException("订单生成失败,请重试");
+                    }
+                    confirmRefundRequest.setReqSeqId(orderCode);
+                    confirmRefundRequest.setOrgReqSeqId(payment.getShareCode());
+//                    confirmRefundRequest.setOrgReqDate(payment.getShareDate());
+                    //多汇付使用appID todo
+//                    HuifuConfirmrefundResult result = huiFuService.confirmRefund(confirmRefundRequest, null);
+//                    if (result.getResp_code().equals("00000000") && (result.getTrans_stat().equals("S") || result.getTrans_stat().equals("P"))) {
+//                        refund(payment, orderType, orderId);
+//                    } else {
+//                        throw new CustomException("交易确认退款请求失败" + result.getResp_desc());
+//                    }
+                } else {
+                    refund(payment, orderType, orderId);
+                }
+            }
+        } else {
+            throw new CustomException("未找的支付明细");
+        }
+
+
+    }
+    private void refund(FsStorePayment payment, String orderType, Long orderId) {
+        V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
+        request.setOrdAmt(payment.getPayMoney().toString());
+        request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
+        request.setReqSeqId("refund-" + payment.getPayCode());
+        Map<String, Object> extendInfoMap = new HashMap<>();
+        extendInfoMap.put("org_req_seq_id", orderType + "-" + payment.getPayCode());
+        request.setExtendInfo(extendInfoMap);
+        HuiFuRefundResult refund = huiFuService.refund(request);
+        logger.info("订单退款返回结果:退款订单id:" + orderId + refund);
+        if ((refund.getResp_code().equals("00000000") || refund.getResp_code().equals("00000100")) && (refund.getTrans_stat().equals("S") || refund.getTrans_stat().equals("P"))) {
+            FsStorePayment paymentMap = new FsStorePayment();
+            paymentMap.setPaymentId(payment.getPaymentId());
+            paymentMap.setStatus(-1);
+            paymentMap.setRefundTime(DateUtils.getNowDate());
+            paymentMap.setRefundMoney(payment.getPayMoney());
+            fsStorePaymentMapper.updateFsStorePayment(paymentMap);
+        } else {
+            throw new CustomException("退款请求失败" + refund.getResp_desc());
+        }
+    }
+    @Override
+    public FsUserInformationCollectionVO getCollectionByUserId(Long userId) {
+        FsUserInformationCollectionVO vo = new FsUserInformationCollectionVO();
+        FsUserInformationCollection collection = fsUserInformationCollectionMapper.selectFsUserInformationCollectionByUserId(userId);
+        if (collection != null) {
+            vo.setAnswers(JSON.parseArray(collection.getJsonInfo(), AnswerVO.class));
+            //医生建议
+            if (collection.getDoctorAdvice() != null){
+                vo.setDoctorAdvice(collection.getDoctorAdvice());
+            }
+        }
+        return vo;
+    }
+
+    private List<AnswerVO> getAnswerVOs(List<AnswerVO> target,List<AnswerVO> source) {
+        target.addAll(source);
+        return target.stream()
+                .collect(Collectors.groupingBy(AnswerVO::getTitle))
+                .values().stream()
+                .map(group -> group.stream().reduce((a, b) -> a).orElse(null))
+                .collect(Collectors.toList());
+    }
+
+    private FsUserInformationCollection getFsUserInformationCollection(FsUserInformationCollectionParam param) {
+        FsUserInformationCollection fsUserInformationCollection = new FsUserInformationCollection();
+        if (!CollectionUtils.isEmpty(param.getAnswers())) {
+            param.getAnswers().forEach(answer -> {
+                if (answer.getFlag() == null) {
+                    answer.setFlag(false);
+                }
+            });
+        }
+        fsUserInformationCollection.setJsonInfo(JSON.toJSONString(param.getAnswers()));
+        fsUserInformationCollection.setUserId(param.getUserId());
+        fsUserInformationCollection.setCompanyUserId(param.getCompanyUserId());
+        fsUserInformationCollection.setQuestionId(param.getQuestionId());
+        fsUserInformationCollection.setId(param.getId());
+        fsUserInformationCollection.setPackageId(param.getPackageId());
+        fsUserInformationCollection.setPayType(param.getPayType());
+        fsUserInformationCollection.setAmount(param.getAmount());
+        fsUserInformationCollection.setIsPackage(param.getIsPackage());
+        fsUserInformationCollection.setUserName(param.getUserName());
+        fsUserInformationCollection.setUserPhoneFour(param.getUserPhoneFour());
+        fsUserInformationCollection.setSex(param.getSex());
+        fsUserInformationCollection.setAllergy(param.getAllergy());
+        fsUserInformationCollection.setRemark(param.getRemark());
+        //获取医生信息
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getCompanyUserId());
+        if (companyUser == null) {
+            throw new CustomException("销售不存在");
+        }
+        Long doctorId = null;
+        if (companyUser.getDoctorId() != null) {
+            FsDoctor doctor = doctorMapper.selectFsDoctorByDoctorId(companyUser.getDoctorId());
+            if (doctor != null) {
+                doctorId = doctor.getDoctorId();
+            }
+        } else {
+            //随机获取医生id
+            doctorId = iFsDoctorService.selectFsDoctorDoctorByPackage();
+        }
+        fsUserInformationCollection.setDoctorId(doctorId);
+
+        return fsUserInformationCollection;
+    }
+
+    public static void main(String[] args) {
+//        String str1 = "[{\"options\":[{\"flag\":false,\"name\":\"通天\",\"value\":0},{\"flag\":false,\"name\":\"哈哈\",\"value\":1}],\"title\":\"测试标题1\",\"value\":1},{\"options\":[{\"flag\":false,\"name\":\"呼呼\",\"value\":0},{\"flag\":false,\"name\":\"嘻嘻\",\"value\":1}],\"title\":\"测试标题2\",\"value\":1},{\"options\":[{\"flag\":false,\"name\":\"胸痛\",\"value\":0},{\"flag\":false,\"name\":\"胸闷\",\"value\":1},{\"flag\":false,\"name\":\"头晕\",\"value\":2},{\"flag\":false,\"name\":\"肢体麻木\",\"value\":3},{\"flag\":false,\"name\":\"无\",\"value\":4}],\"title\":\"您目前是否有心脑血管相关症状,如胸痛、胸闷、头晕、肢体麻木等?\",\"value\":1}]";
+//        String str2 = "[{\"options\":[{\"flag\":true,\"name\":\"胸痛\",\"value\":0},{\"flag\":true,\"name\":\"胸闷\",\"value\":1},{\"flag\":true,\"name\":\"头晕\",\"value\":2},{\"flag\":true,\"name\":\"肢体麻木\",\"value\":3},{\"flag\":true,\"name\":\"无\",\"value\":4}],\"title\":\"您目前是否有心脑血管相关症状,如胸痛、胸闷、头晕、肢体麻木等?\",\"value\":1},{\"options\":[{\"flag\":false,\"name\":\"胃疼\",\"value\":0},{\"flag\":false,\"name\":\"反酸\",\"value\":1},{\"flag\":false,\"name\":\"恶心\",\"value\":2},{\"flag\":false,\"name\":\"呕吐\",\"value\":3},{\"flag\":false,\"name\":\"黑便\",\"value\":4},{\"flag\":false,\"name\":\"无\",\"value\":5}],\"title\":\"您近期是否出现胃部不适症状,如胃痛、反酸、恶心、呕吐或黑便?\",\"value\":1}]";
+//        List<AnswerVO> vo1 = null;
+//        List<AnswerVO> vo2 = JSON.parseArray(str2, AnswerVO.class);
+//        System.out.println(vo1);
+//        System.out.println(vo2);
+//        vo2.addAll(vo1);
+//        Map<String, List<AnswerVO>> collect = vo2.stream()
+//                .collect(Collectors.groupingBy(AnswerVO::getTitle));
+//        List<AnswerVO> collect1 = vo2.stream()
+//                .collect(Collectors.groupingBy(AnswerVO::getTitle))
+//                .values().stream()
+//                .map(group -> group.stream().reduce((a, b) -> a).orElse(null)).collect(Collectors.toList());
+//        System.out.println(JSON.toJSONString(collect));
+//        System.out.println(JSON.toJSONString(collect1));
+        boolean contains = "是否有糖尿病相关疾病?".contains("糖尿病");
+    }
+}

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

@@ -0,0 +1,32 @@
+package com.fs.his.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class AnswerVO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String title;
+
+    private List<Options> options;
+
+    //已选择数据(多选)
+    private List<Integer> value;
+
+    //排序
+    private Integer sort;
+    //private Integer value;
+
+
+    //是否医生已确认
+    private Boolean flag;
+
+   @Data
+    public static class Options implements Serializable{
+        private String name;
+        private Integer value;
+    }
+}

+ 15 - 0
fs-service/src/main/java/com/fs/his/vo/FsQuestionAndAnswerVO.java

@@ -0,0 +1,15 @@
+package com.fs.his.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FsQuestionAndAnswerVO {
+
+    private Long id;
+
+    private String questionName;
+
+    private List<AnswerVO> answers;
+}

+ 79 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserInfoCollectionUVO.java

@@ -0,0 +1,79 @@
+package com.fs.his.vo;
+
+import com.fs.his.domain.FsDoctor;
+import com.fs.his.domain.FsPackage;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class FsUserInfoCollectionUVO {
+    private Long id;
+
+    //套餐包id
+    private Long packageId;
+
+    //支付类型 0-全款 1-物流代收
+    private Integer payType;
+
+    //代收金额
+    private BigDecimal amount;
+
+    private List<AnswerVO> answers;
+
+    //是否关联套餐包 0-不关联 1-关联
+    private Integer isPackage;
+
+    //确认状态 1第一次确认 2第二次确认
+    private Integer status;
+
+    private Integer doctorConfirm;
+
+    private Integer userConfirm;
+
+    private Integer userConfirm2;
+
+    private FsPackage fsPackage;
+
+    //销售id
+    private Long companyUserId;
+
+    //销售公司id
+    private Long companyId;
+
+    //套餐包订单号
+    private String packageOrderCode;
+
+    //订单状态 0-待支付 1-待发货 2-待收货 3-已完成 4-已取消
+    private Integer orderStatus;
+
+    private Long orderId;
+
+    private String userAdvice;
+
+    private String doctorAdvice;
+
+
+    private String patientJson;
+
+    //处方图片
+    private String prescribeImgUrl;
+
+    private FsDoctor doctor;
+
+    //处方审核状态 0待审核 1通过 2拒绝
+    private Integer prescribeStatus;
+
+    //是否过敏
+    private String allergy;
+    //备注
+    private String remark;
+
+    //用户性别 0女 1男
+    private Integer sex;
+    //用户姓名
+    private String userName;
+    //用户电话后四位
+    private String userPhoneFour;
+}

+ 23 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserInformationCollectionListDVO.java

@@ -0,0 +1,23 @@
+package com.fs.his.vo;
+
+import lombok.Data;
+
+@Data
+public class FsUserInformationCollectionListDVO {
+
+    private Long id;
+
+    //用户信息
+    private String userName;
+
+    //医生确认状态 0未确认 1已确认
+    private Integer doctorConfirm;
+
+
+    private Integer userConfirm;
+
+    //用户补充说明
+    private String userAdvice;
+
+    private String doctorAdvice;
+}

+ 47 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserInformationCollectionVO.java

@@ -0,0 +1,47 @@
+package com.fs.his.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class FsUserInformationCollectionVO {
+
+    private Long id;
+
+    private Long questionId;
+
+    //套餐包id
+    private Long packageId;
+
+    //支付类型 0-全款 1-物流代收
+    private Integer payType;
+
+    //代收金额
+    private BigDecimal amount;
+
+    private List<AnswerVO> answers;
+
+    //是否关联套餐包 0-不关联 1-关联
+    private Integer isPackage;
+
+    //用户性别 0女 1男
+    private Integer sex;
+    //用户姓名
+    private String userName;
+    //用户电话后四位
+    private String userPhoneFour;
+
+    //是否过敏
+    private String allergy;
+
+    //备注
+    private String remark;
+
+    //医生建议
+    private String doctorAdvice;
+
+    private Long patientId;
+
+}

+ 19 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsStoreProductScrm.java

@@ -1,9 +1,11 @@
 package com.fs.hisStore.domain;
 
 import java.math.BigDecimal;
+import java.util.Date;
 
 import com.baomidou.mybatisplus.annotation.FieldStrategy;
 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;
 import lombok.Data;
@@ -320,4 +322,21 @@ public class FsStoreProductScrm extends BaseEntity
     private String returnAddress;
 
 
+    /** 原产地 */
+    @Excel(name = "原产地")
+    private String originPlace;
+
+    /** 净含量 */
+    @Excel(name = "净含量")
+    private String netContent;
+
+    /** 有效日期 */
+    @Excel(name = "有效日期")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date shelfLife;
+
+    /** 国产或进口 */
+    @Excel(name = "国产或进口")
+    private String domesticImported;
+
 }

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

+ 20 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreProductAddEditParam.java

@@ -1,5 +1,6 @@
 package com.fs.hisStore.param;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
 import com.fs.hisStore.domain.FsStoreProductAttrValueScrm;
 import com.fs.hisStore.dto.ProductArrtDTO;
@@ -7,6 +8,7 @@ import lombok.Data;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -276,4 +278,22 @@ public class FsStoreProductAddEditParam implements Serializable
         // 指定企业
     private String companyIds;
 
+
+    /** 原产地 */
+    @Excel(name = "原产地")
+    private String originPlace;
+
+    /** 净含量 */
+    @Excel(name = "净含量")
+    private String netContent;
+
+    /** 有效日期 */
+    @Excel(name = "有效日期")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date shelfLife;
+
+    /** 国产或进口 */
+    @Excel(name = "国产或进口")
+    private String domesticImported;
+
 }

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

@@ -185,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);
 
@@ -335,4 +335,14 @@ public interface IFsStoreOrderScrmService
     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);
 }

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

@@ -143,4 +143,6 @@ public interface IFsStoreProductScrmService
     List<FsStoreProductScrm> getStoreProductInProductIds(List<Long> storeProductIds);
 
     List<FsStoreProductListVO> liveList(LiveGoods liveId);
+
+    R copyStoreProduct(Long productId);
 }

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

@@ -121,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;
@@ -375,6 +376,9 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     @Autowired
     private FsWxExpressTaskMapper fsWxExpressTaskMapper;
 
+    @Autowired
+    private IFsStoreCartScrmService fsStoreCartScrmService;
+
     @PostConstruct
     public void initErpServiceMap() {
         erpServiceMap = new HashMap<>();
@@ -1553,14 +1557,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(param.getCreateOrderKey().isEmpty() ){
+            //直接购买
+            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());
                 }
             }
         }
@@ -1596,29 +1610,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();
@@ -1627,6 +1649,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             //组合数据
             CompanyUser user = companyUserService.selectCompanyUserById(param.getCompanyUserId());
             FsStoreOrderScrm storeOrder = new FsStoreOrderScrm();
+            storeOrder.setCartId(joinCartIds);
             //修改默认仓库
 
             storeOrder.setStoreHouseCode("CK01");
@@ -1635,7 +1658,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());
@@ -1700,6 +1729,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());
@@ -1727,7 +1757,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());
             //添加记录
@@ -2592,7 +2628,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");
@@ -2616,8 +2652,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);
@@ -5062,6 +5103,59 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         }
     }
 
+    @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"));
@@ -5098,6 +5192,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         map.put("ZTO", "中通");
         map.put("JD", "京东");
         map.put("DBL", "德邦");
+        map.put("YTO", "圆通");
         return map;
     }
 

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

@@ -1189,4 +1189,19 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
     public List<FsStoreProductListVO> liveList(LiveGoods liveId) {
         return fsStoreProductMapper.liveList(liveId);
     }
+
+    @Override
+    public R copyStoreProduct(Long productId) {
+        FsStoreProductScrm fsStoreProductScrm = fsStoreProductMapper.selectFsStoreProductById(productId);
+        if (fsStoreProductScrm == null) return R.error("商品不存在");
+
+        FsStoreProductScrm copy = new FsStoreProductScrm();
+        BeanUtils.copyProperties(fsStoreProductScrm, copy);
+        copy.setProductId(null);
+        copy.setIsAudit("0");
+        fsStoreProductMapper.insertFsStoreProduct(copy);
+
+        return R.ok();
+
+    }
 }

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

+ 3 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveMapper.java

@@ -170,4 +170,7 @@ public interface LiveMapper
             "WHERE l.live_id = #{liveId} " +
             "GROUP BY l.live_id, l.start_time")
     Integer selectLiveFlagByLiveId(@Param("liveId") Long liveId);
+
+    @Select("SELECT * FROM live WHERE is_audit = 1 and is_del = 0 and status in (1,2,4) and live_type in (2,3) order by create_time desc")
+    List<Live> listToLiveNoEnd(Live live);
 }

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

@@ -104,7 +104,7 @@ public interface LiveOrderMapper {
     @Select("select * from live_order where `status` = 3 AND TIMESTAMPDIFF(HOUR, start_time, NOW()) >= 48  ")
     List<LiveOrder> selectLiveOrderByFinish();
 
-    @Select("select * from live_order where `status` = 2 and extend_order_id is not null ")
+    @Select("select * from live_order where `status` = 1 and extend_order_id is not null ")
     List<LiveOrder> selectUpdateExpress();
 
     @Select("select order_id from live_order where `status` = 2")
@@ -369,8 +369,8 @@ public interface LiveOrderMapper {
     int batchUpdateErpByOrderIds(@Param("maps")ArrayList<Map<String, String>> maps);
 
     @Select({"<script> " +
-            "select o.order_id,o.order_code,o.item_json,o.pay_price,o.status,o.delivery_id,o.finish_time  from live_order o  " +
-            "where o.is_del=0 and o.is_sys_del=0 " +
+            "select o.order_id,o.order_code,o.item_json,o.pay_price,o.status,o.delivery_sn as delivery_id,o.finish_time  from live_order o  " +
+            "where o.is_del=0 " +
             "<if test = 'maps.status != null and maps.status != \"\"     '> " +
             "and o.status =#{maps.status} " +
             "</if>" +
@@ -389,7 +389,7 @@ public interface LiveOrderMapper {
 
     @Select({"<script> " +
             "select o.*  from live_order o  " +
-            "where o.is_del=0 and o.is_sys_del=0 " +
+            "where o.is_del=0 " +
             "<if test = 'maps.status != null and maps.status != \"\"     '> " +
             "and o.status =#{maps.status} " +
             "</if>" +

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

@@ -192,4 +192,6 @@ public interface ILiveService
     String getGotoWxAppLiveLink(String linkStr, String appid);
 
     R liveListAll(PageRequest pageRequest);
+
+    List<Live> listToLiveNoEnd(Live live);
 }

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

@@ -585,13 +585,13 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
         }
         if(liveAfterSales.getRefundType().equals(0)){
             //仅退款未发货处理
-            if(liveAfterSales.getOrderStatus().equals(2)){
+            if(liveAfterSales.getOrderStatus().equals(OrderInfoEnum.STATUS_1.getValue())){
                 //审核通过
                 liveAfterSales.setStatus(3);
                 baseMapper.updateLiveAfterSales(liveAfterSales);
             }
             //待收货直接退款
-            else if(liveAfterSales.getOrderStatus().equals(3)){
+            else if(liveAfterSales.getOrderStatus().equals(OrderInfoEnum.STATUS_2.getValue())){
                 liveAfterSales.setStatus(2);
                 baseMapper.updateLiveAfterSales(liveAfterSales);
             //已完成 退货退款
@@ -613,7 +613,7 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
             StoreConfig config=JSONUtil.toBean(json,StoreConfig.class);
             LiveOrder order=liveOrderMapper.selectLiveOrderByOrderId(String.valueOf(liveAfterSales.getOrderId()));
             if(order.getStoreHouseCode()!=null){
-                if(order.getStoreHouseCode().equals("CQDS001")){
+                if(order.getStoreHouseCode().equals("CK01")){
                     liveAfterSales.setConsignee(config.getRefundConsignee());
                     liveAfterSales.setPhoneNumber(config.getRefundPhoneNumber());
                     liveAfterSales.setAddress(config.getRefundAddress());

+ 22 - 17
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -1258,6 +1258,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public R refundOrderMoney(Long orderId, LiveAfterSales liveAfterSales) {
         IErpOrderService erpOrderService = getErpService();
         FsErpConfig erpConfig = configUtil.generateStructConfigByKey("his.config", FsErpConfig.class);
@@ -1268,7 +1269,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         if (order.getStatus() == -2) {
             return R.error("已退款");
         }
-        if (order.getStatus() != 1 && order.getStatus() != 2) {
+        if (!liveAfterSales.getStatus().equals(AfterSalesStatusEnum.STATUS_3.getValue())) {
             return R.error("非法操作");
         }
         if (erpConfig.getErpOpen() != null
@@ -1277,6 +1278,10 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 && !CloudHostUtils.hasCloudHostName("康年堂")) {
             return R.error("暂未推送至erp,请稍后再试!");
         }
+        liveAfterSales.setRefundAmount(order.getPayPrice());
+        liveAfterSales.setStatus(4);
+        liveAfterSales.setSalesStatus(3);
+        liveAfterSalesService.updateLiveAfterSales(liveAfterSales);
         if (StringUtils.isNotEmpty(order.getExtendOrderId())) {
             ErpRefundUpdateRequest request = new ErpRefundUpdateRequest();
             request.setTid(order.getOrderCode());
@@ -1811,7 +1816,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         totalPrice = totalPrice.add(deliveryMoney);
         liveOrder.setDiscountMoney(totalPrice);
 
-
+        fsStoreProduct.setCost(BigDecimal.ZERO);
         liveOrder.setItemJson(JSON.toJSONString(fsStoreProduct));
         liveOrder.setCreateTime(new Date());
         liveOrder.setUpdateTime(new Date());
@@ -2258,13 +2263,13 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         for (FsMyLiveOrderListQueryVO vo : list) {
 //          List<FsStoreOrderItemVO> items=fsStoreOrderItemMapper.selectMyFsStoreOrderItemListByOrderId(vo.getId());
 //          vo.setItems(items);
-            if (StringUtils.isNotEmpty(vo.getItemJson())) {
-                JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
-                List<LiveOrderItem> items = JSONUtil.toList(jsonArray, LiveOrderItem.class);
-                if (!items.isEmpty()) {
-                    vo.setItems(items);
-                }
-            }
+//            if (StringUtils.isNotEmpty(vo.getItemJson())) {
+//                JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+//                List<LiveOrderItem> items = JSONUtil.toList(jsonArray, LiveOrderItem.class);
+//                if (!items.isEmpty()) {
+//                    vo.setItems(items);
+//                }
+//            }
             //处理是否可以申请售后
             vo.setIsAfterSales(0);
             if (vo.getStatus().equals(OrderInfoEnum.STATUS_3.getValue())) {
@@ -2296,13 +2301,13 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         for (FsMyLiveOrderListQueryVO vo : list) {
 //          List<FsStoreOrderItemVO> items=fsStoreOrderItemMapper.selectMyFsStoreOrderItemListByOrderId(vo.getId());
 //          vo.setItems(items);
-            if (StringUtils.isNotEmpty(vo.getItemJson())) {
-                JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
-                List<LiveOrderItem> items = JSONUtil.toList(jsonArray, LiveOrderItem.class);
-                if (items.size() > 0) {
-                    vo.setItems(items);
-                }
-            }
+//            if (StringUtils.isNotEmpty(vo.getItemJson())) {
+//                JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+//                List<LiveOrderItem> items = JSONUtil.toList(jsonArray, LiveOrderItem.class);
+//                if (items.size() > 0) {
+//                    vo.setItems(items);
+//                }
+//            }
         }
         return list;
     }
@@ -3274,7 +3279,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                 }
             }
         }
-
+        fsStoreProduct.setCost(BigDecimal.ZERO);
         liveOrder.setItemJson(JSON.toJSONString(fsStoreProduct));
         liveOrder.setCreateTime(new Date());
         liveOrder.setUpdateTime(new Date());

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

@@ -469,6 +469,11 @@ public class LiveServiceImpl implements ILiveService
         return R.ok().put("data", result);
     }
 
+    @Override
+    public List<Live> listToLiveNoEnd(Live live) {
+        return baseMapper.listToLiveNoEnd(live);
+    }
+
     /**
      * 修改直播
      *
@@ -807,43 +812,43 @@ public class LiveServiceImpl implements ILiveService
         if (exist == null) {
             return R.error("直播间不存在");
         }
-        
+
         // 1. 复制直播间基础信息
         Long newLiveId = copyLiveBasicInfo(exist, live, now);
-        
+
         // 2. 创建直播数据
         createLiveData(newLiveId);
-        
+
         // 3. 复制直播视频
         copyLiveVideos(existLiveId, newLiveId, now);
-        
+
         // 4. 获取所有自动化任务并按类型分组
         List<LiveAutoTask> allTasks = liveAutoTaskService.selectLiveAutoTaskByLiveId(existLiveId);
         Map<Long, List<LiveAutoTask>> tasksByType = allTasks.stream()
                 .collect(Collectors.groupingBy(LiveAutoTask::getTaskType));
-        
+
         // 5. 复制弹幕任务(taskType=3)
         copyBarrageTasks(tasksByType.get(3L), newLiveId, now);
-        
+
         // 6. 复制红包配置及任务(taskType=2)
         Map<Long, Long> redIdMapping = copyRedConfs(existLiveId, newLiveId, tasksByType.get(2L), now);
-        
+
         // 7. 复制抽奖配置及任务(taskType=4)
         Map<Long, Long> lotteryIdMapping = copyLotteryConfs(existLiveId, newLiveId, tasksByType.get(4L), now);
-        
+
         // 8. 复制商品、商品任务、上下架任务及优惠券关系
-        Map<Long, Long> goodsIdMapping = copyGoodsAndTasks(existLiveId, newLiveId, live, 
+        Map<Long, Long> goodsIdMapping = copyGoodsAndTasks(existLiveId, newLiveId, live,
                 tasksByType.get(1L), tasksByType.get(6L), now);
-        
+
         // 9. 复制优惠券关系
         copyCouponRelations(existLiveId, newLiveId, goodsIdMapping);
-        
+
         // 10. 复制优惠券自动化任务(taskType=5)
         copyCouponTasks(tasksByType.get(5L), newLiveId, goodsIdMapping, now);
 
         return R.ok("复制成功");
     }
-    
+
     /**
      * 复制直播间基础信息
      */
@@ -865,7 +870,7 @@ public class LiveServiceImpl implements ILiveService
         }
         return liveEntity.getLiveId();
     }
-    
+
     /**
      * 创建直播数据
      */
@@ -882,7 +887,7 @@ public class LiveServiceImpl implements ILiveService
         liveData.setFollowNum(0L);
         liveDataService.insertLiveData(liveData);
     }
-    
+
     /**
      * 复制直播视频
      */
@@ -897,7 +902,7 @@ public class LiveServiceImpl implements ILiveService
             liveVideoService.insertLiveVideo(videoEntity);
         }
     }
-    
+
     /**
      * 复制弹幕任务(taskType=3)
      */
@@ -918,20 +923,20 @@ public class LiveServiceImpl implements ILiveService
             liveAutoTaskService.batchInsertLiveAutoTask(addList);
         }
     }
-    
+
     /**
      * 复制红包配置及任务
      */
-    private Map<Long, Long> copyRedConfs(Long existLiveId, Long newLiveId, 
+    private Map<Long, Long> copyRedConfs(Long existLiveId, Long newLiveId,
                                          List<LiveAutoTask> redTasks, Date now) {
         Map<Long, Long> redIdMapping = new HashMap<>();
         if (redTasks == null) {
             redTasks = Collections.emptyList();
         }
         Map<Long, LiveAutoTask> redTaskMap = redTasks.stream()
-                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "redId"), 
+                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "redId"),
                         Function.identity(), (existing, replacement) -> existing));
-        
+
         List<LiveRedConf> liveRedConfs = liveRedConfService.selectByLiveId(existLiveId);
         for (LiveRedConf liveRedConf : liveRedConfs) {
             LiveRedConf newRedConf = new LiveRedConf();
@@ -942,32 +947,32 @@ public class LiveServiceImpl implements ILiveService
             newRedConf.setCreateTime(now);
             newRedConf.setTotalSend(0L);
             liveRedConfService.insertLiveRedConf(newRedConf);
-            
+
             redIdMapping.put(liveRedConf.getRedId(), newRedConf.getRedId());
-            
+
             LiveAutoTask task = redTaskMap.get(liveRedConf.getRedId());
             if (task != null) {
-                LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now, 
+                LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now,
                         JSON.toJSONString(newRedConf));
                 liveAutoTaskService.directInsertLiveAutoTask(newTask);
             }
         }
         return redIdMapping;
     }
-    
+
     /**
      * 复制抽奖配置及任务
      */
-    private Map<Long, Long> copyLotteryConfs(Long existLiveId, Long newLiveId, 
+    private Map<Long, Long> copyLotteryConfs(Long existLiveId, Long newLiveId,
                                              List<LiveAutoTask> lotteryTasks, Date now) {
         Map<Long, Long> lotteryIdMapping = new HashMap<>();
         if (lotteryTasks == null) {
             lotteryTasks = Collections.emptyList();
         }
         Map<Long, LiveAutoTask> lotteryTaskMap = lotteryTasks.stream()
-                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "lotteryId"), 
+                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "lotteryId"),
                         Function.identity(), (existing, replacement) -> existing));
-        
+
         List<LiveLotteryConf> liveLotteryConfs = liveLotteryConfService.selectByLiveId(existLiveId);
         if (!liveLotteryConfs.isEmpty()) {
             List<Long> lotteryIds = liveLotteryConfs.stream()
@@ -975,7 +980,7 @@ public class LiveServiceImpl implements ILiveService
             List<LiveLotteryProductConf> products = liveLotteryProductConfMapper.selectEntityByIds(lotteryIds);
             Map<Long, List<LiveLotteryProductConf>> productsByLotteryId = products.stream()
                     .collect(Collectors.groupingBy(LiveLotteryProductConf::getLotteryId));
-            
+
             for (LiveLotteryConf liveLotteryConf : liveLotteryConfs) {
                 LiveLotteryConf newLotteryConf = new LiveLotteryConf();
                 BeanUtils.copyBeanProp(newLotteryConf, liveLotteryConf);
@@ -985,9 +990,9 @@ public class LiveServiceImpl implements ILiveService
                 newLotteryConf.setCreateTime(now);
                 newLotteryConf.setUpdateTime(now);
                 liveLotteryConfService.insertLiveLotteryConf(newLotteryConf);
-                
+
                 lotteryIdMapping.put(liveLotteryConf.getLotteryId(), newLotteryConf.getLotteryId());
-                
+
                 // 复制奖品
                 List<LiveLotteryProductConf> lotteryProducts = productsByLotteryId.get(liveLotteryConf.getLotteryId());
                 if (lotteryProducts != null) {
@@ -999,10 +1004,10 @@ public class LiveServiceImpl implements ILiveService
                         liveLotteryProductConfMapper.insertLiveLotteryProductConf(newProduct);
                     }
                 }
-                
+
                 LiveAutoTask task = lotteryTaskMap.get(liveLotteryConf.getLotteryId());
                 if (task != null) {
-                    LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now, 
+                    LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now,
                             JSON.toJSONString(newLotteryConf));
                     liveAutoTaskService.directInsertLiveAutoTask(newTask);
                 }
@@ -1010,7 +1015,7 @@ public class LiveServiceImpl implements ILiveService
         }
         return lotteryIdMapping;
     }
-    
+
     /**
      * 复制商品、商品任务、上下架任务
      */
@@ -1023,25 +1028,25 @@ public class LiveServiceImpl implements ILiveService
         if (shelfTasks == null) {
             shelfTasks = Collections.emptyList();
         }
-        
+
         Map<Long, LiveAutoTask> goodsTaskMap = goodsTasks.stream()
-                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"), 
+                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"),
                         Function.identity(), (existing, replacement) -> existing));
         Map<Long, LiveAutoTask> shelfTaskMap = shelfTasks.stream()
-                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"), 
+                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"),
                         Function.identity(), (existing, replacement) -> existing));
-        
+
         LiveGoods queryParam = new LiveGoods();
         queryParam.setLiveId(existLiveId);
         List<LiveGoodsVo> goodsList = liveGoodsService.selectProductListByLiveId(queryParam);
-        
+
         if (!goodsList.isEmpty()) {
             List<Long> goodsProductIds = goodsList.stream()
                     .map(LiveGoodsVo::getProductId).collect(Collectors.toList());
             Map<Long, FsStoreProductScrm> goodsMap = fsStoreProductScrmMapper
                     .selectFsStoreProductByProductIds(goodsProductIds).stream()
                     .collect(Collectors.toMap(FsStoreProductScrm::getProductId, Function.identity()));
-            
+
             for (LiveGoodsVo liveGoods : goodsList) {
                 LiveGoods newGoods = new LiveGoods();
                 BeanUtils.copyBeanProp(newGoods, liveGoods);
@@ -1051,12 +1056,12 @@ public class LiveServiceImpl implements ILiveService
                 newGoods.setIsShow(false);
                 newGoods.setCompanyId(live.getCompanyId());
                 newGoods.setCompanyUserId(live.getCompanyUserId());
-                newGoods.setStock(goodsMap.containsKey(liveGoods.getProductId()) 
+                newGoods.setStock(goodsMap.containsKey(liveGoods.getProductId())
                         ? goodsMap.get(liveGoods.getProductId()).getStock() : 0);
                 liveGoodsService.insertLiveGoods(newGoods);
-                
+
                 goodsIdMapping.put(liveGoods.getGoodsId(), newGoods.getGoodsId());
-                
+
                 // 复制商品推送任务(taskType=1)
                 LiveAutoTask goodsTask = goodsTaskMap.get(liveGoods.getGoodsId());
                 if (goodsTask != null) {
@@ -1064,17 +1069,17 @@ public class LiveServiceImpl implements ILiveService
                     LiveGoodsVo newGoodsVo = new LiveGoodsVo();
                     BeanUtils.copyBeanProp(newGoodsVo, liveGoods);
                     newGoodsVo.setGoodsId(newGoods.getGoodsId());
-                    LiveAutoTask newTask = createAutoTaskEntity(goodsTask, newLiveId, now, 
+                    LiveAutoTask newTask = createAutoTaskEntity(goodsTask, newLiveId, now,
                             JSON.toJSONString(newGoodsVo));
                     liveAutoTaskService.directInsertLiveAutoTask(newTask);
                 }
-                
+
                 // 复制上下架任务(taskType=6)
                 LiveAutoTask shelfTask = shelfTaskMap.get(liveGoods.getGoodsId());
                 if (shelfTask != null) {
                     JSONObject contentJson = JSON.parseObject(shelfTask.getContent());
                     contentJson.put("goodsId", newGoods.getGoodsId());
-                    LiveAutoTask newTask = createAutoTaskEntity(shelfTask, newLiveId, now, 
+                    LiveAutoTask newTask = createAutoTaskEntity(shelfTask, newLiveId, now,
                             contentJson.toJSONString());
                     liveAutoTaskService.directInsertLiveAutoTask(newTask);
                 }
@@ -1082,7 +1087,7 @@ public class LiveServiceImpl implements ILiveService
         }
         return goodsIdMapping;
     }
-    
+
     /**
      * 复制优惠券关系
      */
@@ -1100,11 +1105,11 @@ public class LiveServiceImpl implements ILiveService
             liveCouponIssueMapper.insertLiveCouponIssueRelation(newRelation);
         }
     }
-    
+
     /**
      * 复制优惠券自动化任务(taskType=5)
      */
-    private void copyCouponTasks(List<LiveAutoTask> couponTasks, Long newLiveId, 
+    private void copyCouponTasks(List<LiveAutoTask> couponTasks, Long newLiveId,
                                  Map<Long, Long> goodsIdMapping, Date now) {
         if (couponTasks == null || couponTasks.isEmpty()) {
             return;
@@ -1117,7 +1122,7 @@ public class LiveServiceImpl implements ILiveService
                     Long newGoodsId = goodsIdMapping.get(liveCoupon.getGoodsId());
                     if (newGoodsId != null) {
                         liveCoupon.setGoodsId(newGoodsId);
-                        LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now, 
+                        LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now,
                                 JSON.toJSONString(liveCoupon));
                         liveAutoTaskService.directInsertLiveAutoTask(newTask);
                     }
@@ -1127,7 +1132,7 @@ public class LiveServiceImpl implements ILiveService
             }
         }
     }
-    
+
     /**
      * 创建自动化任务实体
      */

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

@@ -77,3 +77,4 @@ public class LiveDataListVo {
 
 
 
+

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

@@ -58,3 +58,4 @@ public class LiveDataStatisticsVo {
 
 
 
+

+ 8 - 3
fs-service/src/main/java/com/fs/live/vo/LiveOrderVoZm.java

@@ -18,6 +18,11 @@ import java.util.Date;
 @Data
 public class LiveOrderVoZm{
 
+
+    /** 订单号 */
+    @Excel(name = "订单号")
+    private String orderCode;
+
     /** 订单ID */
     private Long orderId;
 
@@ -115,6 +120,9 @@ public class LiveOrderVoZm{
     @Excel(name = "成本价格")
     private BigDecimal cost;
 
+    @Excel(name = "结算价格")
+    private BigDecimal fPrice;
+
     /** 详细地址 */
     @Excel(name = "收货地址")
     private String userAddress;
@@ -134,9 +142,6 @@ public class LiveOrderVoZm{
 
 
 
-    /** 订单号 */
-//    @Excel(name = "订单号")
-    private String orderCode;
 
 
     /** 收货人 */

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

@@ -542,4 +542,6 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
 
 
     List<QwExternalContact> getGroupChatUserByChatIdAndUserName(@Param("userId")String userId,@Param("userName")String userName,@Param("corpId") String corpId,@Param("chatId") String chatId);
+    @Select("select * from qw_external_contact where unionid = #{unionID} order by create_time asc limit 1 ")
+    QwExternalContact selectQwExternalByUnionID(String unionId);
 }

+ 10 - 1
fs-service/src/main/java/com/fs/qw/service/impl/AsyncSopTestService.java

@@ -27,6 +27,7 @@ import com.fs.voice.utils.StringUtil;
 import com.fs.wxUser.param.CompanyWxUserSopParam;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
@@ -510,7 +511,15 @@ public class AsyncSopTestService {
 
         setting.forEach(item->{
             try {
-                push2Service.pushSopAppLinkMsgByExternalIM(cropId,item.getLinkTitle(),item.getLinkDescribe(),item.getLinkImageUrl(),item.getAppLinkUrl(),companyUserId,fsUserId);
+                String linkImageUrl = item.getLinkImageUrl();
+                String linkTitle = item.getLinkTitle();
+                if (StringUtils.isBlank(linkImageUrl)) {
+                    linkImageUrl = item.getCourseUrl();
+                }
+                if (StringUtils.isBlank(linkTitle)) {
+                    linkTitle = item.getTitle();
+                }
+                push2Service.pushSopAppLinkMsgByExternalIM(cropId, linkTitle,item.getLinkDescribe(), linkImageUrl,item.getAppLinkUrl(),companyUserId,fsUserId);
             } catch (JsonProcessingException e) {
                 e.printStackTrace();
             }

+ 9 - 0
fs-service/src/main/java/com/fs/qw/vo/QwSopCourseFinishTempSetting.java

@@ -80,6 +80,9 @@ public class QwSopCourseFinishTempSetting implements Serializable,Cloneable{
         /** 小程序page路径 */
         private String miniprogramPage;
 
+        /** 直播间ID */
+        private String liveId;
+
         //链接标题
         private String linkTitle;
         //链接描述
@@ -116,6 +119,12 @@ public class QwSopCourseFinishTempSetting implements Serializable,Cloneable{
 
         //课程id
         private Long courseId;
+
+        //封面图片地址 app用的参数
+        private String courseUrl;
+
+        //app显示标题 app用的参数
+        private String title;
         @Override
         public Setting clone() {
             try {

+ 3 - 0
fs-service/src/main/java/com/fs/qw/vo/QwSopTempSetting.java

@@ -112,6 +112,9 @@ public class QwSopTempSetting implements Serializable{
             /** 小程序page路径 */
             private String miniprogramPage;
 
+            /** 直播间Id*/
+            private Long liveId;
+
             //文件地址
             private String fileUrl;
             //视频地址

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

@@ -2088,6 +2088,8 @@ public class QwSopLogsServiceImpl extends ServiceImpl<QwSopLogsMapper, QwSopLogs
     // 处理不同类型的内容
     private void processContent(QwSopTempSetting.Content.Setting set, String corpId,
                                 QwMsgTemplateSop templateSop, List<QwMsgTemplateSop.Attachment> attachments,Long courseId) {
+        // 发送直播卡片兼容处理
+        if(set.getContentType() != null && "12".equals(set.getContentType())) set.setContentType("4");
         switch (set.getContentType()) {
             case "1":
                 templateSop.setTextContent(set.getValue());

+ 4 - 25
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -653,7 +653,9 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
 
         List<QwSopTempContent> contentList = qwSopTempContentService.listByTempIds(sopTempIds);
 
-        CountDownLatch latch = new CountDownLatch(tempList.size());
+        qwSopTempDayService.removeByTempIds(tempIds);
+        qwSopTempRulesService.removeByTempIds(tempIds);
+        qwSopTempContentService.removeByTempIds(tempIds);
         // 对每个模板执行同步操作
         for (QwSopTemp temp : tempList) {
             // 构造timeList timeDesc time
@@ -684,31 +686,8 @@ public class QwSopTempServiceImpl implements IQwSopTempService {
             // 插入课程id
             temp.setCourseId(courseId);
             temp.setOpenOfficial("1");
-            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-            temp.setCreateTime(sdf.format(new Date()));
-            temp.setId(UUID.randomUUID().toString());
-            qwSopTempMapper.insertQwSopTemp(temp);
             // 重新生成该模板的规则和内容
-            threadPoolTaskExecutor.execute(() -> {
-                try {
-                    createSopTempRules(temp);
-                } catch (Exception e) {
-                    log.error("同步模板规则和内容失败", e);
-                } finally {
-                    latch.countDown();
-                }
-            });
-
-        }
-
-        try {
-            latch.await();
-            qwSopTempMapper.deleteQwSopTempByIds(tempIds.toArray(new String[0]));
-            qwSopTempDayService.removeByTempIds(tempIds);
-            qwSopTempRulesService.removeByTempIds(tempIds);
-            qwSopTempContentService.removeByTempIds(tempIds);
-        } catch (InterruptedException e) {
-            log.error("等待线程执行完成时被中断", e);
+            threadPoolTaskExecutor.execute(() -> createSopTempRules(temp));
         }
     }
 

+ 49 - 0
fs-service/src/main/java/com/fs/sop/service/impl/SopUserLogsInfoServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fs.common.config.FSSysConfig;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.PubFun;
@@ -663,6 +664,15 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                             case "7":
                                 createVoiceUrl(st, companyUserId, qwSop);
                                 break;
+                            //直播小程序单独
+                            case "12":
+                                String sortLiveLink = "/pages_course/living?companyId=" + qwUser.getCompanyUserId() + "&companyUserId=" + companyUserId + "&liveId=" + st.getLiveId();
+                                st.setContentType("4");
+                                String js = configService.selectConfigByKey("his.config");
+                                FSSysConfig sysConfig= JSON.parseObject(js,FSSysConfig.class);
+                                st.setMiniprogramAppid(sysConfig.getAppId());
+                                st.setMiniprogramPage(sortLiveLink);
+                                break;
                         }
                     }
                     setting.setSetting(list);
@@ -797,6 +807,15 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                                     createVoiceUrl(st, String.valueOf(qwUser.getCompanyUserId()), qwSop);
                                 }
                                 break;
+                            //直播小程序单独
+                            case "12":
+                                String sortLiveLink = "/pages_course/living?companyId=" + qwUser.getCompanyUserId() + "&companyUserId=" + qwUser.getCompanyUserId() + "&liveId=" + st.getLiveId();
+                                st.setContentType("4");
+                                String js = configService.selectConfigByKey("his.config");
+                                FSSysConfig sysConfig= JSON.parseObject(js,FSSysConfig.class);
+                                st.setMiniprogramAppid(sysConfig.getAppId());
+                                st.setMiniprogramPage(sortLiveLink);
+                                break;
                         }
                     }
                     setting.setSetting(list);
@@ -991,6 +1010,15 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                                 st.setMiniprogramAppid("未找到匹配的公司的自定义小程序:"+companyId);
                             }
 
+                            break;
+                        //直播小程序单独
+                        case "12":
+                            String sortLiveLink = "/pages_course/living?companyId=" + qwUser.getCompanyUserId() + "&companyUserId=" + qwUser.getCompanyUserId() + "&liveId=" + st.getLiveId();
+                            st.setContentType("4");
+                            String js = configService.selectConfigByKey("his.config");
+                            FSSysConfig sysConfig= JSON.parseObject(js,FSSysConfig.class);
+                            st.setMiniprogramAppid(sysConfig.getAppId());
+                            st.setMiniprogramPage(sortLiveLink);
                             break;
                         default:
                             break;
@@ -1442,6 +1470,27 @@ public class SopUserLogsInfoServiceImpl implements ISopUserLogsInfoService {
                         st.setMiniprogramAppid("未找到匹配的公司的自定义小程序:"+companyId);
                     }
 
+                    break;
+                //直播小程序单独
+                case "12":
+                    String sortLiveLink;
+                    sortLiveLink = "/pages_course/living?companyId=" + companyId + "&companyUserId=" + companyUserId + "&liveId=" + st.getLiveId();
+
+
+                    String miniprogramLiveTitle = st.getMiniprogramTitle();
+                    int maxLiveLength = 17;
+                    st.setMiniprogramTitle(miniprogramLiveTitle.length() > maxLiveLength ? miniprogramLiveTitle.substring(0, maxLiveLength) + "..." : miniprogramLiveTitle);
+                    String json = configService.selectConfigByKey("his.config");
+                    FSSysConfig sysConfig= JSON.parseObject(json,FSSysConfig.class);
+                    st.setMiniprogramAppid(sysConfig.getAppId());
+                    st.setMiniprogramPage(sortLiveLink);
+                    st.setContentType("4");
+                    try {
+                        st.setMiniprogramPicUrl(StringUtil.strIsNullOrEmpty(st.getMiniprogramPicUrl()) ? "https://cos.his.cdwjyyh.com/fs/20250331/ec2b4e73be8048afbd526124a655ad56.png" : st.getMiniprogramPicUrl());
+                    } catch (Exception e) {
+                        log.error("赋值-小程序封面地址失败-" + e);
+                    }
+
                     break;
                 default:
                     break;

+ 5 - 3
fs-service/src/main/java/com/fs/wx/order/service/ShippingService.java

@@ -1,5 +1,6 @@
 package com.fs.wx.order.service;
 
+import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.*;
@@ -7,10 +8,12 @@ import cn.hutool.json.JSONUtil;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fs.core.config.WxMaConfiguration;
 import com.fs.wx.order.dto.UploadShippingInfoRequest;
 import com.fs.wx.order.dto.WeChatApiConfig;
 import com.fs.wx.order.dto.WeChatApiResponse;
 import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
 import org.springframework.stereotype.Service;
 import org.springframework.web.util.UriComponentsBuilder;
 
@@ -30,8 +33,8 @@ public class ShippingService {
      * @param request 发货信息请求体
      * @return 微信 API 的响应
      */
-    public WeChatApiResponse uploadShippingInfo(UploadShippingInfoRequest request) {
-        WeChatAuthService weChatAuthService = WeChatAuthFactory.getWeChatAuthService(request.getAppid());
+    public WeChatApiResponse uploadShippingInfo(UploadShippingInfoRequest request) throws WxErrorException {
+        final WxMaService weChatAuthService = WxMaConfiguration.getMaService(request.getAppid());
         String accessToken = weChatAuthService.getAccessToken(false);
         if (accessToken == null) {
             log.error("获取微信 Access Token 失败");
@@ -81,7 +84,6 @@ public class ShippingService {
                     log.warn("微信接口返回业务错误: code={}, message={}", weChatApiResponse.getErrcode(), weChatApiResponse.getErrmsg());
                     if(ObjectUtil.equal(weChatApiResponse.getErrcode(),40001)) {
                         log.info("token缓存失效,清除token,等待下次执行...");
-                        weChatAuthService.clearToken();
                     }
                 }
                 return weChatApiResponse;

+ 4 - 4
fs-service/src/main/java/com/fs/wx/order/service/WeChatAuthFactory.java

@@ -14,17 +14,17 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
-@Component
-public class WeChatAuthFactory implements ApplicationContextAware, SmartInitializingSingleton {
+//@Component
+public class WeChatAuthFactory  {
     private static ApplicationContext applicationContext;
     private final static Map<String,WeChatAuthService> weChatAuthServices = new ConcurrentHashMap<>();
 
-    @Override
+
     public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
         WeChatAuthFactory.applicationContext = applicationContext;
     }
 
-    @Override
+
     public void afterSingletonsInstantiated() {
         PaymentMiniProgramConfigMapper mapper = applicationContext.getBean(PaymentMiniProgramConfigMapper.class);
         List<PaymentMiniProgramConfig> configs = mapper.selectAll();

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

@@ -146,6 +146,6 @@ openIM:
 im:
   type: NONE
 #是否为新商户,新商户不走mpOpenId
-isNewWxMerchant: false
+isNewWxMerchant: true
 
-enableRedPackAccount: 1
+enableRedPackAccount: 0

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

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.course.mapper.FsProjectAddressConfigMapper">
+
+
+</mapper>

+ 85 - 0
fs-service/src/main/resources/mapper/his/DoctorMsgMapper.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.doctor.mapper.DoctorMsgMapper">
+
+    <resultMap type="DoctorMsg" id="DoctorMsgResult">
+        <result property="id"    column="id"    />
+        <result property="type"    column="type"    />
+        <result property="title"    column="title"    />
+        <result property="content"    column="content"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="isRead"    column="is_read"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="doctorId"    column="doctor_id"    />
+    </resultMap>
+
+    <sql id="selectDoctorMsgVo">
+        select id, type, title, content, create_time, is_read, update_time, doctor_id from doctor_msg
+    </sql>
+
+    <select id="selectDoctorMsgList" parameterType="DoctorMsg" resultMap="DoctorMsgResult">
+        <include refid="selectDoctorMsgVo"/>
+        <where>
+            <if test="type != null "> and type = #{type}</if>
+            <if test="title != null  and title != ''"> and title = #{title}</if>
+            <if test="content != null  and content != ''"> and content = #{content}</if>
+            <if test="isRead != null "> and is_read = #{isRead}</if>
+            <if test="doctorId != null "> and doctor_id = #{doctorId}</if>
+        </where>
+        order by is_read asc, id desc
+    </select>
+
+    <select id="selectDoctorMsgById" parameterType="Long" resultMap="DoctorMsgResult">
+        <include refid="selectDoctorMsgVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertDoctorMsg" parameterType="DoctorMsg" useGeneratedKeys="true" keyProperty="id">
+        insert into doctor_msg
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="type != null">type,</if>
+            <if test="title != null">title,</if>
+            <if test="content != null">content,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="isRead != null">is_read,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="doctorId != null">doctor_id,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="type != null">#{type},</if>
+            <if test="title != null">#{title},</if>
+            <if test="content != null">#{content},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="isRead != null">#{isRead},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="doctorId != null">#{doctorId},</if>
+         </trim>
+    </insert>
+
+    <update id="updateDoctorMsg" parameterType="DoctorMsg">
+        update doctor_msg
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="type != null">type = #{type},</if>
+            <if test="title != null">title = #{title},</if>
+            <if test="content != null">content = #{content},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="isRead != null">is_read = #{isRead},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="doctorId != null">doctor_id = #{doctorId},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteDoctorMsgById" parameterType="Long">
+        delete from doctor_msg where id = #{id}
+    </delete>
+
+    <delete id="deleteDoctorMsgByIds" parameterType="String">
+        delete from doctor_msg where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 84 - 0
fs-service/src/main/resources/mapper/his/FsDoctorConfirmMapper.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.his.mapper.FsDoctorConfirmMapper">
+
+    <resultMap type="FsDoctorConfirm" id="FsDoctorConfirmResult">
+        <result property="id"    column="id"    />
+        <result property="collectionId"    column="collection_id"    />
+        <result property="startTime"    column="start_time"    />
+        <result property="doctorEndTime"    column="doctor_end_time"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="doctorId"    column="doctor_id"    />
+        <result property="confirmSecond"    column="confirm_second"    />
+    </resultMap>
+
+    <sql id="selectFsDoctorConfirmVo">
+        select id, collection_id, start_time, doctor_end_time, create_time, update_time, doctor_id, confirm_second from fs_doctor_confirm
+    </sql>
+
+    <select id="selectFsDoctorConfirmList" parameterType="FsDoctorConfirm" resultMap="FsDoctorConfirmResult">
+        <include refid="selectFsDoctorConfirmVo"/>
+        <where>
+            <if test="collectionId != null "> and collection_id = #{collectionId}</if>
+            <if test="startTime != null "> and start_time = #{startTime}</if>
+            <if test="endTime != null "> and end_time = #{endTime}</if>
+            <if test="doctorId != null "> and doctor_id = #{doctorId}</if>
+            <if test="confirmSecond != null "> and confirm_second = #{confirmSecond}</if>
+        </where>
+    </select>
+
+    <select id="selectFsDoctorConfirmById" parameterType="Long" resultMap="FsDoctorConfirmResult">
+        <include refid="selectFsDoctorConfirmVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertFsDoctorConfirm" parameterType="FsDoctorConfirm" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_doctor_confirm
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="collectionId != null">collection_id,</if>
+            <if test="startTime != null">start_time,</if>
+            <if test="doctorEndTime != null">doctor_end_time,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="doctorId != null">doctor_id,</if>
+            <if test="confirmSecond != null">confirm_second,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="collectionId != null">#{collectionId},</if>
+            <if test="startTime != null">#{startTime},</if>
+            <if test="doctorEndTime != null">#{doctorEndTime},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="doctorId != null">#{doctorId},</if>
+            <if test="confirmSecond != null">#{confirmSecond},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsDoctorConfirm" parameterType="FsDoctorConfirm">
+        update fs_doctor_confirm
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="collectionId != null">collection_id = #{collectionId},</if>
+            <if test="startTime != null">start_time = #{startTime},</if>
+            <if test="doctorEndTime != null">doctor_end_time = #{doctorEndTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="doctorId != null">doctor_id = #{doctorId},</if>
+            <if test="confirmSecond != null">confirm_second = #{confirmSecond},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsDoctorConfirmById" parameterType="Long">
+        delete from fs_doctor_confirm where id = #{id}
+    </delete>
+
+    <delete id="deleteFsDoctorConfirmByIds" parameterType="String">
+        delete from fs_doctor_confirm where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 70 - 0
fs-service/src/main/resources/mapper/his/FsQuestionAndAnswerMapper.xml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.his.mapper.FsQuestionAndAnswerMapper">
+
+    <resultMap type="FsQuestionAndAnswer" id="FsQuestionAndAnswerResult">
+        <result property="id"    column="id"    />
+        <result property="jsonInfo"    column="json_info"    />
+        <result property="questionName"    column="question_name"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectFsQuestionAndAnswerVo">
+        select id,question_name ,json_info, create_time, update_time from fs_question_and_answer
+    </sql>
+
+    <select id="selectFsQuestionAndAnswerList" parameterType="FsQuestionAndAnswer" resultMap="FsQuestionAndAnswerResult">
+        <include refid="selectFsQuestionAndAnswerVo"/>
+        <where>
+            <if test="jsonInfo != null  and jsonInfo != ''"> and json_info = #{jsonInfo}</if>
+        </where>
+    </select>
+
+    <select id="selectFsQuestionAndAnswerById" parameterType="Long" resultMap="FsQuestionAndAnswerResult">
+        <include refid="selectFsQuestionAndAnswerVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertFsQuestionAndAnswer" parameterType="FsQuestionAndAnswer">
+        insert into fs_question_and_answer
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="jsonInfo != null">json_info,</if>
+            <if test="questionName != null">question_name,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="jsonInfo != null">#{jsonInfo},</if>
+            <if test="questionName != null">#{questionName},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsQuestionAndAnswer" parameterType="FsQuestionAndAnswer">
+        update fs_question_and_answer
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="jsonInfo != null">json_info = #{jsonInfo},</if>
+            <if test="questionName != null">question_name = #{questionName},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsQuestionAndAnswerById" parameterType="Long">
+        delete from fs_question_and_answer where id = #{id}
+    </delete>
+
+    <delete id="deleteFsQuestionAndAnswerByIds" parameterType="String">
+        delete from fs_question_and_answer where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 175 - 0
fs-service/src/main/resources/mapper/his/FsUserInformationCollectionMapper.xml

@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.his.mapper.FsUserInformationCollectionMapper">
+
+    <resultMap type="FsUserInformationCollection" id="FsUserInformationCollectionResult">
+        <result property="id"    column="id"    />
+        <result property="questionId"    column="question_id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="jsonInfo"    column="json_info"    />
+        <result property="userConfirm"    column="user_confirm"    />
+        <result property="doctorConfirm"    column="doctor_confirm"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="doctorId"    column="doctor_id"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="packageId"    column="package_id"    />
+        <result property="payType"    column="pay_type"    />
+        <result property="amount"    column="amount"    />
+        <result property="isPackage"    column="is_package"    />
+        <result property="userConfirm2"    column="user_confirm2"    />
+        <result property="packageOrderCode"    column="package_order_code"    />
+        <result property="status"    column="status"    />
+        <result property="userAdvice"    column="user_advice"    />
+        <result property="doctorAdvice"    column="doctor_advice"    />
+        <result property="doctorConfirmTime"    column="doctor_confirm_time"    />
+        <result property="sex"    column="sex"    />
+        <result property="userName"    column="user_name"    />
+        <result property="userPhoneFour"    column="user_phone_four"    />
+        <result property="allergy"    column="allergy"    />
+        <result property="remark"    column="remark"    />
+        <result property="patientId"    column="patient_id"    />
+    </resultMap>
+
+    <sql id="selectFsUserInformationCollectionVo">
+        select id, question_id, user_id, json_info, user_confirm
+             , doctor_confirm, create_time, update_time,doctor_id,company_user_id
+             ,package_id,pay_type,amount,is_package,user_confirm2,package_order_code
+             ,status,user_advice,doctor_advice,doctor_confirm_time,sex,user_name,user_phone_four
+             ,allergy,remark,patient_id  from fs_user_information_collection
+    </sql>
+
+    <select id="selectFsUserInformationCollectionList" parameterType="FsUserInformationCollection" resultMap="FsUserInformationCollectionResult">
+        <include refid="selectFsUserInformationCollectionVo"/>
+        <where>
+            <if test="questionId != null "> and question_id = #{questionId}</if>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="jsonInfo != null  and jsonInfo != ''"> and json_info = #{jsonInfo}</if>
+            <if test="userConfirm != null "> and user_confirm = #{userConfirm}</if>
+            <if test="doctorConfirm != null "> and doctor_confirm = #{doctorConfirm}</if>
+        </where>
+    </select>
+
+    <select id="selectFsUserInformationCollectionById" parameterType="Long" resultMap="FsUserInformationCollectionResult">
+        <include refid="selectFsUserInformationCollectionVo"/>
+        where id = #{id}
+    </select>
+
+    <select id="selectFsUserInformationCollectionByUserId" parameterType="Long" resultMap="FsUserInformationCollectionResult">
+        <include refid="selectFsUserInformationCollectionVo"/>
+        where user_id = #{userId}
+    </select>
+
+    <select id="selectByUserId" parameterType="Long" resultMap="FsUserInformationCollectionResult">
+        <include refid="selectFsUserInformationCollectionVo"/>
+        where user_id = #{userId}
+    </select>
+
+    <select id="selectByOrderCode" parameterType="String" resultMap="FsUserInformationCollectionResult">
+        <include refid="selectFsUserInformationCollectionVo"/>
+        where package_order_code = #{orderCode}
+    </select>
+
+    <insert id="insertFsUserInformationCollection" parameterType="FsUserInformationCollection" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_user_information_collection
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="questionId != null">question_id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="jsonInfo != null">json_info,</if>
+            <if test="userConfirm != null">user_confirm,</if>
+            <if test="doctorConfirm != null">doctor_confirm,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="doctorId != null">doctor_id,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="packageId != null">package_id,</if>
+            <if test="payType != null">pay_type,</if>
+            <if test="amount != null">amount,</if>
+            <if test="userAdvice != null">user_advice,</if>
+            <if test="doctorAdvice != null">doctor_advice,</if>
+            <if test="isPackage != null">is_package,</if>
+            <if test="userConfirm2 != null">user_confirm2,</if>
+            <if test="packageOrderCode != null">package_order_code,</if>
+            <if test="status != null">status,</if>
+            <if test="doctorConfirmTime != null">doctor_confirm_time,</if>
+            <if test="userPhoneFour != null">user_phone_four,</if>
+            <if test="userName != null">user_name,</if>
+            <if test="sex != null">sex,</if>
+            <if test="allergy != null">allergy,</if>
+            <if test="remark != null">remark,</if>
+            <if test="patientId != null">patient_id,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="questionId != null">#{questionId},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="jsonInfo != null">#{jsonInfo},</if>
+            <if test="userConfirm != null">#{userConfirm},</if>
+            <if test="doctorConfirm != null">#{doctorConfirm},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="doctorId != null">#{doctorId},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="packageId != null">#{packageId},</if>
+            <if test="payType != null">#{payType},</if>
+            <if test="amount != null">#{amount},</if>
+            <if test="userAdvice != null">#{userAdvice},</if>
+            <if test="doctorAdvice != null">#{doctorAdvice},</if>
+            <if test="isPackage != null">#{isPackage},</if>
+            <if test="userConfirm2 != null">#{userConfirm2},</if>
+            <if test="packageOrderCode != null">#{packageOrderCode},</if>
+            <if test="status != null">#{status},</if>
+            <if test="doctorConfirmTime != null">#{doctorConfirmTime},</if>
+            <if test="sex != null">#{sex},</if>
+            <if test="userName != null">#{userName},</if>
+            <if test="userPhoneFour != null">#{userPhoneFour},</if>
+            <if test="allergy != null">#{allergy},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="patientId != null">#{patientId},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsUserInformationCollection" parameterType="FsUserInformationCollection">
+        update fs_user_information_collection
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="questionId != null">question_id = #{questionId},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="jsonInfo != null">json_info = #{jsonInfo},</if>
+            <if test="userConfirm != null">user_confirm = #{userConfirm},</if>
+            <if test="doctorConfirm != null">doctor_confirm = #{doctorConfirm},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="doctorId != null">doctor_id = #{doctorId},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="packageId != null">package_id = #{packageId},</if>
+            <if test="payType != null">pay_type = #{payType},</if>
+            <if test="amount != null">amount = #{amount},</if>
+            <if test="userAdvice != null">user_advice = #{userAdvice},</if>
+            <if test="doctorAdvice != null">doctor_advice = #{doctorAdvice},</if>
+            <if test="userConfirm2 != null">user_confirm2 = #{userConfirm2},</if>
+            <if test="isPackage != null">is_package = #{isPackage},</if>
+            <if test="packageOrderCode != null">package_order_code = #{packageOrderCode},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="doctorConfirmTime != null">doctor_confirm_time = #{doctorConfirmTime},</if>
+            <if test="sex != null">sex = #{sex},</if>
+            <if test="userName != null">user_name = #{userName},</if>
+            <if test="userPhoneFour != null">user_phone_four = #{userPhoneFour},</if>
+            <if test="allergy != null">allergy = #{allergy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="patientId != null">patient_id = #{patientId},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsUserInformationCollectionById" parameterType="Long">
+        delete from fs_user_information_collection where id = #{id}
+    </delete>
+
+    <delete id="deleteFsUserInformationCollectionByIds" parameterType="String">
+        delete from fs_user_information_collection where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 224 - 0
fs-service/src/main/resources/mapper/his/FsUserInformationCollectionScheduleMapper.xml

@@ -0,0 +1,224 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.his.mapper.FsUserInformationCollectionScheduleMapper">
+
+    <resultMap type="FsUserInformationCollectionSchedule" id="FsUserInformationCollectionScheduleResult">
+        <result property="id"    column="id"    />
+        <result property="collectionId"    column="collection_id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="doctorId"    column="doctor_id"    />
+        <result property="packageId"    column="package_id"    />
+        <result property="packageName"    column="package_name"    />
+        <result property="currentStep"    column="current_step"    />
+        <result property="status"    column="status"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="completedTime"    column="completed_time"    />
+        <result property="terminatedTime"    column="terminated_time"    />
+        <result property="terminatedBy"    column="terminated_by"    />
+        <result property="remark"    column="remark"    />
+        <result property="orderCode"    column="order_code"    />
+    </resultMap>
+
+    <sql id="selectFsUserInformationCollectionScheduleVo">
+        select id, collection_id, user_id, doctor_id, package_id, package_name, current_step, status, create_time, completed_time, terminated_time, terminated_by, remark,order_code from fs_user_information_collection_schedule
+    </sql>
+
+    <select id="selectFsUserInformationCollectionScheduleList" parameterType="FsUserInformationCollectionSchedule" resultMap="FsUserInformationCollectionScheduleResult">
+        SELECT
+        s.id,
+        s.collection_id,
+        s.user_id,
+        c.user_name,
+        s.doctor_id,
+        d.doctor_name,
+        s.package_id,
+        s.package_name,
+        s.current_step,
+        s.status,
+        s.create_time,
+        s.completed_time,
+        s.terminated_time,
+        s.terminated_by,
+        s.remark,
+        s.order_code
+        FROM
+        fs_user_information_collection_schedule s
+        LEFT JOIN fs_user_information_collection c ON s.collection_id = c.id
+        LEFT JOIN fs_doctor d ON s.doctor_id = d.doctor_id
+        <where>
+            <if test="collectionId != null "> and s.collection_id = #{collectionId}</if>
+            <if test="userId != null "> and s.user_id = #{userId}</if>
+            <if test="userName != null and userName != ''"> AND c.user_name LIKE CONCAT('%', #{userName}, '%')</if>
+            <if test="doctorId != null"> AND s.doctor_id = #{doctorId}</if>
+            <if test="doctorName != null and doctorName != ''"> AND d.doctor_name LIKE CONCAT('%', #{doctorName}, '%')</if>
+            <if test="packageId != null "> and s.package_id = #{packageId}</if>
+            <if test="packageName != null  and packageName != ''"> and s.package_name like concat('%', #{packageName}, '%')</if>
+            <if test="currentStep != null "> and s.current_step = #{currentStep}</if>
+            <if test="status != null "> and s.status = #{status}</if>
+            <if test="completedTime != null "> and s.completed_time = #{completedTime}</if>
+            <if test="terminatedTime != null "> and s.terminated_time = #{terminatedTime}</if>
+            <if test="terminatedBy != null  and terminatedBy != ''"> and s.terminated_by = #{terminatedBy}</if>
+            <if test="orderCode != null and orderCode != ''"> AND s.order_code = #{orderCode}</if>
+        </where>
+    </select>
+
+    <select id="getUserRecordLatestTaskProcess"
+            parameterType="FsUserInformationCollectionSchedule"
+            resultMap="FsUserInformationCollectionScheduleResult">
+        SELECT
+        s.id,
+        s.collection_id,
+        s.user_id,
+        c.user_name,
+        s.doctor_id,
+        d.doctor_name,
+        s.package_id,
+        s.package_name,
+        s.current_step,
+        s.status,
+        s.create_time,
+        s.completed_time,
+        s.terminated_time,
+        s.terminated_by,
+        s.remark,
+        s.order_code
+        FROM
+        fs_user_information_collection_schedule s
+        LEFT JOIN fs_user_information_collection c ON s.collection_id = c.id
+        LEFT JOIN fs_doctor d ON s.doctor_id = d.doctor_id
+        <where>
+            <!-- 动态过滤条件 -->
+            <if test="collectionId != null"> AND s.collection_id = #{collectionId}</if>
+            <if test="userId != null"> AND s.user_id = #{userId}</if>
+            <if test="userName != null and userName != ''"> AND c.user_name LIKE CONCAT('%', #{userName}, '%')</if>
+            <if test="doctorId != null"> AND s.doctor_id = #{doctorId}</if>
+            <if test="doctorName != null and doctorName != ''"> AND d.doctor_name LIKE CONCAT('%', #{doctorName}, '%')</if>
+            <if test="packageId != null"> AND s.package_id = #{packageId}</if>
+            <if test="packageName != null and packageName != ''"> AND s.package_name LIKE CONCAT('%', #{packageName}, '%')</if>
+            <if test="currentStep != null"> AND s.current_step = #{currentStep}</if>
+            <if test="status != null"> AND s.status = #{status}</if>
+            <if test="completedTime != null"> AND s.completed_time = #{completedTime}</if>
+            <if test="terminatedTime != null"> AND s.terminated_time = #{terminatedTime}</if>
+            <if test="terminatedBy != null and terminatedBy != ''"> AND s.terminated_by = #{terminatedBy}</if>
+            <if test="orderCode != null and orderCode != ''"> AND s.order_code = #{orderCode}</if>
+            AND s.id = (
+            SELECT MAX(s2.id)
+            FROM fs_user_information_collection_schedule s2
+            WHERE s2.collection_id = s.collection_id
+            <if test="collectionId != null"> AND s2.collection_id = #{collectionId}</if>
+            <if test="userId != null"> AND s2.user_id = #{userId}</if>
+            <if test="userName != null and userName != ''"> AND c.user_name LIKE CONCAT('%', #{userName}, '%')</if>
+            <if test="doctorId != null"> AND s2.doctor_id = #{doctorId}</if>
+            <if test="doctorName != null and doctorName != ''"> AND d.doctor_name LIKE CONCAT('%', #{doctorName}, '%')</if>
+            <if test="packageId != null"> AND s2.package_id = #{packageId}</if>
+            <if test="packageName != null and packageName != ''"> AND s2.package_name LIKE CONCAT('%', #{packageName}, '%')</if>
+            <if test="currentStep != null"> AND s2.current_step = #{currentStep}</if>
+            <if test="status != null"> AND s2.status = #{status}</if>
+            <if test="completedTime != null"> AND s2.completed_time = #{completedTime}</if>
+            <if test="terminatedTime != null"> AND s2.terminated_time = #{terminatedTime}</if>
+            <if test="terminatedBy != null and terminatedBy != ''"> AND s2.terminated_by = #{terminatedBy}</if>
+            <if test="orderCode != null and orderCode != ''"> AND s2.order_code = #{orderCode}</if>
+            )
+        </where>
+    </select>
+
+    <select id="selectFsUserInformationCollectionScheduleById" parameterType="Long" resultMap="FsUserInformationCollectionScheduleResult">
+        <include refid="selectFsUserInformationCollectionScheduleVo"/>
+        where id = #{id}
+    </select>
+
+    <select id="selectCollectionScheduleByCollectionId" parameterType="Long" resultMap="FsUserInformationCollectionScheduleResult">
+        SELECT
+            id, collection_id, user_id, doctor_id,
+            package_id, package_name, current_step, status, create_time,
+            completed_time, terminated_time, terminated_by, remark,order_code
+        FROM fs_user_information_collection_schedule
+        WHERE collection_id = #{collectionId}
+        ORDER BY id DESC
+            LIMIT 1
+    </select>
+    <select id="selectFsUserInformationCollectionByOrderCode" parameterType="String" resultMap="FsUserInformationCollectionScheduleResult">
+        <include refid="selectFsUserInformationCollectionScheduleVo"/>
+        where order_code = #{orderCode}
+    </select>
+
+    <select id="selectCollectionScheduleRunningByCollectionId" parameterType="Long" resultMap="FsUserInformationCollectionScheduleResult">
+        SELECT
+            id, collection_id, user_id, doctor_id,
+            package_id, package_name, current_step, status, create_time,
+            completed_time, terminated_time, terminated_by, remark,order_code
+        FROM fs_user_information_collection_schedule
+        WHERE collection_id = #{collectionId}
+          AND status = 1
+        ORDER BY id DESC
+            LIMIT 1
+    </select>
+
+
+    <insert id="insertFsUserInformationCollectionSchedule" parameterType="FsUserInformationCollectionSchedule" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_user_information_collection_schedule
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="collectionId != null">collection_id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="doctorId != null">doctor_id,</if>
+            <if test="packageId != null">package_id,</if>
+            <if test="packageName != null">package_name,</if>
+            <if test="currentStep != null">current_step,</if>
+            <if test="status != null">status,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="completedTime != null">completed_time,</if>
+            <if test="terminatedTime != null">terminated_time,</if>
+            <if test="terminatedBy != null">terminated_by,</if>
+            <if test="remark != null">remark,</if>
+            <if test="orderCode != null">order_code,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="collectionId != null">#{collectionId},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="doctorId != null">#{doctorId},</if>
+            <if test="packageId != null">#{packageId},</if>
+            <if test="packageName != null">#{packageName},</if>
+            <if test="currentStep != null">#{currentStep},</if>
+            <if test="status != null">#{status},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="completedTime != null">#{completedTime},</if>
+            <if test="terminatedTime != null">#{terminatedTime},</if>
+            <if test="terminatedBy != null">#{terminatedBy},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="orderCode != null">#{orderCode},</if>
+        </trim>
+    </insert>
+
+    <update id="updateFsUserInformationCollectionSchedule" parameterType="FsUserInformationCollectionSchedule">
+        update fs_user_information_collection_schedule
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="collectionId != null">collection_id = #{collectionId},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="doctorId != null">doctor_id = #{doctorId},</if>
+            <if test="packageId != null">package_id = #{packageId},</if>
+            <if test="packageName != null">package_name = #{packageName},</if>
+            <if test="currentStep != null">current_step = #{currentStep},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="completedTime != null">completed_time = #{completedTime},</if>
+            <if test="terminatedTime != null">terminated_time = #{terminatedTime},</if>
+            <if test="terminatedBy != null">terminated_by = #{terminatedBy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="orderCode != null">order_code = #{orderCode},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsUserInformationCollectionScheduleById" parameterType="Long">
+        delete from fs_user_information_collection_schedule where id = #{id}
+    </delete>
+
+    <delete id="deleteFsUserInformationCollectionScheduleByIds" parameterType="String">
+        delete from fs_user_information_collection_schedule where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

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

@@ -2001,6 +2001,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test = "maps.nickname != null and  maps.nickname !='' " >
                 AND u.nick_name LIKE CONCAT("%",#{maps.nickname},"%")
             </if >
+            <if test = "maps.nicknameExact != null and  maps.nicknameExact !='' " >
+                AND u.nick_name = #{maps.nicknameExact}
+            </if >
             <if test = "maps.userId != null and  maps.userId !='' " >
                 AND u.user_id = #{maps.userId}
             </if >
@@ -2027,6 +2030,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test = "maps.projectId != null" >
                 AND ucu.project_id = #{maps.projectId}
             </if >
+            <if test = "maps.companyUserIds != null and  maps.companyUserIds.length > 0 " >
+                AND ucu.company_user_id in
+                <foreach collection="maps.companyUserIds" item="item" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if >
         </where>
         ORDER BY
         user_id DESC

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

@@ -77,7 +77,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="descs != null">#{descs},</if>
             <if test="content != null">#{content},</if>
             <if test="imgUrl != null">#{imgUrl},</if>
-            <if test="images != null">images,</if>
+            <if test="images != null">#{images},</if>
             <if test="products != null">#{products},</if>
             <if test="money != null">#{money},</if>
             <if test="payMoney != null">#{payMoney},</if>

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

@@ -71,6 +71,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="returnAddress"    column="return_address"    />
         <result property="brand"    column="brand"    />
         <result property="foodProductionLicenseCode"    column="food_production_license_code"    />
+        <result property="originPlace"    column="origin_place"    />
+        <result property="netContent"    column="net_content"    />
+        <result property="shelfLife"    column="shelf_life"    />
+        <result property="domesticImported"    column="domestic_imported"    />
     </resultMap>
 
     <sql id="selectFsStoreProductVo">
@@ -81,7 +85,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                integral, product_type, prescribe_code, prescribe_spec, prescribe_factory, prescribe_name,
                is_display,tui_cate_id,company_ids,is_drug,drug_image,drug_reg_cert_no,common_name,dosage_form,
                unit_price,batch_number,mah,mah_address,manufacturer,manufacturer_address,indications,dosage,
-               adverse_reactions,contraindications,precautions,is_audit,store_id,return_address,brand,food_production_license_code
+               adverse_reactions,contraindications,precautions,is_audit,store_id,return_address,brand,food_production_license_code,
+               origin_place,net_content,shelf_life,domestic_imported
                from fs_store_product_scrm
     </sql>
 
@@ -93,7 +98,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                p.integral, p.product_type, p.prescribe_code, p.prescribe_spec, p.prescribe_factory, p.prescribe_name,
                p.is_display,p.tui_cate_id,p.company_ids,p.is_drug,p.drug_image,p.drug_reg_cert_no,p.common_name,p.dosage_form,
                p.unit_price,p.batch_number,p.mah,p.mah_address,p.manufacturer,p.manufacturer_address,p.indications,p.dosage,
-               p.adverse_reactions,p.contraindications,p.precautions,p.is_audit,p.store_id,p.return_address,p.brand,p.food_production_license_code
+               p.adverse_reactions,p.contraindications,p.precautions,p.is_audit,p.store_id,p.return_address,p.brand,p.food_production_license_code,
+               p.origin_place,p.net_content,p.shelf_life,p.domestic_imported
         from fs_store_product_scrm p
     </sql>
 
@@ -164,6 +170,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="storeId != null and storeId != ''">and store_id = #{storeId} </if>
             <if test="brand != null and brand != ''">and brand = #{brand} </if>
             <if test="foodProductionLicenseCode != null and foodProductionLicenseCode != ''">and food_production_license_code = #{foodProductionLicenseCode} </if>
+            <if test="originPlace != null and originPlace != ''">and origin_place like concat('%', #{originPlace}, '%') </if>
+            <if test="netContent != null and netContent != ''">and net_content = #{netContent} </if>
+            <if test="shelfLife != null">and shelf_life = #{shelfLife} </if>
+            <if test="domesticImported != null and domesticImported != ''">and domestic_imported = #{domesticImported} </if>
         </where>
     </select>
 
@@ -262,6 +272,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="returnAddress != null">return_address,</if>
             <if test="brand != null and brand != ''">brand,</if>
             <if test="foodProductionLicenseCode != null and foodProductionLicenseCode != ''">food_production_license_code,</if>
+            <if test="originPlace != null and originPlace != ''">origin_place,</if>
+            <if test="netContent != null and netContent != ''">net_content,</if>
+            <if test="shelfLife != null">shelf_life,</if>
+            <if test="domesticImported != null and domesticImported != ''">domestic_imported,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="image != null and image != ''">#{image},</if>
@@ -329,6 +343,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="returnAddress != null">#{returnAddress},</if>
             <if test="brand != null and brand != ''">#{brand},</if>
             <if test="foodProductionLicenseCode != null and foodProductionLicenseCode != ''">#{foodProductionLicenseCode},</if>
+            <if test="originPlace != null and originPlace != ''">#{originPlace},</if>
+            <if test="netContent != null and netContent != ''">#{netContent},</if>
+            <if test="shelfLife != null">#{shelfLife},</if>
+            <if test="domesticImported != null and domesticImported != ''">#{domesticImported},</if>
          </trim>
     </insert>
 
@@ -400,6 +418,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="returnAddress != null">return_address = #{returnAddress},</if>
             <if test="brand != null and brand != ''">brand = #{brand},</if>
             <if test="foodProductionLicenseCode != null and foodProductionLicenseCode != ''">food_production_license_code = #{foodProductionLicenseCode},</if>
+            <if test="originPlace != null">origin_place = #{originPlace},</if>
+            <if test="netContent != null">net_content = #{netContent},</if>
+            <if test="shelfLife != null">shelf_life = #{shelfLife},</if>
+            <if test="domesticImported != null">domestic_imported = #{domesticImported},</if>
         </trim>
         where product_id = #{productId}
     </update>

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

@@ -1067,6 +1067,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="payEndTime != null and payEndTime != ''">
                 AND o.pay_time &lt;= #{payEndTime}
             </if>
+            <if test="userPhone != null and userPhone != ''">
+                AND o.user_phone = #{userPhone}
+            </if>
         </where>
         ORDER BY o.create_time DESC
     </select>

+ 26 - 0
fs-user-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -33,6 +33,8 @@ import com.fs.company.domain.CompanyMoneyLogs;
 import com.fs.company.service.ICompanyMoneyLogsService;
 import com.fs.core.utils.OrderCodeUtils;
 import com.fs.course.config.CourseConfig;
+import com.fs.course.domain.FsProjectAddressConfig;
+import com.fs.course.service.IFsProjectAddressConfigService;
 import com.fs.course.service.IHuaweiVodService;
 import com.fs.event.TemplateBean;
 import com.fs.event.TemplateEvent;
@@ -78,6 +80,7 @@ import org.springframework.web.multipart.MultipartFile;
 import java.io.*;
 import java.math.BigDecimal;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 
 import static com.fs.common.utils.SecurityUtils.getUserId;
 
@@ -134,6 +137,8 @@ public class CommonController {
 
 	@Autowired
 	private IQwAppContactWayService qwAppContactWayService;
+	@Autowired
+	private IFsProjectAddressConfigService projectAddressConfigService;
 
 //	@Autowired
 //	private RocketMQTemplate rocketMQTemplate;
@@ -636,4 +641,25 @@ public class CommonController {
 		return ResponseResult.ok(config.getIsOpenIM());
 	}
 
+	@ApiOperation("获取项目对应api请求域名地址")
+	@GetMapping("/getDomain")
+	public R getDomain(@RequestParam String projectCode) {
+		String redisKey = "projectCode:" + projectCode;
+		FsProjectAddressConfig config = redisCache.getCacheObject(redisKey);
+		if (Objects.isNull(config)) {
+			config = projectAddressConfigService.selectDomainByCode(projectCode);
+			redisCache.setCacheObject(redisKey, config, 5, TimeUnit.MINUTES);
+		}
+		String addressUrl = "";
+		String imgPath = "";
+		Integer sendType = null;
+
+		if (Objects.nonNull(config)) {
+			addressUrl = config.getAddressUrl();
+			imgPath = config.getBucketPath();
+			sendType = config.getSendType();
+		}
+
+		return R.ok().put("addressUrl", addressUrl).put("imgpath", imgPath).put("sendType", sendType);
+	}
 }

+ 100 - 0
fs-user-app/src/main/java/com/fs/app/controller/CompanyUserController.java

@@ -10,6 +10,7 @@ import com.fs.app.annotation.Login;
 import com.fs.app.param.FsBindCompanyUserParam;
 import com.fs.common.annotation.Log;
 import com.fs.common.config.FSConfig;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.core.redis.RedisCache;
@@ -17,6 +18,7 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.file.OssException;
 import com.fs.common.utils.SecurityUtils;
+import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.sign.Md5Utils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUserCard;
@@ -32,11 +34,15 @@ import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
 import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.vo.AudioVO;
+import com.fs.his.domain.FsUserInformationCollection;
 import com.fs.his.param.*;
 import com.fs.his.service.IFsPrescribeService;
+import com.fs.his.service.IFsQuestionAndAnswerService;
+import com.fs.his.service.IFsUserInformationCollectionService;
 import com.fs.his.vo.FsPrescribeListDVO;
 import com.fs.his.vo.FsPrescribeListUVO;
 import com.fs.his.vo.FsPrescribeListVO;
+import com.fs.his.vo.OptionsVO;
 import com.fs.sop.domain.QwSopTempVoice;
 import com.fs.sop.service.IQwSopTempVoiceService;
 import com.fs.system.oss.CloudStorageService;
@@ -54,6 +60,7 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.commons.CommonsMultipartFile;
@@ -87,6 +94,10 @@ public class CompanyUserController extends  AppBaseController {
     private FastgptChatVoiceHomoMapper fastgptChatVoiceHomoMapper;
     @Autowired
     AiHostProper aiHostProper;
+    @Autowired
+    private IFsUserInformationCollectionService fsUserInformationCollectionService;
+    @Autowired
+    private IFsQuestionAndAnswerService fsQuestionAndAnswerService;
 
     public static final String SOP_TEMP_VOICE_KEY = "sop:tempVoice";
     @PostMapping("/login")
@@ -385,5 +396,94 @@ public class CompanyUserController extends  AppBaseController {
         return R.ok().put("data",listPageInfo);
     }
 
+    /**
+     * 查询用户信息采集列表问答列表
+     */
+    @GetMapping("/questionAndAnswer/allList")
+    public TableDataInfo getQuestionAndAnswer()
+    {
+        List<OptionsVO> list = fsQuestionAndAnswerService.selectAllQuestionOptions();
+        return getDataTable(list);
+    }
+
+
+    /**
+     * 查询用户信息采集列表
+     */
+    @GetMapping("/informationCollection/list")
+    @Login
+    public TableDataInfo list(FsUserInformationCollection fsUserInformationCollection)
+    {
+        startPage();
+        Long companyUserId = getCompanyUserId();
+        fsUserInformationCollection.setCompanyUserId(companyUserId);
+        List<FsUserInformationCollection> list = fsUserInformationCollectionService.selectFsUserInformationCollectionList(fsUserInformationCollection);
+        return getDataTable(list);
+    }
+
+
+    /**
+     * 获取用户信息采集详细信息
+     */
+    @GetMapping(value = "/informationCollection/{id}")
+    public R getInformationCollectionInfo(@PathVariable("id") Long id)
+    {
+        return R.ok().put("data",fsUserInformationCollectionService.selectFsUserInformationCollectionById(id));
+    }
+
+    /**
+     * 新增用户信息采集
+     */
+    @Login
+    @PostMapping("/informationCollection")
+    public R add(@RequestBody FsUserInformationCollectionParam fsUserInformationCollection)
+    {
+        Long companyUserId = getCompanyUserId();
+        //查询用户是否绑定销售
+        Long userId = fsUserInformationCollection.getUserId();
+        if (userId == null || userId < 0){
+            return R.error("请选择绑定用户");
+        }
+        CompanyUserUser companyUserUser = new CompanyUserUser();
+        companyUserUser.setCompanyUserId(companyUserId);
+        companyUserUser.setUserId(userId);
+        List<CompanyUserUser> companyUserUsers = companyUserUserService.selectCompanyUserUserList(companyUserUser);
+        if (companyUserUsers == null || companyUserUsers.isEmpty()){
+            return R.error("用户未绑定该销售");
+        }
+
+        fsUserInformationCollection.setCompanyUserId(companyUserId);
+        Long id = fsUserInformationCollectionService.insertFsUserInformationCollection(fsUserInformationCollection);
+        return id == null?R.error("新增失败"):R.ok().put("data",id);
+    }
+
+    /**
+     * 修改用户信息采集
+     */
+    @PutMapping("/informationCollection")
+    @Login
+    public R edit(@RequestBody FsUserInformationCollectionParam fsUserInformationCollection)
+    {
+        Long companyUserId = getCompanyUserId();
+        fsUserInformationCollection.setCompanyUserId(companyUserId);
+        fsUserInformationCollectionService.updateFsUserInformationCollection(fsUserInformationCollection);
+        return R.ok();
+    }
+
+    /**
+     * 删除用户信息采集
+     */
+    @DeleteMapping("/informationCollection/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsUserInformationCollectionService.deleteFsUserInformationCollectionByIds(ids));
+    }
+
+    @GetMapping("/informationCollection/getInfo")
+    public AjaxResult getInformationCollection(FsUserInformationCollection fsUserInformationCollection){
+        return AjaxResult.success(fsUserInformationCollectionService.getInfo(fsUserInformationCollection));
+    }
+
+
 
 }

+ 78 - 0
fs-user-app/src/main/java/com/fs/app/controller/FsUserInformationCollectionController.java

@@ -0,0 +1,78 @@
+package com.fs.app.controller;
+
+import com.fs.app.annotation.Login;
+import com.fs.common.core.domain.R;
+import com.fs.company.service.ICompanyUserUserService;
+import com.fs.his.domain.FsUserInformationCollection;
+import com.fs.his.param.FsUserInformationCollectionParam;
+import com.fs.his.service.IFsUserInformationCollectionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+
+/**
+ * 用户信息采集Controller
+ *
+ * @author fs
+ * @date 2025-10-14
+ */
+@RestController
+@RequestMapping("/user/collection")
+public class FsUserInformationCollectionController extends AppBaseController
+{
+    @Autowired
+    private IFsUserInformationCollectionService fsUserInformationCollectionService;
+
+
+
+    /**
+     * 获取用户信息采集详细信息
+     */
+    @GetMapping(value = "/{id}")
+    @Login
+    public R getInfo(@PathVariable("id") Long id)
+    {
+
+        FsUserInformationCollection info = fsUserInformationCollectionService.selectFsUserInformationCollectionById(id);
+        if (info == null){
+            return R.error("未查询到信息!");
+        }
+        String userId = getUserId();
+        if (info.getUserId() != Long.parseLong(userId)){
+            return R.error("未查询您的采集信息!");
+        }
+        if (info.getDoctorConfirm() == 0){
+            info.setPackageId(null);
+        }
+        return R.ok().put("data", info);
+    }
+
+
+    /**
+     * 修改用户信息采集
+     */
+    @Login
+    @PutMapping
+    public R edit(@RequestBody FsUserInformationCollectionParam fsUserInformationCollection)
+    {
+        Long id = fsUserInformationCollection.getId();
+        if (id == null){
+            return R.error("未查询到信息!");
+        }
+        FsUserInformationCollection info = fsUserInformationCollectionService.selectFsUserInformationCollectionById(id);
+        if (info == null){
+            return R.error("未查询到信息!");
+        }
+        String userId = getUserId();
+        if (info.getUserId() != Long.parseLong(userId)){
+            return R.error("您没有修改信息的权限");
+        }
+        if (!info.getUserId().equals(fsUserInformationCollection.getUserId())){
+            return R.error("用户id不能修改");
+        }
+        fsUserInformationCollectionService.updateFsUserInformationCollection(fsUserInformationCollection);
+        return R.ok();
+    }
+
+
+}

+ 4 - 0
fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

@@ -11,6 +11,7 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.app.annotation.Login;
 import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.course.dto.BatchSendCourseDTO;
 import com.fs.course.param.*;
@@ -85,6 +86,9 @@ public class CourseFsUserController extends AppBaseController {
     public R getCourseByVideoId(@RequestParam("videoId") Long videoId)
     {
         FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
+        if (CloudHostUtils.hasCloudHostName("金牛明医","康年堂")){
+            course.setCourseName("");
+        }
         return R.ok().put("data",course);
     }
 

+ 22 - 4
fs-user-app/src/main/java/com/fs/app/controller/store/CompanyOrderScrmController.java

@@ -55,7 +55,11 @@ public class CompanyOrderScrmController extends AppBaseController {
 
         PageHelper.startPage(param.getPage(), param.getPageSize());
         param.setIsDisplay(0);
-        List<FsStoreProductListQueryVO> productList=productService.selectFsStoreProductListQuery(param);
+        List<FsStoreProductListQueryVO> productList = productService.selectFsStoreProductListQuery(param);
+        for (FsStoreProductListQueryVO fsStoreProductListQueryVO : productList) {
+            fsStoreProductListQueryVO.setCompanyId(companyUser.getCompanyId());
+            fsStoreProductListQueryVO.setCompanyUserId(companyUser.getUserId());
+        }
         PageInfo<FsStoreProductListQueryVO> listPageInfo=new PageInfo<>(productList);
         return R.ok().put("data",listPageInfo);
     }
@@ -64,9 +68,11 @@ public class CompanyOrderScrmController extends AppBaseController {
     @ApiOperation("制单")
     @GetMapping("/createSalesOrder")
     public R createSalesOrder(@RequestParam("token")String token,
-                              @RequestParam("cateIds")String cateIds,
+                              @RequestParam(value = "cateIds", required = false)String cateIds,
                               @RequestParam(value = "orderType",required = false)Integer orderType,
                               @RequestParam(value = "orderMedium",required = false)Integer orderMedium,
+                              @RequestParam(value = "isPackage", required = false, defaultValue = "0")Integer isPackage,
+                              @RequestParam(value = "packageId", required = false)String packageId,
                               HttpServletRequest request){
         Long userId=redisCache.getCacheObject("company-user-token:"+token);
         if(userId==null){
@@ -79,7 +85,18 @@ public class CompanyOrderScrmController extends AppBaseController {
         if(!companyUser.getStatus().equals("0")){
             return R.error("用户已禁用");
         }
-        return orderService.createSalesOrder(companyUser,cateIds,orderType,orderMedium);
+        if(isPackage == 0){
+            return orderService.createSalesOrder(companyUser,cateIds,orderType,orderMedium);
+        } else if(isPackage == 1){
+            // 套餐包制单
+            if (packageId == null){
+                return R.error("套餐包ID不能为空");
+            }
+            return orderService.createPackageSalesOrder(companyUser,packageId,orderType,orderMedium);
+        } else {
+            return R.error("参数错误");
+        }
+
     }
 
     @ApiOperation("改价")
@@ -89,6 +106,7 @@ public class CompanyOrderScrmController extends AppBaseController {
                                    @RequestParam(value = "money",required = false) BigDecimal money,
                                    @RequestParam(value = "payAmount",required = false) BigDecimal payAmount,
                                    @RequestParam(value = "payType",required = false) Integer payType,
+                                   @RequestParam(value = "isPackage",required = false,defaultValue = "0")Integer isPackage,
                                    HttpServletRequest request){
         Long userId=redisCache.getCacheObject("company-user-token:"+token);
         if(userId==null){
@@ -100,7 +118,7 @@ public class CompanyOrderScrmController extends AppBaseController {
         if (payAmount == null){
             payAmount = BigDecimal.ZERO;
         }
-        return orderService.updateSalseOrderMoney(createOrderKey,money,payAmount,payType);
+        return orderService.updateSalseOrderMoney(createOrderKey,money,payAmount,payType, isPackage);
     }
 
     @ApiOperation("商品改价")

+ 5 - 2
fs-user-app/src/main/java/com/fs/framework/aspectj/UserOperationLogAspect.java

@@ -69,7 +69,7 @@ public class UserOperationLogAspect {
     @AfterReturning(pointcut = "@annotation(userOpLog)", returning = "result")
     public void afterReturning(JoinPoint joinPoint,UserOperationLog userOpLog, Object result) {
         FsUserOperationLog operationLog = LOG_HOLDER.get();
-        if (operationLog == null) return;
+        if (operationLog == null || StringUtils.isBlank(operationLog.getOperationType())) return;
 
         try {
             if (operationLog.getUserId() == null){
@@ -117,7 +117,10 @@ public class UserOperationLogAspect {
             //用户
             Long userId =null;
             try {
-                userId = Long.valueOf(jwtUtils.getClaimByToken(ServletUtils.getRequest().getHeader("APPToken")).getSubject().toString());
+                String appToken = ServletUtils.getRequest().getHeader("APPToken");
+                if (StringUtils.isNotBlank(appToken)){
+                    userId = Long.valueOf(jwtUtils.getClaimByToken(appToken).getSubject().toString());
+                }
             } catch (Exception ie){
                 log.info("获取用户id失败");
             }