瀏覽代碼

Merge branch 'master' into 红德堂APP调试

Long 4 天之前
父節點
當前提交
a04b6f5ac1
共有 100 個文件被更改,包括 4842 次插入604 次删除
  1. 24 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyMenuController.java
  2. 81 0
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  3. 120 0
      fs-admin/src/main/java/com/fs/his/controller/HzOMSErpApiController.java
  4. 110 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyIntegralController.java
  5. 28 9
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  6. 211 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserDelayTimeController.java
  7. 7 3
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java
  8. 27 2
      fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java
  9. 8 0
      fs-doctor-app/src/main/java/com/fs/app/controller/CommonController.java
  10. 1 0
      fs-framework/src/main/java/com/fs/framework/config/SecurityConfig.java
  11. 40 0
      fs-service/src/main/java/com/fs/company/domain/CompanyUserDelayTime.java
  12. 72 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyUserDelayTimeMapper.java
  13. 10 0
      fs-service/src/main/java/com/fs/company/param/CompanyUserDelayTimeParam.java
  14. 63 0
      fs-service/src/main/java/com/fs/company/service/ICompanyUserDelayTimeService.java
  15. 29 9
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  16. 106 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserDelayTimeServiceImpl.java
  17. 5 0
      fs-service/src/main/java/com/fs/company/vo/CompanyUserQwListVO.java
  18. 1 0
      fs-service/src/main/java/com/fs/course/config/CourseConfig.java
  19. 5 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCoursePeriod.java
  20. 5 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCourseTrainingCamp.java
  21. 4 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java
  22. 16 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  23. 2 2
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java
  24. 10 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCoursePeriodMapper.java
  25. 1 1
      fs-service/src/main/java/com/fs/course/param/FsCourseRedPacketLogParam.java
  26. 2 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseQuestionBankService.java
  27. 1 1
      fs-service/src/main/java/com/fs/course/service/IFsCourseRedPacketLogService.java
  28. 1 1
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  29. 109 4
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java
  30. 20 9
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java
  31. 48 24
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java
  32. 62 4
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java
  33. 0 1
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseServiceImpl.java
  34. 23 3
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java
  35. 39 30
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  36. 2 1
      fs-service/src/main/java/com/fs/erp/domain/ErpGoods.java
  37. 2 1
      fs-service/src/main/java/com/fs/erp/domain/ErpRefundOrder.java
  38. 1 0
      fs-service/src/main/java/com/fs/erp/dto/ErpRefundUpdateRequest.java
  39. 179 0
      fs-service/src/main/java/com/fs/erp/dto/sdk/HzOMS/api/HzOMSClient.java
  40. 91 0
      fs-service/src/main/java/com/fs/erp/dto/sdk/HzOMS/utils/HzOMSUtils.java
  41. 122 0
      fs-service/src/main/java/com/fs/erp/service/impl/HzOMSErpGoodsServiceImpl.java
  42. 383 0
      fs-service/src/main/java/com/fs/erp/service/impl/HzOMSErpOrderServiceImpl.java
  43. 7 1
      fs-service/src/main/java/com/fs/erp/service/impl/WdtErpOrderServiceImpl.java
  44. 131 4
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  45. 6 1
      fs-service/src/main/java/com/fs/his/config/FsSysConfig.java
  46. 1 0
      fs-service/src/main/java/com/fs/his/domain/FsStoreOrder.java
  47. 64 57
      fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderMapper.java
  48. 9 0
      fs-service/src/main/java/com/fs/his/mapper/FsStoreProductAttrValueMapper.java
  49. 29 17
      fs-service/src/main/java/com/fs/his/mapper/FsStoreProductMapper.java
  50. 1 1
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  51. 11 0
      fs-service/src/main/java/com/fs/his/param/FsStoreOrderSetErpPhoneParam.java
  52. 16 0
      fs-service/src/main/java/com/fs/his/param/HzOMSErpApiParam.java
  53. 37 0
      fs-service/src/main/java/com/fs/his/service/ErpApiService.java
  54. 6 0
      fs-service/src/main/java/com/fs/his/service/IFsStoreOrderService.java
  55. 6 0
      fs-service/src/main/java/com/fs/his/service/IFsStoreProductAttrValueService.java
  56. 3 0
      fs-service/src/main/java/com/fs/his/service/IFsStoreProductService.java
  57. 131 110
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java
  58. 98 41
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java
  59. 15 0
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreProductAttrValueServiceImpl.java
  60. 128 86
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreProductServiceImpl.java
  61. 238 0
      fs-service/src/main/java/com/fs/his/service/impl/HzOMSErpApiServiceImpl.java
  62. 3 0
      fs-service/src/main/java/com/fs/his/vo/FsStoreOrderListVO.java
  63. 11 0
      fs-service/src/main/java/com/fs/his/vo/HzOMSErpResponseDetailVO.java
  64. 19 0
      fs-service/src/main/java/com/fs/his/vo/HzOMSErpResponseErrorItemVO.java
  65. 20 0
      fs-service/src/main/java/com/fs/his/vo/HzOMSErpResponseVO.java
  66. 2 2
      fs-service/src/main/java/com/fs/qw/domain/QwAutoTags.java
  67. 14 0
      fs-service/src/main/java/com/fs/qw/mapper/QwAutoTagsMapper.java
  68. 4 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  69. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwWatchLogMapper.java
  70. 7 2
      fs-service/src/main/java/com/fs/qw/param/QwAutoTagsParam.java
  71. 18 0
      fs-service/src/main/java/com/fs/qw/param/QwExtContactAddTagByWatchParam.java
  72. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwExternalContactService.java
  73. 587 111
      fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java
  74. 13 0
      fs-service/src/main/java/com/fs/qw/vo/QwSopTempSetting2.java
  75. 2 1
      fs-service/src/main/java/com/fs/sop/domain/QwSopTemp.java
  76. 4 0
      fs-service/src/main/java/com/fs/sop/params/SendSopParamDetailsC.java
  77. 25 1
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  78. 28 11
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  79. 13 0
      fs-service/src/main/java/com/fs/store/config/CompanyMenuConfig.java
  80. 7 7
      fs-service/src/main/resources/application-config-druid-lmjy.yml
  81. 2 2
      fs-service/src/main/resources/application-config-druid-qdtst.yml
  82. 1 1
      fs-service/src/main/resources/application-config-druid-yzt.yml
  83. 150 0
      fs-service/src/main/resources/application-druid-jnmy-test.yml
  84. 151 0
      fs-service/src/main/resources/application-druid-jzzx-test.yml
  85. 150 0
      fs-service/src/main/resources/application-druid-lmjy-test.yml
  86. 2 2
      fs-service/src/main/resources/application-druid-lmjy.yml
  87. 103 0
      fs-service/src/main/resources/mapper/company/CompanyUserDelayTimeMapper.xml
  88. 26 2
      fs-service/src/main/resources/mapper/course/FsUserCoursePeriodDaysMapper.xml
  89. 8 7
      fs-service/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml
  90. 3 1
      fs-service/src/main/resources/mapper/course/FsUserCourseTrainingCampMapper.xml
  91. 191 1
      fs-service/src/main/resources/mapper/his/FsStoreOrderMapper.xml
  92. 24 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  93. 30 28
      fs-service/src/main/resources/mapper/sop/QwSopMapper.xml
  94. 1 1
      fs-user-app/src/main/java/com/fs/app/controller/CourseWxH5Controller.java
  95. 4 1
      fs-user-app/src/main/java/com/fs/app/controller/WxH5MpController.java
  96. 140 0
      fs-user-course/pom.xml
  97. 14 0
      fs-user-course/src/main/java/com/fs/FSServletInitializer.java
  98. 26 0
      fs-user-course/src/main/java/com/fs/FsUserCourseApplication.java
  99. 12 0
      fs-user-course/src/main/java/com/fs/course/annotation/Login.java
  100. 15 0
      fs-user-course/src/main/java/com/fs/course/annotation/LoginUser.java

+ 24 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyMenuController.java

@@ -3,16 +3,21 @@ package com.fs.company.controller;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.CompanyMenu;
 import com.fs.company.service.ICompanyMenuService;
+import com.fs.framework.web.service.TokenService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
+import com.fs.common.core.domain.entity.SysMenu;
+import com.fs.common.utils.ServletUtils;
 
 /**
  * 菜单权限Controller
@@ -27,6 +32,8 @@ public class CompanyMenuController extends BaseController
     @Autowired
     private ICompanyMenuService companyMenuService;
 
+    @Autowired
+    private TokenService tokenService;
     /**
      * 查询菜单权限列表
      */
@@ -94,4 +101,21 @@ public class CompanyMenuController extends BaseController
     {
         return toAjax(companyMenuService.deleteCompanyMenuByIds(menuIds));
     }
+
+
+    /**
+     * 获取菜单下拉树列表
+     */
+    /**
+     * 获取菜单下拉树列表
+     */
+    @GetMapping("/treeselect")
+    public AjaxResult treeselect(CompanyMenu menu)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        List<CompanyMenu> menus = companyMenuService.selectMenuList(menu, userId,"00");
+        return AjaxResult.success(companyMenuService.buildMenuTreeSelect(menus));
+    }
+
 }

+ 81 - 0
fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java

@@ -4,8 +4,10 @@ import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.utils.ParseUtils;
@@ -29,11 +31,14 @@ import com.fs.his.enums.ShipperCodeEnum;
 import com.fs.his.param.FsFollowMsgParam;
 import com.fs.his.param.FsStoreOrderParam;
 import com.fs.his.param.FsStoreOrderSalesParam;
+import com.fs.his.param.FsStoreOrderSetErpPhoneParam;
 import com.fs.his.service.IFsExportTaskService;
 import com.fs.his.service.IFsExpressService;
 import com.fs.his.service.IFsStoreService;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.*;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -84,6 +89,9 @@ public class FsStoreOrderController extends BaseController
     @Autowired
     @Qualifier("wdtErpOrderServiceImpl")
     private IErpOrderService wdtOrderService;
+
+    @Autowired
+    SysConfigMapper sysConfigMapper;
     /**
      * 查询订单列表
      */
@@ -471,6 +479,26 @@ public class FsStoreOrderController extends BaseController
         return R.ok();
     }
 
+    @Log(title = "手动推管易", businessType = BusinessType.INSERT)
+    @ApiOperation("批量创建ERP订单")
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:createErpOrder')")
+    @PostMapping(value = "/batchCreateErpOrder")
+    public R batchCreateErpOrder(@RequestBody List<Long> orderIds)
+    {
+        if (orderIds != null && !orderIds.isEmpty()) {
+            orderIds.forEach(orderId->{
+                try {
+                    fsStoreOrderService.createOmsOrder(orderId);
+                } catch (ParseException e) {
+                    throw new RuntimeException(e);
+                }
+
+            });
+        }
+
+        return R.ok();
+    }
+
     @Log(title = "冻结/解冻", businessType = BusinessType.UPDATE)
     @PreAuthorize("@ss.hasPermi('his:storeOrder:editTuiMoney')")
     @GetMapping(value = "/editTuiMoney/{id}")
@@ -576,4 +604,57 @@ public class FsStoreOrderController extends BaseController
     }
 
 
+    /**
+     * 查询erp默认手机号
+     * @return
+     */
+    @GetMapping(value = "/queryErpPhone")
+    public AjaxResult queryErpPhone()
+    {
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("erp.phone");
+        List<String> list = new ArrayList<>();
+        if(sysConfig!=null){
+            String configValue = sysConfig.getConfigValue();
+            if(StringUtils.isNotEmpty(configValue)){
+                list = JSON.parseArray(configValue, String.class);
+            }
+        }
+        return AjaxResult.success(list);
+    }
+
+
+    /**
+     * 设置erp默认手机号
+     * @param phoneList
+     * @return
+     */
+    @PostMapping(value = "/saveErpPhone")
+    public AjaxResult saveErpPhone(@RequestBody List<String> phoneList)
+    {
+        //去重
+        phoneList = phoneList.stream().distinct().collect(Collectors.toList());
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("erp.phone");
+        sysConfig.setConfigValue(JSON.toJSONString(phoneList));
+        return AjaxResult.success(sysConfigMapper.updateConfig(sysConfig));
+    }
+
+    /**
+     * 批量设置erp手机号
+     */
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:createErpOrder')")
+    @Log(title = "订单", businessType = BusinessType.UPDATE)
+    @PostMapping("/editErpPhone")
+    public AjaxResult editErpPhone(@RequestBody FsStoreOrderSetErpPhoneParam param)
+    {
+        String erpPhone = param.getErpPhone();
+        if (StringUtils.isBlank(erpPhone)) {
+            return AjaxResult.error("请选择手机号");
+        }
+        List<Long> orderIds = param.getOrderIds();
+        if (orderIds  == null || orderIds.isEmpty()){
+            return AjaxResult.success();
+        }
+        return toAjax(fsStoreOrderService.batchUpdateErpByOrderIds(param));
+    }
+
 }

+ 120 - 0
fs-admin/src/main/java/com/fs/his/controller/HzOMSErpApiController.java

@@ -0,0 +1,120 @@
+package com.fs.his.controller;
+
+
+import com.fs.erp.domain.ErpOrder;
+import com.fs.erp.dto.ErpOrderResponse;
+import com.fs.erp.dto.sdk.HzOMS.utils.HzOMSUtils;
+import com.fs.erp.service.IErpOrderService;
+import com.fs.his.domain.FsVessel;
+import com.fs.his.param.HzOMSErpApiParam;
+import com.fs.his.service.ErpApiService;
+import com.fs.his.vo.HzOMSErpResponseVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 瀚智OMS Erp系统推送平数据接口
+ */
+
+@RestController
+@RequestMapping("/erp/call")
+public class HzOMSErpApiController {
+
+    @Autowired
+    @Qualifier("hzOMSErpApiServiceImpl")
+    ErpApiService hzOMSErpApiService;
+
+    @Autowired
+    @Qualifier("hzOMSErpOrderServiceImpl")
+    IErpOrderService HzOMSErpOrderService;
+
+    /**
+     * 用于将瀚智商品库存同步到第三方
+     *
+     * @param appkey
+     * @param timestamp
+     * @param datapackage
+     * @param sign
+     * @return
+     */
+    @PostMapping("/ptgoods/stockSync")
+    public HzOMSErpResponseVO stockSync(String appkey, String timestamp, String datapackage, String sign) {
+        HzOMSErpApiParam param = new HzOMSErpApiParam();
+        param.setAppkey(appkey);
+        param.setSign(sign);
+        param.setTimestamp(timestamp);
+        param.setDatapakege(datapackage);
+        HzOMSErpResponseVO hzOMSErpResponseVO = new HzOMSErpResponseVO();
+        try {
+            hzOMSErpResponseVO = hzOMSErpApiService.stockSync(param);
+        } catch (Exception e) {
+            e.printStackTrace();
+            hzOMSErpResponseVO.setSuccess(Boolean.FALSE);
+            hzOMSErpResponseVO.setMessage("同步商品库存同步失败");
+        }
+        return hzOMSErpResponseVO;
+    }
+
+    /**
+     * 用于将瀚智商品库存同步到第三方
+     *
+     * @param appkey
+     * @param timestamp
+     * @param datapackage
+     * @param sign
+     * @return
+     */
+    @PostMapping("/ptgoods/skuStockSync")
+    public HzOMSErpResponseVO skuStockSync(String appkey, String timestamp, String datapackage, String sign) {
+        HzOMSErpApiParam param = new HzOMSErpApiParam();
+        param.setAppkey(appkey);
+        param.setSign(sign);
+        param.setTimestamp(timestamp);
+        param.setDatapakege(datapackage);
+        HzOMSErpResponseVO hzOMSErpResponseVO = new HzOMSErpResponseVO();
+        try {
+            hzOMSErpResponseVO = hzOMSErpApiService.skuStockSync(param);
+        } catch (Exception e) {
+            e.printStackTrace();
+            hzOMSErpResponseVO.setSuccess(Boolean.FALSE);
+            hzOMSErpResponseVO.setMessage("同步商品规格库存失败");
+        }
+        return hzOMSErpResponseVO;
+    }
+
+    /**
+     * 平台订单物流信息回传
+     *
+     * @param appkey
+     * @param timestamp
+     * @param datapackage
+     * @param sign
+     * @return
+     */
+    @PostMapping("/ptorder/logisticsAdd")
+    public HzOMSErpResponseVO logisticsAdd(String appkey, String timestamp, String datapackage, String sign) {
+        HzOMSErpApiParam param = new HzOMSErpApiParam();
+        param.setAppkey(appkey);
+        param.setSign(sign);
+        param.setTimestamp(timestamp);
+        param.setDatapakege(datapackage);
+        return  hzOMSErpApiService.logisticsAdd(param);
+    }
+
+//    @PostMapping("/test/createOrder")
+//    public HzOMSErpResponseVO createOrder(String appkey, String timestamp, String datapackage, String sign) {
+//
+//        ErpOrder order = new ErpOrder();
+//        order.setPlatform_code(datapackage);
+//
+//        ErpOrderResponse erpOrderResponse = HzOMSErpOrderService.addOrder(order);
+//        HzOMSErpResponseVO hzOMSErpResponseVO = new HzOMSErpResponseVO();
+//        hzOMSErpResponseVO.setSuccess(true);
+//        return hzOMSErpResponseVO;
+//    }
+}

+ 110 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyIntegralController.java

@@ -0,0 +1,110 @@
+package com.fs.company.controller.company;
+
+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.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyDept;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import com.fs.his.param.FsUserIntegralLogsParam;
+import com.fs.his.service.IFsUserIntegralLogsService;
+import com.fs.his.vo.FsUserIntegralLogsListVO;
+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 java.util.List;
+
+/**
+ * 积分信息
+ *
+
+ */
+@RestController
+@RequestMapping("/company/companyIntegral")
+public class CompanyIntegralController extends BaseController
+{
+    @Autowired
+    private IFsUserIntegralLogsService fsUserIntegralLogsService;
+    @Autowired
+    private TokenService tokenService;
+    /**
+     * 获取积分信息
+     */
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserIntegralLogsParam userIntegralLogs)
+    {
+        startPage();
+        List<FsUserIntegralLogsListVO> list = fsUserIntegralLogsService.selectFsUserIntegralLogsListVO(userIntegralLogs);
+        for (FsUserIntegralLogsListVO vo : list) {
+            if (vo.getPhone()!=null&&vo.getPhone()!=""){
+                vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+            }
+
+        }
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出积分记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:integral:export')")
+    @Log(title = "积分记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserIntegralLogsParam fsUserIntegralLogs)
+    {
+        List<FsUserIntegralLogsListVO> list = fsUserIntegralLogsService.selectFsUserIntegralLogsListVO(fsUserIntegralLogs);
+        for (FsUserIntegralLogsListVO vo : list) {
+            if (vo.getPhone()!=null&&vo.getPhone()!=""){
+                vo.setPhone(vo.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+            }
+
+        }
+        ExcelUtil<FsUserIntegralLogsListVO> util = new ExcelUtil<FsUserIntegralLogsListVO>(FsUserIntegralLogsListVO.class);
+        return util.exportExcel(list, "积分记录数据");
+    }
+
+    /**
+     * 根id获取详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:integral:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable Long id)
+    {
+        return AjaxResult.success();
+    }
+
+    /**
+     * 修改积分
+     */
+    @PreAuthorize("@ss.hasPermi('company:integral:edit')")
+    @Log(title = "积分记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody CompanyDept dept)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        dept.setCompanyId(loginUser.getCompany().getCompanyId());
+        return toAjax(0);
+    }
+
+    /**
+     * 删除积分
+     */
+    @PreAuthorize("@ss.hasPermi('company:integral:remove')")
+    @Log(title = "积分记录", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{id}")
+    public AjaxResult remove(@PathVariable Long id)
+    {
+        return toAjax(0);
+    }
+
+
+
+
+
+}

+ 28 - 9
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java

@@ -1,5 +1,7 @@
 package com.fs.company.controller.company;
 
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
 import com.fs.common.annotation.Log;
 import com.fs.common.constant.UserConstants;
 import com.fs.common.core.controller.BaseController;
@@ -11,23 +13,20 @@ import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
-import com.fs.company.domain.Company;
-import com.fs.company.domain.CompanyPost;
-import com.fs.company.domain.CompanyRole;
-import com.fs.company.domain.CompanyUser;
+import com.fs.company.domain.*;
+import com.fs.company.mapper.CompanyUserDelayTimeMapper;
 import com.fs.company.param.CompanyUserAreaParam;
 import com.fs.company.param.CompanyUserQwParam;
-import com.fs.company.service.ICompanyPostService;
-import com.fs.company.service.ICompanyRoleService;
-import com.fs.company.service.ICompanyService;
-import com.fs.company.service.ICompanyUserService;
+import com.fs.company.service.*;
 import com.fs.company.vo.CompanyUserQwListVO;
 import com.fs.company.vo.CompanyUserVO;
+import com.fs.course.config.CourseConfig;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.framework.service.TokenService;
 import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwUserVO;
+import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -63,7 +62,10 @@ public class CompanyUserController extends BaseController
     private ICompanyUserService companyUserService;
     @Autowired
     private ICompanyService companyService;
-
+    @Autowired
+    private ICompanyUserDelayTimeService companyUserDelayTimeService;
+    @Autowired
+    private ISysConfigService configService;
     /**
      * 获取用户列表
      */
@@ -101,6 +103,16 @@ public class CompanyUserController extends BaseController
         user.setCompanyId(loginUser.getCompany().getCompanyId());
         startPage();
         List<CompanyUserQwListVO> list = companyUserService.selectCompanyUserQwListVO(user);
+        for (CompanyUserQwListVO companyUserQwListVO : list) {
+             CompanyUserDelayTime companyUserDelayTime = companyUserDelayTimeService.selectCompanyUserDelayTimeByCompanyUser(companyUserQwListVO.getCompanyId(),companyUserQwListVO.getUserId());
+            if (ObjectUtil.isEmpty(companyUserDelayTime)){
+                String json = configService.selectConfigByKey("course.config");
+                CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+                companyUserQwListVO.setSendDelayTime(config.getSendDelayTime());
+            }else {
+                companyUserQwListVO.setSendDelayTime(companyUserDelayTime.getSendDelayTime());
+            }
+        }
         return getDataTable(list);
     }
     @Log(title = "用户管理", businessType = BusinessType.EXPORT)
@@ -222,6 +234,13 @@ public class CompanyUserController extends BaseController
     @DeleteMapping("/{userIds}")
     public AjaxResult remove(@PathVariable Long[] userIds)
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        for (Long userId : userIds) {
+            CompanyUserDelayTime companyUserDelayTime = companyUserDelayTimeService.selectCompanyUserDelayTimeByCompanyUser(loginUser.getUser().getCompanyId(),userId);
+            if (ObjectUtil.isNotEmpty(companyUserDelayTime)){
+                companyUserDelayTimeService.deleteCompanyUserDelayTimeById(companyUserDelayTime.getId());
+            }
+        }
         return toAjax(companyUserService.deleteCompanyUserByIds(userIds));
     }
 

+ 211 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserDelayTimeController.java

@@ -0,0 +1,211 @@
+package com.fs.company.controller.company;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.param.CompanyUserDelayTimeParam;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import com.fs.voice.utils.StringUtil;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.company.domain.CompanyUserDelayTime;
+import com.fs.company.service.ICompanyUserDelayTimeService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 员工发送延时时间关联Controller
+ *
+ * @author fs
+ * @date 2025-06-18
+ */
+@RestController
+@RequestMapping("/company/sendDelayTime")
+public class CompanyUserDelayTimeController extends BaseController
+{
+    @Autowired
+    private ICompanyUserDelayTimeService companyUserDelayTimeService;
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private ICompanyUserService companyUserService;
+
+    /**
+     * 查询员工发送延时时间关联列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:sendDelayTime:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyUserDelayTime companyUserDelayTime)
+    {
+        startPage();
+        List<CompanyUserDelayTime> list = companyUserDelayTimeService.selectCompanyUserDelayTimeList(companyUserDelayTime);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出员工发送延时时间关联列表
+     */
+    @PreAuthorize("@ss.hasPermi('company:sendDelayTime:export')")
+    @Log(title = "员工发送延时时间关联", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyUserDelayTime companyUserDelayTime)
+    {
+        List<CompanyUserDelayTime> list = companyUserDelayTimeService.selectCompanyUserDelayTimeList(companyUserDelayTime);
+        ExcelUtil<CompanyUserDelayTime> util = new ExcelUtil<CompanyUserDelayTime>(CompanyUserDelayTime.class);
+        return util.exportExcel(list, "员工发送延时时间关联数据");
+    }
+
+    /**
+     * 获取员工发送延时时间关联详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('company:sendDelayTime:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(companyUserDelayTimeService.selectCompanyUserDelayTimeById(id));
+    }
+
+    /**
+     * 批量绑定发送延时时间关联
+     */
+    @PreAuthorize("@ss.hasPermi('company:sendDelayTime:batchadd')")
+    @Log(title = "员工发送延时时间关联", businessType = BusinessType.INSERT)
+    @PostMapping("/batchAdd")
+    public R batchAdd(@RequestBody CompanyUserDelayTimeParam companyUserDelayTimeParam) {
+        // 参数校验
+        if (companyUserDelayTimeParam == null || StringUtils.isEmpty(companyUserDelayTimeParam.getId())) {
+            return R.error("参数不能为空");
+        }
+
+        // 获取当前登录用户信息
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        if (loginUser == null || loginUser.getCompany() == null) {
+            return R.error("用户未登录或公司信息不存在");
+        }
+
+        // 处理用户ID集合
+        Set<String> companyUserIds = Arrays.stream(companyUserDelayTimeParam.getId().split(","))
+                .filter(StringUtils::isNotBlank)
+                .collect(Collectors.toSet());
+
+        if (companyUserIds.isEmpty()) {
+            return R.error("用户ID不能为空");
+        }
+
+        // 批量处理延迟时间设置
+        try {
+            companyUserIds.forEach(userId -> {
+                CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.valueOf(userId));
+                if (companyUser == null) {
+                    throw new RuntimeException("用户ID不存在: " + userId);
+                }
+
+                CompanyUserDelayTime delayTime = new CompanyUserDelayTime();
+                delayTime.setCompanyId(loginUser.getCompany().getCompanyId());
+                delayTime.setCompanyUserId(Long.valueOf(userId));
+                delayTime.setNickName(companyUser.getNickName());
+                delayTime.setRemark(companyUserDelayTimeParam.getRemark());
+                delayTime.setCreateBy(loginUser.getUser().getNickName());
+                delayTime.setSendDelayTime(Long.valueOf(companyUserDelayTimeParam.getSendDelayTime()));
+
+                CompanyUserDelayTime companyUserDelayTime = companyUserDelayTimeService.selectCompanyUserDelayTimeByCompanyUser(loginUser.getCompany().getCompanyId(),Long.valueOf(userId));
+                if (ObjectUtil.isEmpty(companyUserDelayTime)){
+                    companyUserDelayTimeService.insertCompanyUserDelayTime(delayTime);
+                }else {
+                    delayTime.setId(companyUserDelayTime.getId());
+                    companyUserDelayTimeService.updateCompanyUserDelayTime(delayTime);
+                }
+            });
+            return R.ok();
+        } catch (RuntimeException e) {
+            e.fillInStackTrace();
+            return R.error(e.getMessage());
+        } catch (Exception e) {
+            e.fillInStackTrace();
+            return R.error("批量设置延迟时间失败");
+        }
+    }
+
+
+    /**
+     * 新增员工发送延时时间关联
+     */
+    @PreAuthorize("@ss.hasPermi('company:sendDelayTime:add')")
+    @Log(title = "员工发送延时时间关联", businessType = BusinessType.INSERT)
+    @PostMapping()
+    public AjaxResult add(@RequestBody CompanyUserDelayTime companyUserDelayTime) {
+        // 获取当前登录用户信息
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+
+        // 设置基本信息
+        companyUserDelayTime.setCompanyId(companyId);
+        companyUserDelayTime.setCreateBy(loginUser.getUser().getNickName());
+
+        // 验证并设置用户信息
+        Long companyUserId = companyUserDelayTime.getCompanyUserId();
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(companyUserId);
+        if (companyUser == null) {
+            throw new ServiceException("员工信息不存在");
+        }
+        companyUserDelayTime.setNickName(companyUser.getNickName());
+
+        // 检查是否已存在延时时间记录
+        CompanyUserDelayTime existingDelayTime = companyUserDelayTimeService
+                .selectCompanyUserDelayTimeByCompanyUser(companyId, companyUser.getUserId());
+        if (existingDelayTime != null) {
+            throw new ServiceException("该员工已经绑定过延时时间信息");
+        }
+
+        // 插入新记录
+        int result = companyUserDelayTimeService.insertCompanyUserDelayTime(companyUserDelayTime);
+        return toAjax(result);
+    }
+
+    /**
+     * 修改员工发送延时时间关联
+     */
+    @PreAuthorize("@ss.hasPermi('company:sendDelayTime:edit')")
+    @Log(title = "员工发送延时时间关联", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody CompanyUserDelayTime companyUserDelayTime)
+    {
+        CompanyUser companyUser = companyUserService.selectCompanyUserById(companyUserDelayTime.getCompanyUserId());
+        companyUserDelayTime.setNickName(companyUser.getNickName());
+        return toAjax(companyUserDelayTimeService.updateCompanyUserDelayTime(companyUserDelayTime));
+    }
+
+    /**
+     * 删除员工发送延时时间关联
+     */
+    @PreAuthorize("@ss.hasPermi('company:sendDelayTime:remove')")
+    @Log(title = "员工发送延时时间关联", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(companyUserDelayTimeService.deleteCompanyUserDelayTimeByIds(ids));
+    }
+}

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

@@ -1,6 +1,7 @@
 package com.fs.company.controller.course;
 
 import com.fs.common.annotation.Log;
+import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
@@ -56,6 +57,8 @@ public class FsCourseRedPacketLogController extends BaseController
         if (fsCourseRedPacketLog.getPhoneMk() != null && fsCourseRedPacketLog.getPhoneMk() != "") {
             fsCourseRedPacketLog.setPhone(encryptPhone(fsCourseRedPacketLog.getPhoneMk()));
         }
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fsCourseRedPacketLog.setCompanyId( loginUser.getCompany().getCompanyId());
 
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {
@@ -75,7 +78,7 @@ public class FsCourseRedPacketLogController extends BaseController
         startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         fsCourseRedPacketLog.setCompanyId( loginUser.getCompany().getCompanyId());
-
+        fsCourseRedPacketLog.setCompanyUserId(loginUser.getUser().getUserId());
         if (fsCourseRedPacketLog.getPhoneMk() != null && fsCourseRedPacketLog.getPhoneMk() != "") {
             fsCourseRedPacketLog.setPhone(encryptPhone(fsCourseRedPacketLog.getPhoneMk()));
         }
@@ -201,9 +204,10 @@ public class FsCourseRedPacketLogController extends BaseController
 
     @Log(title = "短链课程看课记录", businessType = BusinessType.DELETE)
     @PutMapping("/retryCourseRedPacketLog/{logIds}")
-    public AjaxResult retryCourseRedPacketLog(@PathVariable Long[] logIds)
+    @RepeatSubmit
+    public R retryCourseRedPacketLog(@PathVariable Long[] logIds)
     {
-        return toAjax(fsCourseRedPacketLogService.retryCourseRedPacketLog(logIds));
+        return fsCourseRedPacketLogService.retryCourseRedPacketLog(logIds);
     }
 
     @GetMapping("/courseList")

+ 27 - 2
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java

@@ -26,6 +26,7 @@ import com.fs.qw.param.*;
 import com.fs.qw.service.IQwExternalContactInfoService;
 import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qw.service.IQwTagService;
+import com.fs.qw.service.IQwWatchLogService;
 import com.fs.qw.vo.QwExternalContactVO;
 import com.fs.qw.vo.QwFsUserVO;
 import com.github.pagehelper.PageHelper;
@@ -269,14 +270,24 @@ public class QwExternalContactController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('qw:externalContact:addTag')")
-    @Log(title = "添加标签", businessType = BusinessType.INSERT)
+    @Log(title = "添加标签", businessType = BusinessType.UPDATE)
     @PostMapping("/addTag")
     public R addTag(@RequestBody QwExternalContactAddTagParam Param) throws JSONException {
 
         return qwExternalContactService.addUserTag(Param);
     }
+
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:addTag')")
+    @Log(title = "添加标签", businessType = BusinessType.UPDATE)
+    @PostMapping("/addTagByWatch")
+    public R addTagByWatch(@RequestBody QwExtContactAddTagByWatchParam Param) throws JSONException {
+
+        return qwExternalContactService.addTagByWatch(Param);
+
+    }
+
     @PreAuthorize("@ss.hasPermi('qw:externalContact:delTag')")
-    @Log(title = "移除标签", businessType = BusinessType.INSERT)
+    @Log(title = "移除标签", businessType = BusinessType.UPDATE)
     @PostMapping("/delTag")
     public R delTag(@RequestBody QwExternalContactAddTagParam Param)
     {
@@ -284,6 +295,20 @@ public class QwExternalContactController extends BaseController
         return qwExternalContactService.delUserTag(Param);
     }
 
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:delTag')")
+    @Log(title = "在看课记录这移除标签", businessType = BusinessType.UPDATE)
+    @PostMapping("/delTagByWatch")
+    public R delTagByWatch(@RequestBody QwExtContactAddTagByWatchParam Param)
+    {
+
+        return qwExternalContactService.delTagByWatch(Param);
+    }
+
+
+
+
+
+
     @PreAuthorize("@ss.hasPermi('qw:externalContact:transfer')")
     @Log(title = "企业微信客户", businessType = BusinessType.UPDATE)
     @PutMapping("/resignedTransfer")

+ 8 - 0
fs-doctor-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -139,6 +139,14 @@ public class CommonController {
 		return R.ok().put("data",version);
 	}
 
+	@ApiOperation("获取最新版本")
+	@GetMapping("/testGit")
+	public R testGit(@RequestParam("appType")Integer appType)
+	{
+		FsAppVersion version=appVersionService.getNewAppVersion(appType);
+		return R.ok().put("data",version);
+	}
+
 	@ApiOperation("获取数据字典")
 	@GetMapping("/getDictByKey")
 

+ 1 - 0
fs-framework/src/main/java/com/fs/framework/config/SecurityConfig.java

@@ -143,6 +143,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .antMatchers("/*/api-docs").anonymous()
                 .antMatchers("/druid/**").anonymous()
                 .antMatchers("/course/userVideo/videoTranscode").anonymous()
+                .antMatchers("/erp/call/**").anonymous()
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()
                 .and()

+ 40 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyUserDelayTime.java

@@ -0,0 +1,40 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 员工发送延时时间关联对象 company_user_delay_time
+ *
+ * @author fs
+ * @date 2025-06-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyUserDelayTime extends BaseEntity{
+
+    /** id主键 */
+    private Long id;
+
+    /** 公司id */
+    private Long companyId;
+
+    /** 员工id */
+    private Long companyUserId;
+
+    /** 发送延时时间(单位:秒) */
+    @Excel(name = "发送延时时间(单位:秒)")
+    private Long sendDelayTime;
+
+    /** 员工用户昵称 */
+    @Excel(name = "员工用户昵称")
+    private String nickName;
+
+    /** 删除标志(0代表存在 1代表删除) */
+    private String delFlag;
+
+
+}

+ 72 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyUserDelayTimeMapper.java

@@ -0,0 +1,72 @@
+package com.fs.company.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyUserDelayTime;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 员工发送延时时间关联Mapper接口
+ *
+ * @author fs
+ * @date 2025-06-18
+ */
+public interface CompanyUserDelayTimeMapper extends BaseMapper<CompanyUserDelayTime>{
+    /**
+     * 查询员工发送延时时间关联
+     *
+     * @param id 员工发送延时时间关联主键
+     * @return 员工发送延时时间关联
+     */
+    CompanyUserDelayTime selectCompanyUserDelayTimeById(Long id);
+
+    /**
+     * 查询员工发送延时时间关联列表
+     *
+     * @param companyUserDelayTime 员工发送延时时间关联
+     * @return 员工发送延时时间关联集合
+     */
+    List<CompanyUserDelayTime> selectCompanyUserDelayTimeList(CompanyUserDelayTime companyUserDelayTime);
+
+    /**
+     * 新增员工发送延时时间关联
+     *
+     * @param companyUserDelayTime 员工发送延时时间关联
+     * @return 结果
+     */
+    int insertCompanyUserDelayTime(CompanyUserDelayTime companyUserDelayTime);
+
+    /**
+     * 修改员工发送延时时间关联
+     *
+     * @param companyUserDelayTime 员工发送延时时间关联
+     * @return 结果
+     */
+    int updateCompanyUserDelayTime(CompanyUserDelayTime companyUserDelayTime);
+
+    /**
+     * 删除员工发送延时时间关联
+     *
+     * @param id 员工发送延时时间关联主键
+     * @return 结果
+     */
+    int deleteCompanyUserDelayTimeById(Long id);
+
+    /**
+     * 批量删除员工发送延时时间关联
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteCompanyUserDelayTimeByIds(Long[] ids);
+
+
+    /**
+     * 通过销售用户id和企业id进行查询数据
+     *
+     * @param companyUserId
+     * @param companyId
+     * @return
+     */
+    CompanyUserDelayTime selectUserDelayTimeByCompanyUserId(@Param("companyUserId") Long companyUserId, @Param("companyId") Long companyId);
+}

+ 10 - 0
fs-service/src/main/java/com/fs/company/param/CompanyUserDelayTimeParam.java

@@ -0,0 +1,10 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+@Data
+public class CompanyUserDelayTimeParam {
+    public String sendDelayTime;
+    public String id;
+    public String remark;
+}

+ 63 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyUserDelayTimeService.java

@@ -0,0 +1,63 @@
+package com.fs.company.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.company.domain.CompanyUserDelayTime;
+
+/**
+ * 员工发送延时时间关联Service接口
+ *
+ * @author fs
+ * @date 2025-06-18
+ */
+public interface ICompanyUserDelayTimeService extends IService<CompanyUserDelayTime>{
+    /**
+     * 查询员工发送延时时间关联
+     *
+     * @param id 员工发送延时时间关联主键
+     * @return 员工发送延时时间关联
+     */
+    CompanyUserDelayTime selectCompanyUserDelayTimeById(Long id);
+
+    /**
+     * 查询员工发送延时时间关联列表
+     *
+     * @param companyUserDelayTime 员工发送延时时间关联
+     * @return 员工发送延时时间关联集合
+     */
+    List<CompanyUserDelayTime> selectCompanyUserDelayTimeList(CompanyUserDelayTime companyUserDelayTime);
+
+    /**
+     * 新增员工发送延时时间关联
+     *
+     * @param companyUserDelayTime 员工发送延时时间关联
+     * @return 结果
+     */
+    int insertCompanyUserDelayTime(CompanyUserDelayTime companyUserDelayTime);
+
+    /**
+     * 修改员工发送延时时间关联
+     *
+     * @param companyUserDelayTime 员工发送延时时间关联
+     * @return 结果
+     */
+    int updateCompanyUserDelayTime(CompanyUserDelayTime companyUserDelayTime);
+
+    /**
+     * 批量删除员工发送延时时间关联
+     *
+     * @param ids 需要删除的员工发送延时时间关联主键集合
+     * @return 结果
+     */
+    int deleteCompanyUserDelayTimeByIds(Long[] ids);
+
+    /**
+     * 删除员工发送延时时间关联信息
+     *
+     * @param id 员工发送延时时间关联主键
+     * @return 结果
+     */
+    int deleteCompanyUserDelayTimeById(Long id);
+
+    CompanyUserDelayTime selectCompanyUserDelayTimeByCompanyUser(Long companyId, Long userId);
+}

+ 29 - 9
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -13,6 +13,7 @@ import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
 import com.fs.company.param.CompanyParam;
 import com.fs.company.service.ICompanyProfitService;
+import com.fs.company.service.ICompanyRoleService;
 import com.fs.company.vo.CompanyCrmVO;
 import com.fs.company.vo.CompanyNameVO;
 import com.fs.company.vo.CompanyVO;
@@ -24,6 +25,7 @@ import com.fs.his.domain.FsStorePayment;
 import com.fs.his.dto.InquiryConfigDTO;
 import com.fs.his.mapper.FsStoreOrderMapper;
 import com.fs.his.vo.OptionsVO;
+import com.fs.store.config.CompanyMenuConfig;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.service.ISysConfigService;
@@ -52,7 +54,8 @@ public class CompanyServiceImpl implements ICompanyService
     private CompanyMapper companyMapper;
     @Autowired
     private CompanyDeptMapper deptMapper;
-
+    @Autowired
+    private ICompanyRoleService roleService;
     @Autowired
     private CompanyRoleMapper roleMapper;
     @Autowired
@@ -144,13 +147,30 @@ public class CompanyServiceImpl implements ICompanyService
             post.setStatus("0");
             postMapper.insertCompanyPost(post);
             //创建角色
-            CompanyRole role=new CompanyRole();
-            role.setCompanyId(company.getCompanyId());
-            role.setRoleName("管理员");
-            role.setRoleKey("admin");
-            role.setRoleSort(0);
-            role.setStatus("0");
-            roleMapper.insertCompanyRole(role);
+            // 创建管理员角色(拥有全部权限)
+            CompanyRole adminRole = new CompanyRole();
+            adminRole.setCompanyId(company.getCompanyId());
+            adminRole.setRoleName("管理员");
+            adminRole.setRoleKey("admin");
+            adminRole.setRoleSort(0);
+            adminRole.setStatus("0");
+            roleMapper.insertCompanyRole(adminRole);
+
+            // 创建销售角色(新增部分,只有"我的"相关权限)
+            CompanyRole salesRole = new CompanyRole();
+            salesRole.setCompanyId(company.getCompanyId());
+            salesRole.setRoleName(company.getCompanyName() + "_销售");
+            salesRole.setRoleKey(company.getCompanyId()+"_sales");
+            salesRole.setRoleSort(1);
+            salesRole.setDataScope("5");
+            salesRole.setStatus("0");
+
+            //增加销售角色菜单权限
+            String json = configService.selectConfigByKey("companymenu.config");
+            CompanyMenuConfig config = JSONUtil.toBean(json, CompanyMenuConfig.class);
+            // 注意:此处需要额外处理销售角色的权限,设置为仅"我的"相关权限
+            salesRole.setMenuIds(config.getMenuIds());
+            roleService.insertRole(salesRole);
             //添加用户
             CompanyUser user=new CompanyUser();
             user.setCompanyId(company.getCompanyId());
@@ -167,7 +187,7 @@ public class CompanyServiceImpl implements ICompanyService
             userMapper.insertCompanyUser(user);
             //添加用户角色表
             CompanyUserRole userRole=new CompanyUserRole();
-            userRole.setRoleId(role.getRoleId());
+            userRole.setRoleId(adminRole.getRoleId());
             userRole.setUserId(user.getUserId());
             userRoleMapper.insertCompanyUserRole(userRole);
             //添加用户岗位表

+ 106 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserDelayTimeServiceImpl.java

@@ -0,0 +1,106 @@
+package com.fs.company.service.impl;
+
+import java.util.List;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.company.mapper.CompanyUserDelayTimeMapper;
+import com.fs.company.domain.CompanyUserDelayTime;
+import com.fs.company.service.ICompanyUserDelayTimeService;
+
+/**
+ * 员工发送延时时间关联Service业务层处理
+ *
+ * @author fs
+ * @date 2025-06-18
+ */
+@Service
+public class CompanyUserDelayTimeServiceImpl extends ServiceImpl<CompanyUserDelayTimeMapper, CompanyUserDelayTime> implements ICompanyUserDelayTimeService {
+
+    /**
+     * 查询员工发送延时时间关联
+     *
+     * @param id 员工发送延时时间关联主键
+     * @return 员工发送延时时间关联
+     */
+    @Override
+    public CompanyUserDelayTime selectCompanyUserDelayTimeById(Long id)
+    {
+        return baseMapper.selectCompanyUserDelayTimeById(id);
+    }
+
+    /**
+     * 查询员工发送延时时间关联列表
+     *
+     * @param companyUserDelayTime 员工发送延时时间关联
+     * @return 员工发送延时时间关联
+     */
+    @Override
+    public List<CompanyUserDelayTime> selectCompanyUserDelayTimeList(CompanyUserDelayTime companyUserDelayTime)
+    {
+        return baseMapper.selectCompanyUserDelayTimeList(companyUserDelayTime);
+    }
+
+    /**
+     * 新增员工发送延时时间关联
+     *
+     * @param companyUserDelayTime 员工发送延时时间关联
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyUserDelayTime(CompanyUserDelayTime companyUserDelayTime)
+    {
+        companyUserDelayTime.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertCompanyUserDelayTime(companyUserDelayTime);
+    }
+
+    /**
+     * 修改员工发送延时时间关联
+     *
+     * @param companyUserDelayTime 员工发送延时时间关联
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyUserDelayTime(CompanyUserDelayTime companyUserDelayTime)
+    {
+        companyUserDelayTime.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateCompanyUserDelayTime(companyUserDelayTime);
+    }
+
+    /**
+     * 批量删除员工发送延时时间关联
+     *
+     * @param ids 需要删除的员工发送延时时间关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyUserDelayTimeByIds(Long[] ids)
+    {
+        return baseMapper.deleteCompanyUserDelayTimeByIds(ids);
+    }
+
+    /**
+     * 删除员工发送延时时间关联信息
+     *
+     * @param id 员工发送延时时间关联主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyUserDelayTimeById(Long id)
+    {
+        return baseMapper.deleteCompanyUserDelayTimeById(id);
+    }
+
+    /**
+     * 通过员工id和企业id查询数据信息
+     *
+     * @param companyId
+     * @param userId
+     * @return
+     */
+    @Override
+    public CompanyUserDelayTime selectCompanyUserDelayTimeByCompanyUser(Long companyId, Long userId) {
+        return baseMapper.selectUserDelayTimeByCompanyUserId(userId,companyId);
+    }
+}

+ 5 - 0
fs-service/src/main/java/com/fs/company/vo/CompanyUserQwListVO.java

@@ -116,4 +116,9 @@ public class CompanyUserQwListVO extends BaseEntity {
 
     private Integer qwStatus;
     private String addressId;
+
+    /**
+     * 发送延时时间
+     */
+    private Long sendDelayTime;
 }

+ 1 - 0
fs-service/src/main/java/com/fs/course/config/CourseConfig.java

@@ -32,6 +32,7 @@ public class CourseConfig implements Serializable {
     private Integer minutesNum; //多少分钟算完课
     private Integer openCommentStatus; //开启评论/弹幕
     private Integer viewCommentNum; // 查看历史评论数量
+    private Long sendDelayTime; // 查看发送延时时间
 
     private String companyUserQRCode;// 默认客服二维码图片
     private String courseLogo;//课程Logo

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

@@ -104,4 +104,9 @@ public class FsUserCoursePeriod
     @Excel(name = "开启评论或者弹幕,1-开启评论;2-开启弹幕;3-都关闭")
     private String courseLogo;
 
+    /**
+     * 删除状态0、正常,1、已删除
+     */
+    private Integer delFlag;
+
 }

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

@@ -36,4 +36,9 @@ public class FsUserCourseTrainingCamp
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @Excel(name = "创建时间")
     private LocalDateTime createTime;
+
+    /**
+     * 删除状态0、正常,1、已删除
+     */
+    private Integer delFlag;
 }

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

@@ -114,6 +114,7 @@ public interface FsCourseRedPacketLogMapper
             "<if test = ' maps.userId !=null '> and l.user_id = #{maps.userId} </if>" +
             "<if test = ' maps.watchLogId !=null '> and l.watch_log_id = #{maps.watchLogId} </if>" +
             "<if test = ' maps.companyId !=null '> and l.company_id = #{maps.companyId} </if>" +
+            "<if test = ' maps.companyUserId !=null '> and l.company_user_id = #{maps.companyUserId} </if>" +
             "<if test = ' maps.companyUserName !=null '> and cu.nick_name  like concat('%', #{maps.companyUserName}, '%') </if>" +
             "<if test = ' maps.nickName !=null '> and u.nick_name  like concat('%', #{maps.nickName}, '%') </if>" +
             "<if test = ' maps.courseId !=null '> and l.course_id = #{maps.courseId} </if>" +
@@ -156,4 +157,7 @@ public interface FsCourseRedPacketLogMapper
     @Select("SELECT company_id, SUM(amount) as money FROM fs_course_red_packet_log    WHERE status = 0 and create_time >= DATE_SUB(CURDATE(), INTERVAL 2 DAY)  AND create_time < DATE_SUB(CURDATE(), INTERVAL 1 DAY) GROUP BY company_id  ")
     List<RedPacketMoneyVO> selectFsCourseAddRedPacketLogByCompany();
 
+    @Select("select * from fs_course_red_packet_log where video_id = #{videoId} and user_id = #{userId} and period_id = #{periodId} limit 1")
+    FsCourseRedPacketLog selectUserFsCourseRedPacketLog(@Param("videoId") Long videoId, @Param("userId")Long userId, @Param("periodId")Long periodId);
+
 }

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

@@ -6,6 +6,7 @@ import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.dto.WatchLogDTO;
 import com.fs.course.param.*;
 import com.fs.course.vo.*;
+import com.fs.qw.domain.QwExternalContact;
 import com.fs.sop.vo.QwRatingVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
@@ -403,4 +404,19 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
      * @return  count
      */
     int countByMap(@Param("params") Map<String, Object> params);
+
+
+    @Select("<script>" +
+            "select qec.id,qec.user_id,qec.external_user_id,qec.corp_id,qec.tag_ids,qec.name from fs_course_watch_log qwl " +
+            "left join qw_external_contact qec on qwl.qw_external_contact_id=qec.id " +
+            "        where qwl.log_id in\n" +
+            "        <foreach collection=\"logIds\" item=\"id\" open=\"(\" separator=\",\" close=\")\">\n" +
+            "            #{id}\n" +
+            "        </foreach>" +
+            "GROUP BY " +
+            "    qec.user_id,\n" +
+            "    qec.external_user_id,\n" +
+            "    qec.corp_id"+
+            "</script>")
+    List<QwExternalContact> selectQwWatchLogFomExtContact(@Param("logIds") List<Long> logIds);
 }

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

@@ -149,7 +149,7 @@ public interface FsUserCourseMapper
             "<if test = ' maps.isShow !=null '> " +
             "and c.is_show = #{maps.isShow} " +
             "</if>" +
-            " order by c.course_id  "+
+            " order by c.sort, c.course_id  "+
             "</script>"})
     List<FsUserCourseListPVO> selectFsUserCourseListPVO(@Param("maps") FsUserCourse param);
 
@@ -256,7 +256,7 @@ public interface FsUserCourseMapper
             "        <if test=\"data.keyword != null and data.keyword !='' \">\n" +
             "            AND course_name LIKE concat('%',#{data.keyword},'%')\n" +
             "        </if>" +
-            "order by course_id asc" +
+            "order by sort asc, course_id asc" +
             "</script> ")
     List<FsCourseListBySidebarVO> getFsCourseListBySidebar(@Param("data") FsCourseListBySidebarParam param);
 

+ 10 - 0
fs-service/src/main/java/com/fs/course/mapper/FsUserCoursePeriodMapper.java

@@ -136,4 +136,14 @@ public interface FsUserCoursePeriodMapper
 
     List<FsUserCoursePeriod> selectPeriodListByTrainingCampIds(@Param("trainingCampIds") Long[] trainingCampIds);
 
+    @Update("<script>" +
+            "UPDATE fs_user_course_period SET del_flag = #{delFlag} WHERE period_id IN " +
+            "<foreach collection='ids' item='id' open='(' separator=',' close=')'>" +
+            "#{id}" +
+            "</foreach>" +
+            "</script>")
+    int updateBatchDelFlag(@Param("ids") Long [] ids, @Param("delFlag") Integer delFlag);
+
+
+
 }

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

@@ -11,7 +11,7 @@ public class FsCourseRedPacketLogParam {
     private Long userId;
 
     private Long companyId;
-
+    private Long companyUserId;
     private String companyUserName;
 
     private String nickName;

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

@@ -88,4 +88,6 @@ public interface IFsCourseQuestionBankService
      * @return  list
      */
     List<FsCourseQuestionBankImportDTO> exportData(FsCourseQuestionBank fsCourseQuestionBank);
+
+    R courseAnswerByFsUser(FsCourseQuestionAnswerUParam param);
 }

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

@@ -80,6 +80,6 @@ public interface IFsCourseRedPacketLogService
 
     BigDecimal getNewVipRedPackAmountByCompanyUserIdId(Long userId);
 
-    int retryCourseRedPacketLog(Long[] logIds);
+    R retryCourseRedPacketLog(Long[] logIds);
 
 }

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

@@ -627,7 +627,7 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         JSONObject news = new JSONObject(true); // true 表示保持字段顺序
         news.put("link", linkUrl);
         news.put("title", course.getCourseName());
-        news.put("desc", param.getTitle()==null?"":param.getTitle());
+        news.put("desc", param.getTitle()==null?course.getCourseName():param.getTitle());
         news.put("imgUrl", course.getImgUrl());
         return R.ok().put("news",news);
     }

+ 109 - 4
fs-service/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java

@@ -9,14 +9,12 @@ import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.course.config.CourseConfig;
-import com.fs.course.domain.FsCourseAnswerLogs;
-import com.fs.course.domain.FsCourseQuestionBank;
-import com.fs.course.domain.FsCourseWatchLog;
-import com.fs.course.domain.FsUserCourseCategory;
+import com.fs.course.domain.*;
 import com.fs.course.dto.FsCourseQuestionBankImportDTO;
 import com.fs.course.mapper.*;
 import com.fs.course.param.FsCourseQuestionAnswerUParam;
 import com.fs.course.service.IFsCourseQuestionBankService;
+import com.fs.his.domain.FsUser;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.service.IFsStorePaymentService;
 import com.fs.system.service.ISysConfigService;
@@ -134,6 +132,113 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
         return fsCourseQuestionBankMapper.deleteFsCourseQuestionBankById(id);
     }
 
+    @Override
+    public R courseAnswerByFsUser(FsCourseQuestionAnswerUParam param) {
+        FsUser user = fsUserMapper.selectFsUserByUserId(param.getUserId());
+        if (user==null){
+            return R.error("未识别到领取信息");
+        }
+        //获取配置参数
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+
+        //本次答题正确个数
+        int thisRightCount = 0;
+        //用户答错次数
+        int errorCount = 0;
+        List<FsCourseQuestionBank> incorrectQuestions = new ArrayList<>();
+        //日志id
+        Long logId = null;
+
+        new FsCourseAnswerLogs();
+        FsCourseAnswerLogs rightLog;
+        //判断短链类型
+
+        FsCourseWatchLog log = courseWatchLogMapper.getWatchLogByFsUser(param.getVideoId(), param.getUserId(), param.getCompanyUserId());
+        if (log==null){
+            return R.error("无记录");
+        }
+        if (log.getLogType()!=2){
+            return R.error("未完课");
+        }
+        logId = log.getLogId();
+
+        rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(), param.getQwUserId());
+        if (rightLog != null) {
+            if (log.getRewardType() != null) {
+                // 增加判断,去查询红包记录是否已发送成功,如果成功,则返回当前提示,否则返回答题成功(让其可以继续答题,直到红包领取完成)
+                FsCourseRedPacketLog fsCourseRedPacketLog = redPacketLogMapper.selectUserFsCourseRedPacketLog(param.getVideoId(), param.getUserId(),param.getPeriodId());
+                if(fsCourseRedPacketLog != null && fsCourseRedPacketLog.getStatus() == 1) {
+                    return R.error("该课程已答题完成,不可重复答题");
+                } else {
+                    return R.ok("答题成功");
+                }
+            } else {
+                return R.ok("答题成功");
+            }
+        }
+        errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideo(param.getVideoId(), param.getUserId(),param.getQwUserId());
+
+
+
+        if (errorCount >= config.getAnswerErrorCount()) {
+            return R.error("该课题到达答错次数限制");
+        }
+        int remainCount = config.getAnswerErrorCount()-errorCount-1;
+
+        // 一次性获取所有问题的正确答案
+        Map<Long, FsCourseQuestionBank> correctAnswersMap = fsCourseQuestionBankMapper.selectFsCourseQuestionBankByIds(
+                param.getQuestions().stream().map(FsCourseQuestionBank::getId).collect(Collectors.toList())
+        ).stream().collect(Collectors.toMap(FsCourseQuestionBank::getId, question -> question));
+
+        for (FsCourseQuestionBank questionBank : param.getQuestions()) {
+            FsCourseQuestionBank correctAnswer = correctAnswersMap.get(questionBank.getId());
+            if (correctAnswer.getType() == 1) {
+                if (questionBank.getAnswer().equals(correctAnswer.getAnswer())) {
+                    thisRightCount++;
+                } else {
+                    correctAnswer.setAnswer(null);
+                    incorrectQuestions.add(correctAnswer);
+                }
+            } else if (correctAnswer.getType() == 2) {
+                String[] userAnswers = convertStringToArray(questionBank.getAnswer());
+                String[] correctAnswers = convertStringToArray(correctAnswer.getAnswer());
+
+                Arrays.sort(userAnswers);
+                Arrays.sort(correctAnswers);
+
+                if (Arrays.equals(userAnswers, correctAnswers)) {
+                    thisRightCount++;
+                } else {
+                    correctAnswer.setAnswer(null);
+                    incorrectQuestions.add(correctAnswer);
+                }
+            }
+        }
+
+        FsCourseAnswerLogs logs = new FsCourseAnswerLogs();
+        logs.setWatchLogId(logId);
+        logs.setUserId(param.getUserId());
+        logs.setVideoId(param.getVideoId());
+        logs.setCourseId(param.getCourseId());
+        logs.setCompanyId(param.getCompanyId());
+        logs.setCompanyUserId(param.getCompanyUserId());
+        logs.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null );
+        logs.setQuestionJson(JSONObject.toJSONString(param.getQuestions()));
+        logs.setCreateTime(new Date());
+        logs.setPeriodId(param.getPeriodId());
+
+        if (thisRightCount == param.getQuestions().size()) {
+            logs.setIsRight(1);
+            courseAnswerLogsMapper.insertFsCourseAnswerLogs(logs);
+            return R.ok("答题成功");
+        } else {
+            logs.setIsRight(0);
+            courseAnswerLogsMapper.insertFsCourseAnswerLogs(logs);
+            return R.ok("答题失败").put("incorrectQuestions", incorrectQuestions).put("remain",remainCount);
+        }
+    }
+
     @Override
     @Transactional
     public R courseAnswer(FsCourseQuestionAnswerUParam param,Boolean isH5User) {

+ 20 - 9
fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java

@@ -12,6 +12,8 @@ import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMoneyLogs;
 import com.fs.company.mapper.CompanyMapper;
 import com.fs.company.mapper.CompanyMoneyLogsMapper;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.param.FsCourseRedPacketLogParam;
 import com.fs.course.vo.FsCourseRedPacketLogListPVO;
 import com.fs.his.domain.FsUser;
@@ -37,7 +39,8 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
 {
     @Autowired
     private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
-
+    @Autowired
+    private CompanyMapper companyMapper;
     /**
      * 查询短链课程看课记录
      *
@@ -162,12 +165,14 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
     @Autowired
     private FsUserMapper fsUserMapper;
     @Autowired
-    private CompanyMapper companyMapper;
+    private FsCourseWatchLogMapper courseWatchLogMapper;
     @Autowired
     private CompanyMoneyLogsMapper moneyLogsMapper;
     @Override
     @Transactional
-    public int retryCourseRedPacketLog(Long[] logIds) {
+    public R retryCourseRedPacketLog(Long[] logIds) {
+        int suc=0;
+        int err=0;
         for (int i = 0; i < logIds.length; i++) {
             Long id = logIds[i];
             FsCourseRedPacketLog param = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogByLogId(id);
@@ -179,10 +184,8 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
                 BigDecimal money = company.getMoney();
                 BigDecimal subtract = money.subtract(amount);
                 if (subtract.compareTo(BigDecimal.ZERO)<0){
-                    return 0;
+                    err++;
                 }
-
-
                 WxSendRedPacketParam packetParam = new WxSendRedPacketParam();
                 FsUser user = fsUserMapper.selectFsUserByUserId(param.getUserId());
                 packetParam.setOpenId(user.getMaOpenId());
@@ -202,14 +205,17 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
                     }else {
                         redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
                     }
+                    FsCourseWatchLog log = new FsCourseWatchLog();
+                    log.setLogId(param.getWatchLogId());
+                    log.setRewardType(1);
+                    courseWatchLogMapper.updateFsCourseWatchLog(log);
                     // 添加红包记录
+                    redPacketLog.setLogId(param.getLogId());
                     redPacketLog.setStatus(0);
                     fsCourseRedPacketLogMapper.updateFsCourseRedPacketLog(redPacketLog);
                     // 更新观看记录的奖励类型
-
                     company.setMoney(subtract);
                     companyMapper.updateCompany(company);
-
                     CompanyMoneyLogs logs=new CompanyMoneyLogs();
                     logs.setCompanyId(company.getCompanyId());
                     logs.setRemark("扣除红包金额");
@@ -218,11 +224,16 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
                     logs.setBalance(company.getMoney());
                     logs.setCreateTime(new Date());
                     moneyLogsMapper.insertCompanyMoneyLogs(logs);
+                    suc++;
+                }else {
+                    err++;
                 }
+            }else {
+                err++;
             }
 
         }
-        return 0;
+        return R.ok("成功:"+suc+" 失败:"+err);
     }
 
 }

+ 48 - 24
fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java

@@ -33,10 +33,7 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -262,34 +259,61 @@ public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCourseP
 
     @Override
     public R periodCourseMove(Long id, Long targetId) {
-        List<Long> idList = new ArrayList<>();
-        idList.add(id);
-        idList.add(targetId);
+        // 参数校验
+        if (id == null || targetId == null || id.equals(targetId)) {
+            return R.error(400, "参数不合法");
+        }
+
+        // 批量查询数据
+        List<Long> idList = Arrays.asList(id, targetId);
         List<FsUserCoursePeriodDays> fsUserCoursePeriodDays = baseMapper.selectBatchIds(idList);
-        if(fsUserCoursePeriodDays.isEmpty()){
-            return R.error(404, "当前数据或者移动的目标数据不存在");
+
+        // 数据校验
+        if (fsUserCoursePeriodDays.size() != 2) {
+            return R.error(404, "当前数据或移动的目标数据不存在");
         }
 
-        List<FsUserCoursePeriodDays> list = new ArrayList<>();
-        FsUserCoursePeriodDays periodDays = fsUserCoursePeriodDays.get(0);
-        LocalDate currentDayDate = periodDays.getDayDate();
-        Integer lesson = periodDays.getLesson();
+        // 获取源数据和目标数据
+        FsUserCoursePeriodDays source = fsUserCoursePeriodDays.get(0);
+        FsUserCoursePeriodDays target = fsUserCoursePeriodDays.get(1);
 
-        FsUserCoursePeriodDays periodDaysTarget = fsUserCoursePeriodDays.get(1);
-        periodDays.setDayDate(periodDaysTarget.getDayDate());
-        periodDays.setLesson(periodDaysTarget.getLesson());
-        list.add(periodDays);
+        // 交换数据
+        swapPeriodDaysData(source, target);
 
-        periodDaysTarget.setDayDate(currentDayDate);
-        periodDaysTarget.setLesson(lesson);
-        list.add(periodDaysTarget);
+        // 批量更新
         try {
-            fsUserCoursePeriodDaysMapper.batchUpdateCoursePeriodDays(list);
+            fsUserCoursePeriodDaysMapper.batchUpdateCoursePeriodDays(Arrays.asList(source, target));
+            return R.ok();
         } catch (RuntimeException e) {
-            log.error("上移/下移异常,当前id:{}, 目标id:{}", id, targetId);
-            return R.error();
+            log.error("上移/下移异常,当前id:{},目标id:{}", id, targetId, e);
+            return R.error(500, "操作失败,请稍后重试");
         }
-        return R.ok();
+    }
+
+    /**
+     * 交换两个课程时间段的数据
+     */
+    private void swapPeriodDaysData(FsUserCoursePeriodDays source, FsUserCoursePeriodDays target) {
+        // 保存源数据
+        LocalDate tempDayDate = source.getDayDate();
+        Integer tempLesson = source.getLesson();
+        LocalDateTime tempStartDateTime = source.getStartDateTime();
+        LocalDateTime tempEndDateTime = source.getEndDateTime();
+        LocalDateTime tempLastJoinTime = source.getLastJoinTime();
+
+        // 将目标数据赋给源
+        source.setDayDate(target.getDayDate());
+        source.setLesson(target.getLesson());
+        source.setStartDateTime(target.getStartDateTime());
+        source.setEndDateTime(target.getEndDateTime());
+        source.setLastJoinTime(target.getLastJoinTime());
+
+        // 将保存的源数据赋给目标
+        target.setDayDate(tempDayDate);
+        target.setLesson(tempLesson);
+        target.setStartDateTime(tempStartDateTime);
+        target.setEndDateTime(tempEndDateTime);
+        target.setLastJoinTime(tempLastJoinTime);
     }
 
     @Override

+ 62 - 4
fs-service/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodServiceImpl.java

@@ -16,8 +16,12 @@ import org.springframework.transaction.annotation.Transactional;
 import javax.annotation.Resource;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * 会员营期Service业务层处理
@@ -85,10 +89,42 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
      * @return 结果
      */
     @Override
-    public int updateFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod)
-    {
+    public int updateFsUserCoursePeriod(FsUserCoursePeriod fsUserCoursePeriod) {
+        // 1. 查询原始数据
         fsUserCoursePeriod.setUpdateTime(LocalDateTime.now());
-        return fsUserCoursePeriodMapper.updateFsUserCoursePeriod(fsUserCoursePeriod);
+
+        FsUserCoursePeriod fsUserCoursePeriod1 = fsUserCoursePeriodMapper.selectFsUserCoursePeriodById(fsUserCoursePeriod.getPeriodId());
+        int flag = fsUserCoursePeriodMapper.updateFsUserCoursePeriod(fsUserCoursePeriod);
+
+        // 2. 判定是否变更过开始时间(periodStartingTime)
+        if (fsUserCoursePeriod1.getPeriodStartingTime().equals(fsUserCoursePeriod.getPeriodStartingTime())) {
+            // 如果开始时间没有变化,直接返回
+            return flag;
+        }
+
+        // 3. 计算课程时间差(以天数为单位)
+        long daysDifference = ChronoUnit.DAYS.between(fsUserCoursePeriod1.getPeriodStartingTime(), fsUserCoursePeriod.getPeriodStartingTime());
+
+        // 4. 获取课程天数并进行时间调整
+        FsUserCoursePeriodDays fsUserCoursePeriodDays = new FsUserCoursePeriodDays();
+        fsUserCoursePeriodDays.setPeriodId(fsUserCoursePeriod1.getPeriodId());
+        List<FsUserCoursePeriodDays> list = fsUserCoursePeriodDaysMapper.selectFsUserCoursePeriodDaysList(fsUserCoursePeriodDays);
+
+        // 对每个课程日期进行时间增加
+        for (FsUserCoursePeriodDays coursePeriodDays : list) {
+            // 将课程的日期按照差值天数进行调整
+            LocalDate adjustedDate = coursePeriodDays.getDayDate().plusDays(daysDifference);
+            LocalDateTime startDateTime = coursePeriodDays.getStartDateTime().plusDays(daysDifference);
+            LocalDateTime endDateTime = coursePeriodDays.getEndDateTime().plusDays(daysDifference);
+            LocalDateTime lastJsonTime = coursePeriodDays.getLastJoinTime().plusDays(daysDifference);
+            coursePeriodDays.setDayDate(adjustedDate);
+            coursePeriodDays.setStartDateTime(startDateTime);
+            coursePeriodDays.setEndDateTime(endDateTime);
+            coursePeriodDays.setLastJoinTime(lastJsonTime);
+            fsUserCoursePeriodDaysMapper.updateFsUserCoursePeriodDays(coursePeriodDays); // 更新数据库中的课程日期
+        }
+
+        return flag;
     }
 
     /**
@@ -100,7 +136,29 @@ public class FsUserCoursePeriodServiceImpl implements IFsUserCoursePeriodService
     @Override
     public int deleteFsUserCoursePeriodByIds(Long[] periodIds)
     {
-        return fsUserCoursePeriodMapper.deleteFsUserCoursePeriodByIds(periodIds);
+        if (checkPeriodStatus(periodIds)) {
+            throw new ServiceException("存在进行中或已结束的营期,不允许删除");
+        }
+        return fsUserCoursePeriodMapper.updateBatchDelFlag(periodIds,1);
+    }
+    private boolean checkPeriodStatus(Long[] ids) {
+        LocalDate currentDate = LocalDate.now(); // 2025-06-30
+        Set<Long> idSet = Arrays.stream(ids).collect(Collectors.toSet());
+        List<FsUserCoursePeriod> list = fsUserCoursePeriodMapper.selectFsUserCoursePeriodsByIds(idSet);
+
+        List<FsUserCoursePeriod> filteredList = list.stream()
+                .filter(period -> {
+                    LocalDate startTime = period.getPeriodStartingTime();
+                    LocalDate endTime = period.getPeriodEndTime();
+
+                    boolean isActive = startTime != null && !startTime.isAfter(currentDate)
+                            && (endTime == null || !endTime.isBefore(currentDate));
+
+                    return isActive;
+                })
+                .collect(Collectors.toList());
+
+        return !filteredList.isEmpty();
     }
 
     /**

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

@@ -506,7 +506,6 @@ public class FsUserCourseServiceImpl implements IFsUserCourseService
         String courseJson = JSON.toJSONString(courseMap);
         link.setRealLink(realLink + courseJson);
 
-        link.setLink(random);
         link.setCreateTime(new Date());
 
         //获取过期时间

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

@@ -2,6 +2,7 @@ package com.fs.course.service.impl;
 
 
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.common.exception.ServiceException;
@@ -69,14 +70,33 @@ public class FsUserCourseTrainingCampServiceImpl extends ServiceImpl<FsUserCours
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void del(Long[] ids) {
-        // 检查是否存在进行中的营期
+        // 检查是否存在进行中或者已结束的营期
         if (checkPeriodStatus(ids)) {
-            throw new ServiceException("存在进行中的营期,请营期结束后再操作");
+            throw new ServiceException("存在进行中或已结束的营期,不允许删除");
         }
+        batchUpdateDelFlag(Arrays.asList(ids),1);
 
-        baseMapper.deleteBatchIds(Arrays.asList(ids));
+        // 删除训练营的同时需要删除营期
+        List<FsUserCoursePeriod> fsUserCoursePeriods = fsUserCoursePeriodMapper.selectPeriodListByTrainingCampIds(ids);
+        List<Long> periodIds = fsUserCoursePeriods.stream().map(FsUserCoursePeriod::getPeriodId).collect(Collectors.toList());
+        if(!periodIds.isEmpty()){
+            fsUserCoursePeriodMapper.updateBatchDelFlag(periodIds.toArray(new Long[0]),1);
+        }
     }
 
+    /**
+     * 批量修改训练列表数据
+     * @param ids
+     * @param delFlag
+     * @return
+     */
+    public int batchUpdateDelFlag(List<Long> ids, Integer delFlag) {
+        LambdaUpdateWrapper<FsUserCourseTrainingCamp> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.in(FsUserCourseTrainingCamp::getTrainingCampId, ids)
+                .set(FsUserCourseTrainingCamp::getDelFlag, delFlag);
+
+        return baseMapper.update(null, updateWrapper);
+    }
     /**
      * 检查是否存在进行中的营期
      * @param ids 训练营ID集合

+ 39 - 30
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -438,35 +438,24 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     }
 
     private R handleRoom(FsUserCourseVideoAddKfUParam param,FsUser user) {
-//        //查询客户列表
+        //查询客户列表
 //        List<QwExternalContact> contacts = qwExternalContactMapper.selectQwExternalContactListVOByfsUserId(user.getUserId());
-//        if (contacts.isEmpty()){
-//            return R.error("未注册");
-//        }
-//        //找出对应销售匹配的客户
-//        QwExternalContact matchedContact = contacts.stream()
-//                .filter(contact -> contact.getQwUserId().equals(Long.parseLong(param.getQwUserId())))
-//                .findFirst()
-//                .orElse(null);
-//
-//        if (matchedContact==null){
-//            return R.error("无权限观看,未添加本群主");
-//        }
-//
-//        param.setQwExternalId(matchedContact.getId());
-////        //查询是否有添加客服
-////        QwExternalContact externalContact = qwExternalContactMapper.selectQwExternalContactById(param.getQwExternalId());
-////        if (externalContact==null){
-////            return R.error("客户不存在!");
-////        }
-////        if (!externalContact.getQwUserId().equals(param.getUserId())){
-////            return R.error("无权限观看,添加群主非本群主");
-////        }
-//        FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(param.getQwExternalId(), param.getVideoId(),param.getQwUserId());
-//        if (log==null){
-//            createWatchLog(param);
+//        if (!contacts.isEmpty()){
+//            //找出对应销售匹配的客户
+//            QwExternalContact matchedContact = contacts.stream()
+//                    .filter(contact -> contact.getQwUserId().equals(Long.parseLong(param.getQwUserId())))
+//                    .findFirst()
+//                    .orElse(null);
+//            if (matchedContact!=null){
+//                param.setQwExternalId(matchedContact.getId());
+//                FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(param.getQwExternalId(), param.getVideoId(),param.getQwUserId());
+//                if (log==null){
+//                    createWatchLog(param);
+//                }
+//                return R.ok().put("qwExternalId",matchedContact.getId());
+//            }
 //        }
-//        return R.ok().put("qwExternalId",matchedContact.getId());
+
         FsCourseLink courseLink = courseLinkMapper.selectFsCourseLinkByLink(param.getLink());
         System.out.println("查询的链接参数"+courseLink);
         String msg = "<div style=\"color: red;margin-bottom: 15px;font-weight: bold;\">本课程为群会员独享<br>请长按二维码</div>\n" +
@@ -474,19 +463,24 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         System.out.println("企微传参:"+courseLink.getChatId()+"corpId:"+param.getCorpId());
         QwGroupChatDetailsResult result = qwApiService.groupChatDetails(courseLink.getChatId(), param.getCorpId());
         if(result.getErrCode() != 0){
-            return R.error("企微接口请求失败,请联系管理员:" +result.getErrMsg());
+            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();
@@ -494,17 +488,20 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 //        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);
             }
 
@@ -882,7 +879,18 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             return R.error("无记录");
         }
         if (log.getRewardType() != null) {
-            return R.error("奖励已发放");
+            FsCourseRedPacketLog fsCourseRedPacketLog = redPacketLogMapper.selectUserFsCourseRedPacketLog(param.getVideoId(), param.getUserId(),param.getPeriodId());
+            if(fsCourseRedPacketLog != null && fsCourseRedPacketLog.getStatus() == 1) {
+                return R.error("奖励已发放");
+            }
+            if(fsCourseRedPacketLog != null && fsCourseRedPacketLog.getStatus() == 0) {
+                if(StringUtils.isNotEmpty(fsCourseRedPacketLog.getResult())){
+                    R r = JSON.parseObject(fsCourseRedPacketLog.getResult(), R.class);
+                    return r;
+                } else {
+                    return R.error();
+                }
+            }
         }
 
 
@@ -1499,7 +1507,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         List<FsUserCourseVideo> collect = videoResourceList.stream().map(e -> {
             FsUserCourseVideo entity = new FsUserCourseVideo();
-            entity.setTitle(e.getFileName());
+            entity.setTitle(e.getResourceName());
             entity.setVideoUrl(e.getVideoUrl());
             entity.setThumbnail(e.getThumbnail());
             entity.setDuration(e.getDuration().longValue());
@@ -1570,6 +1578,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         news.put("miniprogramPage", linkByMiniApp);
 
         return R.ok().put("data",news);
+
     }
 
     @Override

+ 2 - 1
fs-service/src/main/java/com/fs/erp/domain/ErpGoods.java

@@ -19,5 +19,6 @@ public class ErpGoods {
     String stock_status_code;
     List<ErpGoodsSku> skus;
     List<ErpCombineItem> combine_item;
-
+    //hzOMSErp 使用 商品id
+    Long storeProductId;
 }

+ 2 - 1
fs-service/src/main/java/com/fs/erp/domain/ErpRefundOrder.java

@@ -11,6 +11,7 @@ public class ErpRefundOrder {
     String vip_code;
     String trade_platform_code;
     List<ErpOrderItem> details;
-
+    String orderCode;
+    Long afterSalesId;
 
 }

+ 1 - 0
fs-service/src/main/java/com/fs/erp/dto/ErpRefundUpdateRequest.java

@@ -9,5 +9,6 @@ public class ErpRefundUpdateRequest implements Serializable {
     String tid;//平台单号
     String oid;//子订单号
     Integer refund_state;//0:未退款 1:退款完成 2:退款中
+    Long storeAfterSalesId;//售后id
 
 }

+ 179 - 0
fs-service/src/main/java/com/fs/erp/dto/sdk/HzOMS/api/HzOMSClient.java

@@ -0,0 +1,179 @@
+package com.fs.erp.dto.sdk.HzOMS.api;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.http.HttpRequest;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
+import com.fs.his.config.FsSysConfig;
+import com.fs.his.utils.ConfigUtil;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+@Component
+@Slf4j
+public class HzOMSClient {
+
+    @Autowired
+    private ConfigUtil configUtil;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    private final String TOKEN_URL = "0";
+
+    private final String EXEC_RULE_URL = "1";
+
+    private final String HZOMS_TOKEN_KEY = "erp::hzoms::token";
+
+    /**
+     * 根据规则名称发送请求
+     *
+     * @param ruleName
+     * @return
+     */
+    public JSONObject send(String ruleName, JSONObject params) {
+        CloseableHttpClient client = null;
+        try {
+            client = HttpClients.createDefault();
+            HttpPost httpPost = new HttpPost(getRequestUrl(EXEC_RULE_URL));
+            JSONObject bodyObj = new JSONObject();
+            bodyObj.put("ruleName", ruleName);
+            bodyObj.put("dataPackage", params);
+            log.info("瀚智OMS请求参数打印:{}", bodyObj.toJSONString());
+            StringEntity entity = new StringEntity(bodyObj.toJSONString(),"UTF-8");
+            httpPost.setEntity(entity);
+//            httpPost.setHeader("Accept", "application/json");
+            httpPost.setHeader("Content-type", "application/json");
+            httpPost.setHeader("token", getToken());
+            httpPost.setHeader("itenantid", getItenantid());
+            HttpEntity response = client.execute(httpPost).getEntity();
+            String responseString = EntityUtils.toString(response);
+            log.info(" 执行动作," + ruleName + "瀚智OMS接口响应数据:{}", responseString);
+            JSONObject jsonObject = JSONObject.parseObject(responseString);
+            if(null != jsonObject && !jsonObject.isEmpty() && jsonObject.containsKey("code") && !"200".equals(jsonObject.getString("code"))){
+                log.info("瀚智OMS接口报错了,错误信息:{}", jsonObject.getString("message"));
+            }
+            return jsonObject;
+        } catch (UnsupportedEncodingException e) {
+            log.error("请求参数编码异常", e);
+            throw new RuntimeException("请求参数编码异常", e);
+        } catch (IOException e) {
+            log.error("HTTP请求执行异常", e);
+            throw new RuntimeException("HTTP请求执行异常, 执行动作" + ruleName, e);
+        } finally {
+            if (null != client) {
+                try {
+                    client.close();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+
+    /**
+     * 获取token
+     *
+     * @return
+     */
+    public String getToken() {
+        try {
+            String token = redisCache.getCacheObject(HZOMS_TOKEN_KEY);
+            if (StringUtils.isNotBlank(token)) {
+                return token;
+            } else {
+                try {
+                    synchronized (HZOMS_TOKEN_KEY) {
+                        String tokenSec = redisCache.getCacheObject(HZOMS_TOKEN_KEY);
+                        if (StringUtils.isNotBlank(tokenSec)) {
+                            return tokenSec;
+                        }
+                        String requestUrl = getRequestUrl(TOKEN_URL);
+                        FsSysConfig sysConfig = configUtil.getSysConfig();
+                        HashMap headers = new HashMap<>();
+                        headers.put("Content-Type", "application/x-www-form-urlencoded");
+                        String body = HttpRequest.post(requestUrl)
+                                .addHeaders(headers)
+                                .form("appkey", sysConfig.getErpHzOMSAppKey())
+                                .form("secret", sysConfig.getErpHzOMSAppsecret()).execute().body();
+                        JSONObject jsonObject = JSONObject.parseObject(body);
+                        if (null != jsonObject && !jsonObject.isEmpty()) {
+                            String code = jsonObject.getString("code");
+                            if (null != code && "200".equals(code)) {
+                                log.info("瀚智OMS getTokenHTTP请求结果::" + jsonObject);
+                                JSONObject tokenData = jsonObject.getJSONObject("data");
+                                if (null != tokenData && !tokenData.isEmpty()) {
+                                    String tokenStr = tokenData.getString("token");
+                                    Long expires = tokenData.getLong("expires_in");
+                                    long currentTime = System.currentTimeMillis();
+                                    //提前过期
+                                    int intExact = Math.toIntExact((expires - currentTime - 180000) / 1000);
+                                    redisCache.setCacheObject(HZOMS_TOKEN_KEY, tokenStr, (int) intExact, TimeUnit.SECONDS);
+                                }
+                            }else{
+                                log.error("瀚智OMS getTokenHTTP请求失败::" + jsonObject);
+                                return "";
+                            }
+                        }
+                    }
+                } catch (ArithmeticException e) {
+                    System.out.println("时间转换溢出");
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                    log.error("瀚智OMS获取tokenHTTP请求失败", ex);
+                }
+                return getToken();
+            }
+        } catch (Exception ex) {
+            log.error("获取token异常", ex);
+            return "";
+        }
+    }
+
+    /**
+     * 获取瀚智租户id
+     *
+     * @return
+     */
+    public String getItenantid() {
+        FsSysConfig sysConfig = configUtil.getSysConfig();
+        String erpHzOMSItenantid = sysConfig.getErpHzOMSItenantid();
+        return erpHzOMSItenantid;
+    }
+
+    /**
+     * 根据type获取请求地址
+     *
+     * @param tpye
+     * @return
+     */
+    public String getRequestUrl(String tpye) {
+        FsSysConfig sysConfig = configUtil.getSysConfig();
+        String url = "";
+        if (TOKEN_URL.equals(tpye)) {
+            url = sysConfig.getErpHzOMTokenUrl();
+        } else if (EXEC_RULE_URL.equals(tpye)) {
+            url = sysConfig.getErpHzOMBaseUrl();
+        }
+        return url;
+    }
+
+}

+ 91 - 0
fs-service/src/main/java/com/fs/erp/dto/sdk/HzOMS/utils/HzOMSUtils.java

@@ -0,0 +1,91 @@
+package com.fs.erp.dto.sdk.HzOMS.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+
+public class HzOMSUtils {
+
+    public static String getHzOMSUrl(String url) {
+        return "http://hzoms.51fengshang.com" + url;
+    }
+
+
+    /**
+     * 验签 669b0134c045bb06bb55767e0bc0bf99 B0A162029069D9067D0137E3869ABAAA
+     * 数据签名,签名算法为MD5(timestamp+datapakege+secret)appkey与 secret是一对,由瀚智提供(MD5 加
+     * 密为 32 位大写)
+     *
+     * @return
+     */
+    public static Boolean signValidate(String timestamp, String datapackage, String secret, String sign) throws IOException {
+        StringBuilder sb  = new StringBuilder();
+        sb.append(timestamp).append(datapackage).append(secret);
+        String s = encryptMD5(sb.toString());
+        String upperCase = s.toUpperCase();
+        return upperCase.equals(sign);
+    }
+
+    private static String byte2hex(byte[] bytes) {
+        StringBuilder sign = new StringBuilder();
+        for (int i = 0; i < bytes.length; i++) {
+            String hex = Integer.toHexString(bytes[i] & 0xFF);
+            if (hex.length() == 1) {
+                //保证所有的16进制都是两位:00-ff,其中[80~ff]代表[-128,-1]
+                sign.append("0");
+            }
+            sign.append(hex);
+        }
+        return sign.toString();
+    }
+    private static String encryptMD5(String data) throws IOException {
+        String s = null;
+        byte[] bytes = null;
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            bytes = md.digest(data.getBytes("UTF-8"));
+            s = bytesToHex(bytes);
+        } catch (GeneralSecurityException gse) {
+            String msg = getStringFromException(gse);
+            throw new IOException(msg);
+        }
+        return s;
+    }
+
+    private static String getStringFromException(Throwable e) {
+        String result = "";
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(bos);
+        e.printStackTrace(ps);
+        try {
+            result = bos.toString("UTF-8");
+        } catch (IOException ioe) {
+        }
+        return result;
+    }
+
+    /**
+     * 字节数组转十六进制字符串(优化版)
+     * @param bytes 字节数组
+     * @return 十六进制字符串
+     */
+    private static String bytesToHex(byte[] bytes) {
+        // 一个字节对应两个十六进制字符
+        char[] hexChars = new char[bytes.length * 2];
+        // 预定义十六进制字符
+        final char[] hexArray = "0123456789abcdef".toCharArray();
+
+        for (int i = 0; i < bytes.length; i++) {
+            // 取字节的高4位
+            int high = (bytes[i] & 0xF0) >>> 4;
+            // 取字节的低4位
+            int low = bytes[i] & 0x0F;
+            // 转换为对应的十六进制字符
+            hexChars[i * 2] = hexArray[high];
+            hexChars[i * 2 + 1] = hexArray[low];
+        }
+        return new String(hexChars);
+    }
+}

+ 122 - 0
fs-service/src/main/java/com/fs/erp/service/impl/HzOMSErpGoodsServiceImpl.java

@@ -0,0 +1,122 @@
+package com.fs.erp.service.impl;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.erp.domain.ErpGoods;
+import com.fs.erp.dto.*;
+import com.fs.erp.dto.sdk.HzOMS.api.HzOMSClient;
+import com.fs.erp.service.IErpGoodsService;
+import com.fs.his.domain.FsStoreProduct;
+import com.fs.his.domain.FsStoreProductAttr;
+import com.fs.his.domain.FsStoreProductAttrValue;
+import com.fs.his.service.IFsStoreProductAttrService;
+import com.fs.his.service.IFsStoreProductAttrValueService;
+import com.fs.his.service.IFsStoreProductService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author MixLiu
+ * @date 2025/6/26 下午5:55)
+ */
+@Service
+public class HzOMSErpGoodsServiceImpl implements IErpGoodsService {
+
+
+    @Autowired
+    HzOMSClient hzOMSClient;
+
+    @Autowired
+    private IFsStoreProductService fsStoreProductService;
+
+    @Autowired
+    private IFsStoreProductAttrService fsStoreProductAttrService;
+
+    @Autowired
+    private IFsStoreProductAttrValueService fsStoreProductAttrValueService;
+
+    @Override
+    public BaseResponse addGoods(ErpGoods goods) {
+
+        Long storeProductId = goods.getStoreProductId();
+        JSONObject hzGoods = buildGoods(storeProductId);
+
+        hzOMSClient.send("oms_open_ptshopgoods_sync", hzGoods);
+
+        return null;
+    }
+
+    @Override
+    public ErpGoodsQueryResponse getGoods(ErpGoodsQueryRequert param) {
+        return null;
+    }
+
+    @Override
+    public ErpGoodsStockQueryResponse getGoodsStock(ErpGoodsStockQueryRequert param) {
+        return null;
+    }
+
+    /**
+     * 构建店铺商品上传的参数
+     *
+     * @param storeProductId
+     * @return
+     */
+    public JSONObject buildGoods(Long storeProductId) {
+
+        JSONObject res = new JSONObject();
+        //商品
+        FsStoreProduct fsStoreProduct = fsStoreProductService.selectFsStoreProductByProductId(storeProductId);
+
+        //平台店铺编码 todo 测试环境只能写死 110111 用于联调测试 fsStoreProduct.getStoreId()
+        res.put("cptshopcode", 110111);
+        JSONArray goodsList = new JSONArray();
+        //添加上传的商品
+        JSONObject goods = new JSONObject();
+        //平台商品编码
+        goods.put("cptgoodsid", fsStoreProduct.getProductId());
+        //平台商品名称
+        goods.put("cptgoodsname", fsStoreProduct.getProductName());
+        //平台商品价格 f
+        goods.put("fptprice", fsStoreProduct.getPrice());
+        //平台商品库存 f
+        goods.put("fptstock", fsStoreProduct.getStock());
+        //上架状态0 下架;1 上架 f
+        goods.put("isalestatus", fsStoreProduct.getIsShow());
+        //平台分类编码 f
+//        goods.put("cptgroupid", );
+        //平台分类名称 f
+//        goods.put("cptgroupname", );
+        goods.put("cptgoodsid", fsStoreProduct.getProductId());
+
+        //商品规格
+        JSONArray attrList = new JSONArray();
+        //商品规格List
+        List<FsStoreProductAttrValue> fsStoreProductAttrValues = fsStoreProductAttrValueService.selectFsStoreProductAttrValueListByProductId(fsStoreProduct.getProductId());
+        fsStoreProductAttrValues.forEach(attr->{
+            JSONObject attrObj = new JSONObject();
+            //平台规格名称
+            attrObj.put("cptspecname", attr.getSku());
+            //平台规格编码
+            attrObj.put("cptspeccode", attr.getId());
+            //自定义规格编码 f
+//            attrObj.put("ccustid", attr.getAttrName());
+            //规格库存
+            attrObj.put("fptstock", attr.getStock());
+            //规格价格
+            attrObj.put("fptprice", attr.getPrice());
+            //规格上架状态 0 下架;1 上架
+            attrObj.put("isalestatus", fsStoreProduct.getIsShow());
+            attrList.add(attrObj);
+        });
+
+        //规格信息 f
+        goods.put("skus",attrList);
+        goodsList.add(goods);
+        res.put("goodsList", goodsList);
+
+        return res;
+    }
+}

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

@@ -0,0 +1,383 @@
+package com.fs.erp.service.impl;
+
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.utils.StringUtils;
+import com.fs.erp.domain.ErpOrder;
+import com.fs.erp.domain.ErpRefundOrder;
+import com.fs.erp.dto.*;
+import com.fs.erp.dto.sdk.HzOMS.api.HzOMSClient;
+import com.fs.erp.service.IErpOrderService;
+
+import com.fs.his.domain.*;
+import com.fs.his.service.*;
+import com.fs.his.utils.PhoneUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 瀚智OMS EPR订单对接服务实现类
+ */
+@Slf4j
+@Service
+public class HzOMSErpOrderServiceImpl implements IErpOrderService {
+
+    @Autowired
+    private IFsStoreOrderService fsStoreOrderService;
+
+    @Autowired
+    private IFsStoreOrderItemService fsStoreOrderItemService;
+
+    @Autowired
+    private IFsStoreProductService fsStoreProductService;
+
+    @Autowired
+    private IFsStoreProductAttrValueService fsStoreProductAttrValueService;
+
+    @Autowired
+    private IFsStoreAfterSalesService fsStoreAfterSalesService;
+
+    @Autowired
+    private IFsStoreService fsStoreService;
+
+    @Autowired
+    HzOMSClient hzOMSClient;
+
+    @Override
+    public ErpOrderResponse addOrder(ErpOrder order) {
+        try {
+            JSONObject hzOrder = buildHzOMSOrder(order.getPlatform_code());
+
+            JSONObject omsOpenPtorderCreate = hzOMSClient.send("oms_open_ptorder_create", hzOrder);
+            ErpOrderResponse res = new ErpOrderResponse();
+            res.setCode("hzomssuccess");
+            return res;
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public ErpOrderResponse refundOrder(ErpRefundOrder order) {
+        String orderCode = order.getOrderCode();
+        Long storeAfterSalesId = order.getAfterSalesId();
+        JSONObject jsonObject = buildRefundOrder(orderCode, storeAfterSalesId);
+        JSONObject omsOpenPtreturnorderCreate = hzOMSClient.send("oms_open_ptreturnorder_create", jsonObject);
+        return null;
+    }
+
+    @Override
+    public ErpDeliverysResponse getDeliver(ErpDeliverysRequest param) {
+        return null;
+    }
+
+    @Override
+    public ErpOrderQueryResponse getOrder(ErpOrderQueryRequert param) {
+        return null;
+    }
+
+    @Override
+    public BaseResponse refundUpdate(ErpRefundUpdateRequest param) {
+
+        String orderCode = param.getTid();
+        Long storeAfterSalesId = param.getStoreAfterSalesId();
+        FsStoreAfterSales fsStoreAfterSales = fsStoreAfterSalesService.selectFsStoreAfterSalesById(storeAfterSalesId);
+
+        FsStoreOrder fsStoreOrder = fsStoreOrderService.selectFsStoreOrderByOrderCode(orderCode);
+
+        JSONObject jsonObject = new JSONObject();
+        //平台店铺编码 todo 测试环境只能写死 110111 用于联调测试 fsStoreOrder.getStoreId()
+        jsonObject.put("cptshopcode", 110111);
+        //平台订单号
+        jsonObject.put("cptordercode", orderCode);
+        //平台售后单号
+        jsonObject.put("cptretruncode", fsStoreAfterSales.getId());
+        //申请退款金额
+        jsonObject.put("frefundamt", fsStoreAfterSales.getRefundAmount());
+
+        Integer iaftersalesstatus = null;
+        //退货退款
+        if (Integer.valueOf(1).equals(fsStoreAfterSales.getRefundType())) {
+            if (fsStoreAfterSales.getSalesStatus().equals(1)) {
+                //用户取消申请
+                iaftersalesstatus = 30;
+            } else if (fsStoreAfterSales.getSalesStatus().equals(2)) {
+                //商家拒绝
+                iaftersalesstatus = 50;
+            } else if (fsStoreAfterSales.getSalesStatus().equals(3) && fsStoreAfterSales.getStatus().equals(4)) {
+                //已完成&&退款成功
+                iaftersalesstatus = 60;
+            } else {
+                //售后中
+                //售后中对应erp依然是申请阶段不需要做任何操作
+                BaseResponse baseResponse = new BaseResponse();
+                baseResponse.setSuccess(true);
+                return baseResponse;
+            }
+        } else if (Integer.valueOf(0).equals(fsStoreAfterSales.getRefundType())) {
+            if (fsStoreAfterSales.getSalesStatus().equals(1)) {
+                //用户取消申请
+                iaftersalesstatus = 30;
+            } else if (fsStoreAfterSales.getSalesStatus().equals(2)) {
+                //商家拒绝
+                iaftersalesstatus = 35;
+            } else if (fsStoreAfterSales.getSalesStatus().equals(3) && fsStoreAfterSales.getStatus().equals(4)) {
+                //已完成&&退款成功
+                iaftersalesstatus = 25;
+            } else {
+                //售后中
+                //售后中对应erp依然是申请阶段不需要做任何操作
+                BaseResponse baseResponse = new BaseResponse();
+                baseResponse.setSuccess(true);
+                return baseResponse;
+            }
+        }
+        //售后状态 10 申请部分退款;15 申请全额退款;25 同意退款;30 取消申请;35 驳回退款;40 申请退货退款;45 同意退货退款;50 驳回退货退款;55 退货成功;60 退款成功
+        jsonObject.put("iaftersalesstatus", iaftersalesstatus);
+        JSONObject omsOpenPtreturnorderStatuschange = hzOMSClient.send("oms_open_ptreturnorder_statuschange", jsonObject);
+        BaseResponse res = new BaseResponse();
+        res.setSuccess(true);
+        return res;
+    }
+
+    @Override
+    public ErpOrderResponse finishOrder(ErpOrder order) {
+        return null;
+    }
+
+    /**
+     * 构建瀚智创建订单参数
+     *
+     * @param orderCode
+     * @return
+     */
+    private JSONObject buildHzOMSOrder(String orderCode) {
+        //通过订单号查询更多订单信息
+        FsStoreOrder fsOrder = fsStoreOrderService.selectFsStoreOrderByOrderCode(orderCode);
+        FsStore fsStore = fsStoreService.selectFsStoreByStoreId(fsOrder.getStoreId());
+
+        JSONObject obj = new JSONObject();
+        String iordertype = "";
+        if (Integer.valueOf(1).equals(fsOrder.getShippingType())) {
+            iordertype = "2";
+        } else if (Integer.valueOf(2).equals(fsOrder.getShippingType())) {
+            iordertype = "1";
+        }
+        //订单类型 0 O2O配送订单; 1 O2O 自提订单; 2 B2C 快递配送
+        obj.put("iordertype", iordertype);
+        //订单单号
+        obj.put("cptordercode", fsOrder.getOrderCode());
+        //平台店铺编码 todo 测试环境只能写死 110111 用于联调测试 fsOrder.getStoreId()
+        obj.put("cptshopcode", 110111);
+        //平台店铺名称
+        obj.put("cptshopname", fsStore.getStoreName());
+        Integer iorderstatus = null;
+        //自提且已经支付
+        if ("1".equals(iordertype) && Integer.valueOf(1).equals(fsOrder.getIsPay())) {
+            iorderstatus = 30;
+        } else {
+            if (Integer.valueOf(1).equals(fsOrder.getStatus())) {
+                iorderstatus = 0;
+            } else if (Integer.valueOf(2).equals(fsOrder.getStatus())) {
+                iorderstatus = 5;
+            }
+        }
+        //订单状态 0待接单 5待拣货 10待配送 15配送中 20已完成 25已取消 30待自提
+        obj.put("iorderstatus", iorderstatus);
+
+        String userAddress = fsOrder.getUserAddress();
+        String[] s = null;
+        if (StringUtils.isNotBlank(userAddress)) {
+            s = userAddress.split(" ");
+        } else {
+            throw new RuntimeException("用户收货地址有问题");
+        }
+
+        //收货省
+        obj.put("cprovince", s[0]);
+        //收货市
+        obj.put("ccity", s[1]);
+        //收货区
+        obj.put("carea", s[2]);
+        //收货城镇乡 f
+//        obj.put("ctown", "");
+        //收货人密文 无密文填写明文
+        obj.put("creceivers", fsOrder.getUserName());
+        //收货人联系电话密文 无密文填写明文
+        obj.put("creceiversphone", fsOrder.getUserPhone());
+        //收货人详细地址密文 无密文填写明文
+        obj.put("creceiversaddress", userAddress);
+        //收货人脱敏信息
+        obj.put("cdesenreceivers", fsOrder.getUserName());
+        //收货联系电话脱敏信息
+        obj.put("cdesenreceiversphone", PhoneUtil.decryptPhone(fsOrder.getUserPhone()));
+        //收货联系地址脱敏信息
+        obj.put("cdesenreceiversaddress", userAddress);
+        //订单总金额 单位元
+        obj.put("forderamount", fsOrder.getTotalPrice());
+        //支付金额 单位元
+        obj.put("fpayamount", fsOrder.getPayPrice());
+        //订单总优惠 单位元 f
+        obj.put("fdiscamount", fsOrder.getDiscountMoney());
+        //商家优惠 f
+//        obj.put("fpoidisc", "");
+        //平台优惠 f
+//        obj.put("fplatformdisc", "");
+        //运费优惠 f
+//        obj.put("ffreightdiscount", "");
+        //运费 f
+        obj.put("ffreight", fsOrder.getDeliveryPayMoney());
+        //下单时间 格式 yyyy-MM-dd hh:mm:ss 如:2025-01-01 00:11:22
+        obj.put("dorderstarttime", fsOrder.getCreateTime());
+        //是否处方 0 否 1 是 可选
+        obj.put("iisprescription", fsOrder.getIsPrescribe());
+        //买家备注 f
+        obj.put("corderremark", fsOrder.getRemark());
+        //商家备注 f
+//        obj.put("csellermessage", "");
+        //自提订单自提码 f
+//        obj.put("ctakecode", "");
+        //订单旗帜 1 红 2 黄 3 绿 4 蓝 5 紫 f
+//        obj.put("corderflag", "");
+        //支付时间  格式 yyyy-MM-dd hh:mm:ss 如:2025-01-01 00:11:22 f
+        obj.put("dpurchasetime", fsOrder.getPayTime());
+        //预计送达时间 格式 yyyy-MM-dd hh:mm:ss 如:2025-01-01 00:11:22 f
+//        obj.put("dpredeliverytime", "");
+        //实际送达时间  格式 yyyy-MM-dd hh:mm:ss 如:2025-01-01 00:11:22 f
+//        obj.put("dconfirmtime", "");
+        List<FsStoreOrderItem> fsStoreOrderItems = fsStoreOrderItemService.selectFsStoreOrderItemListByOrderId(fsOrder.getOrderId());
+        int totalOrderItemCount = fsStoreOrderItems.size();
+        BigDecimal divide = fsOrder.getDiscountMoney().divide(BigDecimal.valueOf(totalOrderItemCount), 2, BigDecimal.ROUND_HALF_UP);
+        JSONArray goodArr = new JSONArray();
+
+        fsStoreOrderItems.forEach(fsStoreOrderItem -> {
+            JSONObject goodItem = new JSONObject();
+            //商品编码
+            goodItem.put("cptgoodsid", fsStoreOrderItem.getProductId());
+            //商品规格编码 f
+            goodItem.put("cptspeccode", fsStoreOrderItem.getProductAttrValueId());
+
+            FsStoreProduct fsStoreProduct = fsStoreProductService.selectFsStoreProductById(fsStoreOrderItem.getProductId());
+            FsStoreProductAttrValue fsStoreProductAttrValue = new FsStoreProductAttrValue();
+            //判断是否含有商品规格信息 有则查询商品规格信息 没有取商品的价格
+            if(null != fsStoreOrderItem.getProductAttrValueId()){
+                fsStoreProductAttrValue = fsStoreProductAttrValueService.selectFsStoreProductAttrValueById(fsStoreOrderItem.getProductAttrValueId());
+            }else{
+                fsStoreProductAttrValue.setPrice(fsStoreProduct.getPrice());
+            }
+            //商品名称
+            goodItem.put("cgoodsname", fsStoreProduct.getProductName());
+            //商品规格名称 f
+//            goodItem.put("cspecname", );
+            //购买数量
+            goodItem.put("fqty", fsStoreOrderItem.getNum());
+            //原单价
+            goodItem.put("fnormprice", fsStoreProductAttrValue.getPrice());
+
+            //实售单价 优惠后的单价 单位元
+            goodItem.put("fprice", fsStoreProductAttrValue.getPrice().subtract(divide));
+            //商品优惠金额 f
+//            goodItem.put("fdiscprice", new BigDecimal(0));
+            //商品条码 f
+//            goodItem.put("cbarcode", "");
+            goodArr.add(goodItem);
+        });
+        //订单商品信息
+        obj.put("ptorder_goods_list", goodArr);
+
+        return obj;
+    }
+
+    /**
+     * 构建申请售后订单参数
+     *
+     * @param orderCode
+     * @return
+     */
+    private JSONObject buildRefundOrder(String orderCode, Long storeAfterSalesId) {
+        //通过订单号查询更多订单信息
+        FsStoreOrder fsOrder = fsStoreOrderService.selectFsStoreOrderByOrderCode(orderCode);
+
+        Long orderId = fsOrder.getOrderId();
+        FsStoreAfterSales fsStoreAfterSales = fsStoreAfterSalesService.selectFsStoreAfterSalesById(storeAfterSalesId);
+
+        JSONObject obj = new JSONObject();
+        //平台店铺编码 todo 测试环境只能写死 110111 用于联调测试 fsStoreAfterSales.getStoreId()
+        obj.put("cptshopcode", 110111);
+        //平台订单号
+        obj.put("cptordercode", orderCode);
+        //平台售后单号
+        obj.put("cptretruncode", fsStoreAfterSales.getId());
+        Integer iretruntype = null;
+        if (Integer.valueOf(0).equals(fsStoreAfterSales.getRefundType())) {
+            iretruntype = 5;
+        } else if (Integer.valueOf(1).equals(fsStoreAfterSales.getRefundType())) {
+            iretruntype = 1;
+        }
+        //售后类型 1 退货退款 5 仅退款
+        obj.put("iretruntype", iretruntype);
+        //申请类型 0 部分退款 1 全部退款
+        obj.put("iservicetype", Integer.valueOf(1));
+        Integer iaftersalesstatus = null;
+        //退货退款
+        if (iretruntype.equals(1)) {
+            if (fsStoreAfterSales.getStatus().equals(0)) {
+                //申请退货退款
+                iaftersalesstatus = 40;
+            }
+        } else if (iretruntype.equals(5)) {
+            if (fsStoreAfterSales.getStatus().equals(0)) {
+                //申请全额退款
+                iaftersalesstatus = 15;
+            }
+        }
+
+        //售后状态10 申请部分退款;15 申请全额退款 25 同意退款 30 取消申请 35 驳回退款申请 40 申请退货退款 45 同意退货退款 50 驳回退货退款申请 55 退货成功 60 退款成功
+        obj.put("iaftersalesstatus", iaftersalesstatus);
+        //申请退款金额
+        obj.put("frefundamt", fsStoreAfterSales.getRefundAmount());
+        //申请原因 f
+        obj.put("creason", fsStoreAfterSales.getReasons());
+        //售后图片url地址 f
+        obj.put("cimgurl", fsStoreAfterSales.getExplainImg());
+
+        JSONArray goodsList = new JSONArray();
+
+        List<FsStoreOrderItem> fsStoreOrderItems = fsStoreOrderItemService.selectFsStoreOrderItemListByOrderId(fsOrder.getOrderId());
+        int totalOrderItemCount = fsStoreOrderItems.size();
+        BigDecimal divide = fsOrder.getDiscountMoney().divide(BigDecimal.valueOf(totalOrderItemCount), 2, BigDecimal.ROUND_HALF_UP);
+        fsStoreOrderItems.forEach(fsStoreOrderItem -> {
+            JSONObject goodItem = new JSONObject();
+            //商品编码
+            goodItem.put("cptgoodsid", fsStoreOrderItem.getProductId());
+            //商品规格编码 f
+            goodItem.put("cptspeccode", fsStoreOrderItem.getProductAttrValueId());
+
+            FsStoreProduct fsStoreProduct = fsStoreProductService.selectFsStoreProductById(fsStoreOrderItem.getProductId());
+            FsStoreProductAttrValue fsStoreProductAttrValue = fsStoreProductAttrValueService.selectFsStoreProductAttrValueById(fsStoreOrderItem.getProductAttrValueId());
+            //商品名称
+            goodItem.put("cgoodsname", fsStoreProduct.getProductName());
+            //商品规格名称 f
+//            goodItem.put("cspecname", );
+            //本次申请数量
+            goodItem.put("fqty", fsStoreOrderItem.getNum());
+            //原单价
+            goodItem.put("fnormprice", fsStoreProductAttrValue.getPrice());
+            //本次申请退款单价 单位元
+            goodItem.put("fprice", fsStoreProductAttrValue.getPrice().subtract(divide));
+
+            goodsList.add(goodItem);
+        });
+        //售后商品信息
+        obj.put("ptreturnorder_goods_list", goodsList);
+
+        return obj;
+    }
+
+}

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

@@ -191,7 +191,7 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
             // 单价
             erpWdtOrder.setPrice(fsStoreProduct.getPrice());
             // 状态
-            erpWdtOrder.setStatus(TradeStatus.SHIPPED.getValue());
+            erpWdtOrder.setStatus(TradeStatus.PAID_WAITING_FOR_SHIPMENT.getValue());
             // 退款状态
             erpWdtOrder.setRefundStatus(RefundStatus.NO_REFUND.getValue());
 
@@ -605,6 +605,12 @@ public class WdtErpOrderServiceImpl implements IErpOrderService {
             config.propertyNamingStrategy = PropertyNamingStrategy.CamelCase;
             ErpWdtTradeQueryResponse tradeQueryResponseDTO = JSON.parseObject(execute, ErpWdtTradeQueryResponse.class);
             if(ObjectUtil.equal(0,tradeQueryResponseDTO.getCode())){
+//                if (tradeQueryResponseDTO.getTrades().isEmpty()){
+//                    //尝试查询原始订单
+//                    map.put("tid",param.getCode());
+//                    execute = client.execute("sales_api_trade_query.php", map);
+//                    ErpWdtTradeInfo2 erpWdtTradeInfo2 = JSON.parseObject(execute, ErpWdtTradeInfo2.class);
+//                }
                 ErpOrderQueryResponse erpOrderQueryResponse = ErpWdtToErpOrderMapper.INSTANCE.toErpOrderQueryResponse(tradeQueryResponseDTO);
                 log.info("查询订单成功: {}", erpOrderQueryResponse);
                 return erpOrderQueryResponse;

+ 131 - 4
fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java

@@ -27,11 +27,11 @@ import com.fs.fastgptApi.service.Impl.AudioServiceImpl;
 import com.fs.fastgptApi.util.AiImgUtil;
 import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.qw.domain.*;
-import com.fs.qw.mapper.QwCompanyMapper;
-import com.fs.qw.mapper.QwExternalContactInfoMapper;
-import com.fs.qw.mapper.QwExternalContactMapper;
-import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.mapper.*;
+import com.fs.qw.param.QwAutoTagsRulesTags;
 import com.fs.qw.service.*;
+import com.fs.qwApi.domain.QwResult;
+import com.fs.qwApi.param.QwEditUserTagParam;
 import com.fs.qwApi.param.QwSendMsgParam;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.qwHookApi.param.QwHookSendMsgParam;
@@ -39,9 +39,11 @@ import com.fs.qwHookApi.vo.QwHookMsgVO;
 import com.fs.qwHookApi.vo.QwHookVO;
 import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.mapper.QwSopLogsMapper;
+import com.fs.voice.utils.StringUtil;
 import com.fs.wxwork.dto.*;
 import com.fs.wxwork.service.WxWorkService;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -49,6 +51,10 @@ import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
 import java.lang.reflect.Field;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
@@ -76,12 +82,20 @@ public class AiHookServiceImpl implements AiHookService {
     QwApiService qwApiService;
     @Autowired
     QwCompanyMapper qwCompanyMapper;
+
+    @Autowired
+    private IQwExternalErrRetryService errRetryService;
+    @Autowired
+    QwAutoTagsMapper qwAutoTagsMapper;
     @Autowired
     FsCourseWatchLogMapper fsCourseWatchLogMapper;
     @Autowired
     FsUserCourseVideoMapper fsUserCourseVideoMapper;
     @Autowired
     FsCourseWatchLogMapper   watchLogMapper;
+
+    @Autowired
+    QwAutoTagsLogsMapper qwAutoTagsLogsMapper;
     @Autowired
     IQwJsApiService qwGetJsapiTicketService;
     @Autowired
@@ -391,6 +405,13 @@ public class AiHookServiceImpl implements AiHookService {
             }
             //存聊天记录
             addSaveAiMsg(2,2,contentKh,user,fastGptChatSession.getSessionId(),role.getRoleId(),qwExternalContacts,fastGptChatSession.getUserId(),result.getUsage().prompt_tokens,result.getUsage().completion_tokens,token);
+            Integer replyOne = redisCache.getCacheObject("reply:" + fastGptChatSession.getSessionId());
+            if (replyOne == 1){
+                QwUser qwUser = qwUserService.selectQwUserById(fastGptChatSession.getQwUserId());
+                if (ObjectUtils.isNotEmpty(qwUser)&&ObjectUtils.isNotEmpty(qwUser.getIpadStatus())&&qwUser.getIpadStatus() == 1){
+                    addQwAutoTags(qwUser,qwExternalContacts.getUserId(),qwExternalContacts.getExternalUserId());
+                }
+            }
             if (!content.isEmpty()){
                 addSaveAiMsg(1,2,content,user,fastGptChatSession.getSessionId(),role.getRoleId(),qwExternalContacts,fastGptChatSession.getUserId(),null,null,null);
                 //从fastgpt_chat_artificial_words表中查询所有转人工文本
@@ -461,6 +482,112 @@ public class AiHookServiceImpl implements AiHookService {
         return R.ok();
     }
 
+    /**
+     * 增加标签
+     *
+     * @param qwUser
+     * @param userID
+     * @param externalUserID
+     */
+    private void addQwAutoTags(QwUser qwUser, String userID, String externalUserID) {
+        QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalByExternalIdAndCompanyIdToIdAndFs(
+                externalUserID, userID, qwUser.getCorpId());
+
+        QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
+        qwEditUserTagParam.setUserid(userID);
+        qwEditUserTagParam.setExternal_userid(externalUserID);
+
+        QwAutoTags qwAutoTags = qwAutoTagsMapper.selectQwAutoReplyTagsByIdJSON(qwUser.getCorpId(), qwUser.getId());
+        if (qwAutoTags == null) {
+            return;
+        }
+
+        Set<String> combinedTagsSet = new HashSet<>();
+        Set<String> timeSlotMatchedTags = new HashSet<>();
+        String tagRemark = null;
+        boolean isMatch = false;
+
+        QwAutoTagsLogs qwAutoTagsLogs = new QwAutoTagsLogs();
+        qwAutoTagsLogs.setAutoTagId(qwAutoTags.getId());
+        qwAutoTagsLogs.setType(3L);
+        qwAutoTagsLogs.setQwUserid(qwUser.getId());
+        qwAutoTagsLogs.setExternalUserId(externalUserID);
+
+        List<QwAutoTagsRulesTags> rulesList = JSON.parseArray(qwAutoTags.getRulesTags(), QwAutoTagsRulesTags.class);
+
+        LocalDate todayDate = LocalDate.now();
+        DayOfWeek today = todayDate.getDayOfWeek();
+        int todayIndex = today.getValue();
+        LocalTime now = LocalTime.now();
+
+        for (QwAutoTagsRulesTags rule : rulesList) {
+            if (!rule.getWeek().contains(todayIndex)) {
+                continue;
+            }
+            LocalTime start = LocalTime.parse(rule.getStartTime());
+            LocalTime end = LocalTime.parse("24:00".equals(rule.getEndTime()) ? "23:59:59" : rule.getEndTime());
+
+            if (now.isBefore(start) || now.isAfter(end)) {
+                continue;
+            }
+
+            combinedTagsSet.addAll(rule.getTags());
+            timeSlotMatchedTags.addAll(rule.getTags());
+            isMatch = true;
+
+            if (rule.getIsDay() != null && rule.getIsDay() == 1) {
+                tagRemark = todayDate.format(DateTimeFormatter.ofPattern("yyMMdd")).substring(1);
+            }
+
+            if (!StringUtil.strIsNullOrEmpty(rule.getRemarks())) {
+                tagRemark = (tagRemark == null ? "" : tagRemark + "-") + rule.getRemarks();
+            }
+        }
+
+        if (!isMatch) {
+            return;
+        }
+
+        List<String> tagsToAdd = new ArrayList<>(combinedTagsSet);
+        qwEditUserTagParam.setAdd_tag(tagsToAdd);
+
+        qwAutoTagsLogs.setEffectiveRules(JSON.toJSONString(new ArrayList<>(timeSlotMatchedTags)));
+        qwAutoTagsLogs.setAddTime(new Date());
+        qwAutoTagsLogs.setCompanyId(qwUser.getCompanyId());
+        qwAutoTagsLogs.setCorpId(qwUser.getCorpId());
+
+        QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, qwUser.getCorpId());
+        log.info("自动添加标签的状态: {}|{}|qwEditUserTagParam--{}",
+                qwResult.getErrcode(), qwResult.getErrmsg(), qwEditUserTagParam);
+
+        if (qwResult.getErrcode() == 0) {
+            qwExternalContact.setTagIds(JSON.toJSONString(tagsToAdd));
+            qwAutoTagsLogsMapper.insertOrUpdateQwAutoTagsLogs(qwAutoTagsLogs);
+        } else if (qwResult.getErrcode() == 45035) {
+            insertQwExternalErrRetryTool(
+                    qwUser.getCorpId(),
+                    JSON.toJSONString(qwEditUserTagParam),
+                    1,
+                    qwResult.getErrmsg());
+            log.info("自动添加标签添加失败-已加入补偿机制中: {}", qwEditUserTagParam);
+        }
+    }
+
+    public void  insertQwExternalErrRetryTool(String corpId,String params,Integer retryState,String msg) {
+
+        QwExternalErrRetry errRetry=new QwExternalErrRetry();
+        errRetry.setTakeState(1);
+        errRetry.setCorpId(corpId);
+        errRetry.setRetryState(retryState);
+        errRetry.setRetryType(2);
+        errRetry.setRetryParams(JSON.toJSONString(params));
+        errRetry.setRetryNum(0);
+        errRetry.setCreateTime(new Date());
+        errRetry.setMark(msg);
+
+        errRetryService.insertQwExternalErrRetry(errRetry);
+
+    }
     private void sendAiVoiceMsg(String content, Long sendId , String uuid,Long serverId,QwUser user) {
         if (content == null || content.trim().isEmpty()){
             System.out.println("输出为空格");

+ 6 - 1
fs-service/src/main/java/com/fs/his/config/FsSysConfig.java

@@ -28,7 +28,12 @@ public class FsSysConfig {
     String erpWdShopCode;
     String erpWdBaseUrl;
     String erpWarehouseCode;
-
+    //erp 瀚智OMS
+    String erpHzOMSAppKey;
+    String erpHzOMSAppsecret;
+    String erpHzOMSItenantid;
+    String erpHzOMTokenUrl;
+    String erpHzOMBaseUrl;
     //支付接口
     Integer payOpen;//是否开启
     String payPartnerId;

+ 1 - 0
fs-service/src/main/java/com/fs/his/domain/FsStoreOrder.java

@@ -264,5 +264,6 @@ public class FsStoreOrder extends BaseEntity
     private Long customerId;
     private Integer source;
     private BigDecimal billPrice;
+    private String erpPhone;
 
 }

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

@@ -74,63 +74,63 @@ public interface FsStoreOrderMapper
     public int deleteFsStoreOrderByOrderIds(String[] orderIds);
     @Select("select order_id from fs_store_order where status = 3 and delivery_status = 0  and DATE(create_time)<='2024-3-1' ")
     List<Long> selectSyncExpressIds();
-    @Select({"<script> " +
-            "select so.*,st.store_name,us.nick_name,us.phone,d.doctor_name,p.patient_name,p.prescribe_code,c.company_name,cu.nick_name company_user_name " +
-            " FROM fs_store_order so LEFT JOIN fs_store st ON so.store_id =st.store_id " +
-            " LEFT JOIN fs_user us ON us.user_id=so.user_id " +
-            " LEFT JOIN fs_prescribe p ON p.prescribe_id =so.prescribe_id " +
-            " LEFT JOIN fs_doctor d ON so.doctor_id= d.doctor_id  " +
-            " LEFT JOIN company c on c.company_id =so.company_id " +
-            " LEFT JOIN company_user cu on cu.user_id=so.company_user_id  " +
-            " WHERE 1=1 "+
-            "<if test=\"maps.packageSecondName != null and maps.packageSecondName != '' \"> and so.package_second_name like concat('%', #{maps.packageSecondName}, '%')</if>"+
-            "<if test=\"maps.storeId != null \"> and so.store_id = #{maps.storeId}</if>\n" +
-            "<if test=\"maps.orderCode != null  and maps.orderCode != ''\"> and so.order_code = #{maps.orderCode}</if>\n" +
-            "<if test=\"maps.prescribeCode != null  and maps.prescribeCode != ''\"> and p.prescribe_code = #{maps.prescribeCode}</if>\n" +
-            "<if test=\"maps.userName != null  and maps.userName != ''\"> and so.user_name like concat('%', #{maps.userName}, '%')</if>\n" +
-            "<if test=\"maps.userPhone != null  and maps.userPhone != ''\"> and so.user_phone = #{maps.userPhone}</if>\n" +
-            "<if test=\"maps.userId != null  \"> and so.user_id = #{maps.userId}</if>\n" +
-            "<if test=\"maps.isFirst != null  \"> and so.is_first = #{maps.isFirst}</if>\n" +
-            "<if test=\"maps.status != null \"> and so.status = #{maps.status}</if>\n" +
-            "<if test=\"maps.source != null \"> and so.source = #{maps.source}</if>\n" +
-            "<if test=\"maps.deliverySn != null  and maps.deliverySn != ''\"> and so.delivery_sn = #{maps.deliverySn}</if>\n" +
-            "<if test=\"maps.prescribeId != null \"> and so.prescribe_id = #{maps.prescribeId}</if>\n" +
-            "<if test=\"maps.companyUserId != null \"> and so.company_user_id = #{maps.companyUserId}</if>\n" +
-            "<if test=\"maps.sTime != null \">  and DATE(so.create_time) &gt;= DATE(#{maps.sTime})</if>\n" +
-            "<if test=\"maps.eTime != null \">  and DATE(so.create_time) &lt;= DATE(#{maps.eTime})</if>\n" +
-            "<if test=\"maps.paysTime != null \">  and DATE(so.pay_time) &gt;= DATE(#{maps.paysTime})</if>\n" +
-            "<if test=\"maps.payeTime != null \">  and DATE(so.pay_time) &lt;= DATE(#{maps.payeTime})</if>\n" +
-            "<if test=\"maps.orderCreateType != null \"> and so.order_create_type = #{maps.orderCreateType}</if>"+
-            "<if test=\"maps.patientName != null \"> and p.patient_name like concat('%', #{maps.patientName}, '%')</if>"+
-            "<if test=\"maps.doctorName != null \"> and d.doctor_name like concat('%', #{maps.doctorName}, '%')</if>"+
-            "<if test=\"maps.orderType != null \"> and so.order_type = #{maps.orderType}</if>" +
-            "<if test=\"maps.deliverySendsTime != null \">  and DATE(so.delivery_send_time) &gt;= DATE(#{maps.deliverySendsTime})</if>\n" +
-            "<if test=\"maps.deliverySendeTime != null \">  and DATE(so.delivery_send_time) &lt;= DATE(#{maps.deliverySendeTime})</if>\n" +
-            "<if test=\"maps.deliveryImportsTime != null \">  and DATE(so.delivery_import_time) &gt;= DATE(#{maps.deliveryImportsTime})</if>\n" +
-            "<if test=\"maps.deliveryImporteTime != null \">  and DATE(so.delivery_import_time) &lt;= DATE(#{maps.deliveryImporteTime})</if>\n" +
-            "<if test=\"maps.tuisTime != null \">  and DATE(so.tui_money_time) &gt;= DATE(#{maps.tuisTime})</if>\n" +
-            "<if test=\"maps.tuieTime != null \">  and DATE(so.tui_money_time) &lt;= DATE(#{maps.tuieTime})</if>\n" +
-            "<if test=\"maps.companyUserNickName != null and  maps.companyUserNickName !='' \"> and cu.nick_name like concat( #{maps.companyUserNickName}, '%')</if> \n" +
-            "<if test = 'maps.companyId != null  and   maps.companyId != \"-1\" '> " + "and so.company_id =#{maps.companyId} " + "</if>" +
-            "<if test = ' maps.companyId == \"-1\" '> and so.company_id is null </if>" +
-            "<if test = 'maps.deliveryStatus != null    '> and so.delivery_status =#{maps.deliveryStatus} </if>" +
-            "<if test = 'maps.customerId != null    '> and so.customer_id =#{maps.customerId} </if>" +
-            "<if test = 'maps.deliveryPayStatus != null    '> and so.delivery_pay_status =#{maps.deliveryPayStatus} </if>" +
-            "<if test = 'maps.tuiMoneyStatus != null    '> and so.tui_money_status =#{maps.tuiMoneyStatus} </if>" +
-            "<if test = 'maps.deptId != null    '>   AND (so.dept_id = #{maps.deptId} OR so.dept_id IN ( SELECT t.dept_id FROM company_dept t WHERE find_in_set(#{maps.deptId}, ancestors) )) </if>" +
-            "<if test = 'maps.packageName != null and maps.packageName != \"\" '> and so.package_name like concat('%', #{maps.packageName}, '%')</if> " +
-            "<if test = 'maps.payType != null '> and so.pay_type IN <foreach collection=\"maps.payType.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
-            "<if test = 'maps.scheduleId != null  and  maps.scheduleId != \"-1\"   '> and so.schedule_id IN <foreach collection=\"maps.scheduleId.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
-            "<if test = 'maps.scheduleId == \"-1\"   '> and so.schedule_id is null </if>" +
-            "<if test = 'maps.orderBuyType != null  and  maps.orderBuyType != \"-1\"   '> and so.order_buy_type IN <foreach collection=\"maps.orderBuyType.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
-            "<if test = 'maps.orderBuyType == \"-1\"   '> and so.order_buy_type is null </if>" +
-            "<if test = 'maps.orderChannel == \"-1\"   '> and so.order_channel is null </if>" +
-            "<if test = 'maps.orderChannel != null  and  maps.orderChannel != \"-1\"   '> and so.order_channel IN <foreach collection=\"maps.orderChannel.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
-            "<if test = 'maps.qwSubject == \"-1\"'>and so.qw_subject is null </if>" +
-            "<if test = 'maps.qwSubject != null  and  maps.qwSubject != \"-1\" '> and so.qw_subject IN <foreach collection=\"maps.qwSubject.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
-            " ${maps.params.dataScope} "+
-            " ORDER BY so.order_id desc "+
-            "</script>"})
+//    @Select({"<script> " +
+//            "select so.*,st.store_name,us.nick_name,us.phone,d.doctor_name,p.patient_name,p.prescribe_code,c.company_name,cu.nick_name company_user_name " +
+//            " FROM fs_store_order so LEFT JOIN fs_store st ON so.store_id =st.store_id " +
+//            " LEFT JOIN fs_user us ON us.user_id=so.user_id " +
+//            " LEFT JOIN fs_prescribe p ON p.prescribe_id =so.prescribe_id " +
+//            " LEFT JOIN fs_doctor d ON so.doctor_id= d.doctor_id  " +
+//            " LEFT JOIN company c on c.company_id =so.company_id " +
+//            " LEFT JOIN company_user cu on cu.user_id=so.company_user_id  " +
+//            " WHERE 1=1 "+
+//            "<if test=\"maps.packageSecondName != null and maps.packageSecondName != '' \"> and so.package_second_name like concat('%', #{maps.packageSecondName}, '%')</if>"+
+//            "<if test=\"maps.storeId != null \"> and so.store_id = #{maps.storeId}</if>\n" +
+//            "<if test=\"maps.orderCode != null  and maps.orderCode != ''\"> and so.order_code = #{maps.orderCode}</if>\n" +
+//            "<if test=\"maps.prescribeCode != null  and maps.prescribeCode != ''\"> and p.prescribe_code = #{maps.prescribeCode}</if>\n" +
+//            "<if test=\"maps.userName != null  and maps.userName != ''\"> and so.user_name like concat('%', #{maps.userName}, '%')</if>\n" +
+//            "<if test=\"maps.userPhone != null  and maps.userPhone != ''\"> and so.user_phone = #{maps.userPhone}</if>\n" +
+//            "<if test=\"maps.userId != null  \"> and so.user_id = #{maps.userId}</if>\n" +
+//            "<if test=\"maps.isFirst != null  \"> and so.is_first = #{maps.isFirst}</if>\n" +
+//            "<if test=\"maps.status != null \"> and so.status = #{maps.status}</if>\n" +
+//            "<if test=\"maps.source != null \"> and so.source = #{maps.source}</if>\n" +
+//            "<if test=\"maps.deliverySn != null  and maps.deliverySn != ''\"> and so.delivery_sn = #{maps.deliverySn}</if>\n" +
+//            "<if test=\"maps.prescribeId != null \"> and so.prescribe_id = #{maps.prescribeId}</if>\n" +
+//            "<if test=\"maps.companyUserId != null \"> and so.company_user_id = #{maps.companyUserId}</if>\n" +
+//            "<if test=\"maps.sTime != null \">  and DATE(so.create_time) &gt;= DATE(#{maps.sTime})</if>\n" +
+//            "<if test=\"maps.eTime != null \">  and DATE(so.create_time) &lt;= DATE(#{maps.eTime})</if>\n" +
+//            "<if test=\"maps.paysTime != null \">  and DATE(so.pay_time) &gt;= DATE(#{maps.paysTime})</if>\n" +
+//            "<if test=\"maps.payeTime != null \">  and DATE(so.pay_time) &lt;= DATE(#{maps.payeTime})</if>\n" +
+//            "<if test=\"maps.orderCreateType != null \"> and so.order_create_type = #{maps.orderCreateType}</if>"+
+//            "<if test=\"maps.patientName != null \"> and p.patient_name like concat('%', #{maps.patientName}, '%')</if>"+
+//            "<if test=\"maps.doctorName != null \"> and d.doctor_name like concat('%', #{maps.doctorName}, '%')</if>"+
+//            "<if test=\"maps.orderType != null \"> and so.order_type = #{maps.orderType}</if>" +
+//            "<if test=\"maps.deliverySendsTime != null \">  and DATE(so.delivery_send_time) &gt;= DATE(#{maps.deliverySendsTime})</if>\n" +
+//            "<if test=\"maps.deliverySendeTime != null \">  and DATE(so.delivery_send_time) &lt;= DATE(#{maps.deliverySendeTime})</if>\n" +
+//            "<if test=\"maps.deliveryImportsTime != null \">  and DATE(so.delivery_import_time) &gt;= DATE(#{maps.deliveryImportsTime})</if>\n" +
+//            "<if test=\"maps.deliveryImporteTime != null \">  and DATE(so.delivery_import_time) &lt;= DATE(#{maps.deliveryImporteTime})</if>\n" +
+//            "<if test=\"maps.tuisTime != null \">  and DATE(so.tui_money_time) &gt;= DATE(#{maps.tuisTime})</if>\n" +
+//            "<if test=\"maps.tuieTime != null \">  and DATE(so.tui_money_time) &lt;= DATE(#{maps.tuieTime})</if>\n" +
+//            "<if test=\"maps.companyUserNickName != null and  maps.companyUserNickName !='' \"> and cu.nick_name like concat( #{maps.companyUserNickName}, '%')</if> \n" +
+//            "<if test = 'maps.companyId != null  and   maps.companyId != \"-1\" '> " + "and so.company_id =#{maps.companyId} " + "</if>" +
+//            "<if test = ' maps.companyId == \"-1\" '> and so.company_id is null </if>" +
+//            "<if test = 'maps.deliveryStatus != null    '> and so.delivery_status =#{maps.deliveryStatus} </if>" +
+//            "<if test = 'maps.customerId != null    '> and so.customer_id =#{maps.customerId} </if>" +
+//            "<if test = 'maps.deliveryPayStatus != null    '> and so.delivery_pay_status =#{maps.deliveryPayStatus} </if>" +
+//            "<if test = 'maps.tuiMoneyStatus != null    '> and so.tui_money_status =#{maps.tuiMoneyStatus} </if>" +
+//            "<if test = 'maps.deptId != null    '>   AND (so.dept_id = #{maps.deptId} OR so.dept_id IN ( SELECT t.dept_id FROM company_dept t WHERE find_in_set(#{maps.deptId}, ancestors) )) </if>" +
+//            "<if test = 'maps.packageName != null and maps.packageName != \"\" '> and so.package_name like concat('%', #{maps.packageName}, '%')</if> " +
+//            "<if test = 'maps.payType != null '> and so.pay_type IN <foreach collection=\"maps.payType.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
+//            "<if test = 'maps.scheduleId != null  and  maps.scheduleId != \"-1\"   '> and so.schedule_id IN <foreach collection=\"maps.scheduleId.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
+//            "<if test = 'maps.scheduleId == \"-1\"   '> and so.schedule_id is null </if>" +
+//            "<if test = 'maps.orderBuyType != null  and  maps.orderBuyType != \"-1\"   '> and so.order_buy_type IN <foreach collection=\"maps.orderBuyType.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
+//            "<if test = 'maps.orderBuyType == \"-1\"   '> and so.order_buy_type is null </if>" +
+//            "<if test = 'maps.orderChannel == \"-1\"   '> and so.order_channel is null </if>" +
+//            "<if test = 'maps.orderChannel != null  and  maps.orderChannel != \"-1\"   '> and so.order_channel IN <foreach collection=\"maps.orderChannel.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
+//            "<if test = 'maps.qwSubject == \"-1\"'>and so.qw_subject is null </if>" +
+//            "<if test = 'maps.qwSubject != null  and  maps.qwSubject != \"-1\" '> and so.qw_subject IN <foreach collection=\"maps.qwSubject.split(',')\"  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach></if>" +
+//            " ${maps.params.dataScope} "+
+//            " ORDER BY so.order_id desc "+
+//            "</script>"})
     List<FsStoreOrderListVO> selectFsStoreOrderListVO(@Param("maps")FsStoreOrderParam fsStoreOrder);
 
     @Select({"<script> " +
@@ -1085,4 +1085,11 @@ public interface FsStoreOrderMapper
     @Select("select * from fs_store_order where  `status`=2 ")
     List<FsStoreOrder> selectWdtOmsOrderdeliveryOp();
 
+    int batchUpdateErpByOrderIds(@Param("maps") FsStoreOrderSetErpPhoneParam param);
+
+    @Select(" select * from fs_store_order where order_code=#{orderCode} and store_id=#{storeId}")
+    FsStoreOrder getOrderByOrderCodeAndStoreId(@Param("orderCode")String orderCode,@Param("storeId") Long storeId);
+
+    @Update("UPDATE fs_store_order SET delivery_code = #{order.deliveryCode} , delivery_name = #{order.deliveryName} , delivery_sn = #{order.deliverySn}   WHERE follow_doctor_id = #{order.orderId}")
+    int updateStoreOrderDeliveryInfo(@Param("order") FsStoreOrder order);
 }

+ 9 - 0
fs-service/src/main/java/com/fs/his/mapper/FsStoreProductAttrValueMapper.java

@@ -155,4 +155,13 @@ public interface FsStoreProductAttrValueMapper
     @Select("    select id,doctor_brokerage, product_id, sku, stock, sales, price, image, cost_price cost, bar_code, ot_price, weight, volume, brokerage, brokerage_two,brokerage_three,give_integral  from fs_store_product_attr_value\n" +
             "    where  product_id=#{productId}")
     List<FsStoreProductAttrValue> selectFsStoreProductAttrValueVOByProductId(Long productId);
+
+    @Select(" select * from fs_store_product_attr_value where id=#{id} and product_id=#{productId} ")
+    FsStoreProductAttrValue  selectFsStoreProductAttrValueByIdAndPruductId(@Param("id") Long id,@Param("productId") Long productId);
+
+    @Update(" update fs_store_product_attr_value set stock = #{stock} where id = #{id} ")
+    int updateStoreProductAttrValueStock(Long id,Integer stock);
+
+    @Select("select * from fs_store_product_attr_value where product_id = #{productId}")
+    List<FsStoreProductAttrValue> selectFsStoreProductAttrValueListByProductId(@Param("productId") Long productId);
 }

+ 29 - 17
fs-service/src/main/java/com/fs/his/mapper/FsStoreProductMapper.java

@@ -18,8 +18,7 @@ import java.util.List;
  * @author fs
  * @date 2023-06-08
  */
-public interface FsStoreProductMapper
-{
+public interface FsStoreProductMapper {
     /**
      * 查询商品
      *
@@ -58,9 +57,9 @@ public interface FsStoreProductMapper
             "<if test = 'maps.barCode != null    '> " +
             "and p.product_id in(SELECT product_id FROM fs_store_product_attr_value WHERE bar_code =#{maps.barCode}) " +
             "</if>" +
-            " order by p.product_id desc "+
+            " order by p.product_id desc " +
             "</script>"})
-    public List<FsStoreProductVO> selectFsStoreProductListVO(@Param("maps")FsStoreProduct fsStoreProduct);
+    public List<FsStoreProductVO> selectFsStoreProductListVO(@Param("maps") FsStoreProduct fsStoreProduct);
 
     @Select({"<script> " +
             "select p.*,pc.cate_name,st.store_name  from fs_store_product p left join fs_store_product_category pc on p.cate_id=pc.cate_id LEFT JOIN fs_store st ON st.store_id=p.store_id  " +
@@ -83,7 +82,7 @@ public interface FsStoreProductMapper
             "<if test = 'maps.barCode != null    '> " +
             "and p.product_id in(SELECT product_id FROM fs_store_product_attr_value WHERE bar_code =#{maps.barCode}) " +
             "</if>" +
-            " order by p.product_id desc "+
+            " order by p.product_id desc " +
             "</script>"})
     public List<FsStoreProductListSVO> selectFsStoreProductListSVO(@Param("maps") FsStoreProductListSParam fsStoreProduct);
 
@@ -124,12 +123,14 @@ public interface FsStoreProductMapper
 
     @Select("select * FROM fs_store_product_rule WHERE is_del=0")
     List<FsStoreProductRule> selectFsStoreProductRuleList();
+
     @Update("update fs_store_product_attr_value set stock=stock+#{num}, sales=sales-#{num}" +
             " where product_id=#{productId} and id=#{productAttrValueId}")
-    int incProductAttrStock(@Param("num")Long num,@Param("productId") Long productId,@Param("productAttrValueId") Long productAttrValueId);
+    int incProductAttrStock(@Param("num") Long num, @Param("productId") Long productId, @Param("productAttrValueId") Long productAttrValueId);
+
     @Update("update fs_store_product set stock=stock+#{num}, sales=sales-#{num}" +
             " where product_id=#{productId}")
-    int incStockDecSales( @Param("num")Long num, @Param("productId")Long productId);
+    int incStockDecSales(@Param("num") Long num, @Param("productId") Long productId);
 
 
     @Select({"<script> " +
@@ -153,17 +154,17 @@ public interface FsStoreProductMapper
             "<if test = 'maps.barCode != null    '> " +
             "and p.product_id in(SELECT product_id FROM fs_store_product_attr_value WHERE bar_code =#{maps.barCode}) " +
             "</if>" +
-            " order by p.product_id desc "+
+            " order by p.product_id desc " +
             "</script>"})
-    public List<Long> selectFsStoreProductIdList(@Param("maps")FsStoreProduct fsStoreProduct);
+    public List<Long> selectFsStoreProductIdList(@Param("maps") FsStoreProduct fsStoreProduct);
 
     @Update({"<script> " +
-            "UPDATE fs_store_product SET price=price*#{price} , ot_price=ot_price*#{price} WHERE product_id in"+
+            "UPDATE fs_store_product SET price=price*#{price} , ot_price=ot_price*#{price} WHERE product_id in" +
             "<foreach item=\"id\" collection=\"ids\" open=\"(\" separator=\",\" close=\")\">\n" +
             "            #{id}\n" +
-            "        </foreach>"+
+            "        </foreach>" +
             "</script>"})
-    int updateFsStoreProductPrice(@Param("ids")List<Long> ids,@Param("price") double price);
+    int updateFsStoreProductPrice(@Param("ids") List<Long> ids, @Param("price") double price);
 
     @Select({"<script> " +
             "select p.*,pc.cate_name,st.store_name  from fs_store_product p left join fs_store_product_category pc on p.cate_id=pc.cate_id LEFT JOIN fs_store st ON st.store_id=p.store_id  " +
@@ -186,9 +187,10 @@ public interface FsStoreProductMapper
             "<if test = 'maps.barCode != null    '> " +
             "and p.product_id in(SELECT product_id FROM fs_store_product_attr_value WHERE bar_code =#{maps.barCode}) " +
             "</if>" +
-            " order by p.product_id desc "+
+            " order by p.product_id desc " +
             "</script>"})
-    List<FsStoreProductExcelVO> selectFsStoreProductExcelVO(@Param("maps")FsStoreProduct fsStoreProduct);
+    List<FsStoreProductExcelVO> selectFsStoreProductExcelVO(@Param("maps") FsStoreProduct fsStoreProduct);
+
     @Select({"<script> " +
             "select v.*,p.product_name,p.product_type,p.is_drug,c.cate_name,store.store_name,p.prescribe_spec  from fs_store_product_attr_value v inner join fs_store_product p on p.product_id=v.product_id left join fs_store_product_category c on c.cate_id=p.cate_id LEFT JOIN fs_store store ON store.store_id=p.store_id      " +
             "where 1=1 and v.bar_code is not null " +
@@ -204,9 +206,9 @@ public interface FsStoreProductMapper
             "<if test = 'maps.cateId != null    '> " +
             "and (p.cate_id =#{maps.cateId} or c.pid=#{maps.cateId} )" +
             "</if>" +
-            " order by v.id desc "+
+            " order by v.id desc " +
             "</script>"})
-    List<FsStoreProductAttrValueVO> selectFsStoreProductAttrValueListVO(@Param("maps")FsProductAttrValueParam param);
+    List<FsStoreProductAttrValueVO> selectFsStoreProductAttrValueListVO(@Param("maps") FsProductAttrValueParam param);
 
     @Select("SELECT * FROM fs_store_product\n" +
             "WHERE images LIKE '%https://htj-1258038825.cos.ap-beijing.myqcloud.com%'; ")
@@ -218,6 +220,7 @@ public interface FsStoreProductMapper
 
     @Select("select * FROM fs_store_product where bar_code =#{barCode} and (store_id = 30 or store_id = 33)")
     List<FsStoreProduct> selectCode(String barCode);
+
     @Select("select v.*,p.product_name,p.product_type,p.is_drug,c.cate_name,store.store_name,p.prescribe_spec  from fs_store_product_attr_value v inner join fs_store_product p on p.product_id=v.product_id left join fs_store_product_category c on c.cate_id=p.cate_id LEFT JOIN fs_store store ON store.store_id=p.store_id\n" +
             "where 1=1 and v.product_id=#{productId} limit 1")
     FsStoreProductAttrValueVO selectFsStoreProductAttrValueVOByProdId(Long productId);
@@ -227,6 +230,7 @@ public interface FsStoreProductMapper
     FsStoreProductAttrVO selectFsStoreProductAttrVOByProdId(Long productId);
 
     FsStoreProduct selectFsStoreProductById(Long productId);
+
     @Select({"<script> " +
             "select count(1) from fs_store_product  " +
             "where 1=1 " +
@@ -237,7 +241,7 @@ public interface FsStoreProductMapper
             "and find_in_set(#{companyIds}, company_ids) " +
             "</if>" +
             "</script>"})
-    Long selectFsStoreProductCompanyCount(@Param("type") int type,@Param("companyIds") Long companyIds);
+    Long selectFsStoreProductCompanyCount(@Param("type") int type, @Param("companyIds") Long companyIds);
 
     @Select({"<script> " +
             "select count(1) from fs_store_product  " +
@@ -247,4 +251,12 @@ public interface FsStoreProductMapper
             "</if>" +
             "</script>"})
     Long selectFsStoreProductCount(int type);
+
+    @Select(" select * from fs_store_product where product_id = #{productId} and store_id = #{storeId}")
+    FsStoreProduct getStoreProductByProductIdAndStoreId(@Param("productId") Long productId, @Param("storeId") Long storeId);
+
+    @Update({"UPDATE fs_store_product SET stock=#{stock}  WHERE product_id = #{productId} "})
+    int updateStoreProductStock(@Param("productId") Long productId, @Param("stock") Integer stock);
+
+
 }

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

@@ -104,7 +104,7 @@ public interface FsUserMapper
     @Select("select f1.*,f2.nick_name tui_name,f2.phone tui_phone FROM fs_user f1 LEFT JOIN fs_user f2 ON f1.tui_user_id =f2.user_id where f1.user_id=#{userId} ")
     FsUserVO selectFsUserVoByUserId(Long userId);
 
-    @Select("select * from fs_user where ma_open_id=#{openId}")
+    @Select("select * from fs_user where mp_open_id=#{openId}")
     FsUser selectFsUserByOpenId(String openId);
     @Select("select * from fs_user where phone=#{phone}")
     FsUser selectFsUserByPhone(String phone);

+ 11 - 0
fs-service/src/main/java/com/fs/his/param/FsStoreOrderSetErpPhoneParam.java

@@ -0,0 +1,11 @@
+package com.fs.his.param;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FsStoreOrderSetErpPhoneParam {
+    private String erpPhone;
+    private List<Long> orderIds;
+}

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

@@ -0,0 +1,16 @@
+package com.fs.his.param;
+
+import lombok.Data;
+
+@Data
+public class HzOMSErpApiParam {
+
+    //瀚智分配的appkey
+    private String appkey;
+
+    private String timestamp;
+
+    private String datapakege;
+
+    private String sign;
+}

+ 37 - 0
fs-service/src/main/java/com/fs/his/service/ErpApiService.java

@@ -0,0 +1,37 @@
+package com.fs.his.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fs.his.param.HzOMSErpApiParam;
+import com.fs.his.vo.HzOMSErpResponseVO;
+import org.apache.poi.ss.formula.functions.T;
+
+public interface ErpApiService {
+
+    /**
+     * hzoms 同步商品库存
+     * @param param
+     * @return
+     */
+    HzOMSErpResponseVO stockSync(HzOMSErpApiParam param);
+
+    /**
+     * hzoms 用于将瀚智商品库存同步到第三方
+     * @param param
+     * @return
+     */
+    HzOMSErpResponseVO  skuStockSync(HzOMSErpApiParam param);
+
+    /**
+     * hzoms 平台订单物流信息回传
+     * @param param
+     * @return
+     */
+    HzOMSErpResponseVO  logisticsAdd(HzOMSErpApiParam param);
+
+    /**
+     * hzoms 解码参数
+     * @param s
+     * @return
+     */
+    JSONObject getDecodeDataJSON(String s);
+}

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

@@ -211,4 +211,10 @@ public interface IFsStoreOrderService
     ErpOrder getErpOrder(FsStoreOrder order) throws ParseException;
 
     Integer selectFsStoreOrderCountByType(Long companyId, long l, int i);
+
+    int batchUpdateErpByOrderIds(FsStoreOrderSetErpPhoneParam param);
+
+    FsStoreOrder getOrderByOrderCodeAndStoreId(String orderCode, Long storeId);
+
+    int updateStoreOrderDeliveryInfo(FsStoreOrder order);
 }

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

@@ -62,4 +62,10 @@ public interface IFsStoreProductAttrValueService
     public int deleteFsStoreProductAttrValueById(Long id);
 
     List<FsStoreProductAttrValueListDVO> selectFsStoreProductAttrValueListDVO(FsStoreProductAttrValueListDParam param);
+
+    FsStoreProductAttrValue selectFsStoreProductAttrValueByIdAndPruductId(Long id, Long productId);
+
+    int updateStoreProductAttrValueStock(Long id , Integer stock);
+
+    List<FsStoreProductAttrValue> selectFsStoreProductAttrValueListByProductId( Long productId);
 }

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

@@ -94,4 +94,7 @@ public interface IFsStoreProductService
     Long selectFsStoreProductCount(int type, Long companyId);
     Long selectFsStoreProductCount(int type);
 
+    FsStoreProduct getStoreProductByProductIdAndStoreId(Long productId, Long storeId);
+
+    int updateStoreProductStock(Long productId, Integer num);
 }

+ 131 - 110
fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java

@@ -22,6 +22,7 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.impl.CompanyServiceImpl;
 import com.fs.core.config.WxPayProperties;
 import com.fs.core.utils.OrderCodeUtils;
+import com.fs.his.config.FsSysConfig;
 import com.fs.his.config.StoreConfig;
 import com.fs.erp.dto.BaseResponse;
 import com.fs.erp.dto.ErpRefundUpdateRequest;
@@ -35,6 +36,7 @@ import com.fs.his.enums.FsStoreOrderStatusEnum;
 import com.fs.his.mapper.*;
 import com.fs.his.param.*;
 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.V2TradePaymentScanpayRefundRequest;
@@ -62,6 +64,7 @@ import com.google.gson.Gson;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -93,6 +96,10 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
     private TzBankService tzBankService;
     @Autowired
     private IErpOrderService erpOrderService;
+    @Autowired
+    @Qualifier("hzOMSErpOrderServiceImpl")
+    private IErpOrderService hzOMSerpOrderService;
+
     @Autowired
     private FsStorePaymentMapper fsStorePaymentMapper;
     @Autowired
@@ -128,7 +135,8 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
     FsExportTaskMapper fsExportTaskMapper;
     @Autowired
     IFsStoreOrderBillLogService fsStoreOrderBillLogService;
-
+    @Autowired
+    private ConfigUtil configUtil;
 
     /**
      * 查询售后记录
@@ -222,29 +230,29 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
     @Override
     public int auditing(FsStoreAfterSales fsStoreAfterSales) {
         FsStoreAfterSales order = fsStoreAfterSalesMapper.selectFsStoreAfterSalesById(fsStoreAfterSales.getId());
-        if (order==null) {
+        if (order == null) {
 
-        throw new CustomException("订单不存在");
+            throw new CustomException("订单不存在");
         }
-        if (order.getSalesStatus()!=0){
+        if (order.getSalesStatus() != 0) {
             throw new CustomException("非法更改");
         }
-        Integer status=null;
+        Integer status = null;
         FsStoreAfterSales fs = new FsStoreAfterSales();
-        if (order.getOrderStatus()==2){
-            status=3;
-        }else if (order.getOrderStatus()==3){
+        if (order.getOrderStatus() == 2) {
+            status = 3;
+        } else if (order.getOrderStatus() == 3) {
             FsStore fsStore = fsStoreMapper.selectFsStoreByStoreId(order.getStoreId());
             fs.setPhoneNumber(fsStore.getRefundPhone());
             fs.setAddress(fsStore.getRefundAddress());
             fs.setConsignee(fsStore.getRefundConsignee());
-            status=2;
-        }else if (order.getOrderStatus()==4){
+            status = 2;
+        } else if (order.getOrderStatus() == 4) {
             FsStore fsStore = fsStoreMapper.selectFsStoreByStoreId(order.getStoreId());
             fs.setPhoneNumber(fsStore.getRefundPhone());
             fs.setAddress(fsStore.getRefundAddress());
             fs.setConsignee(fsStore.getRefundConsignee());
-            status=1;
+            status = 1;
         }
 
         fs.setId(fsStoreAfterSales.getId());
@@ -264,9 +272,13 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
     @Override
     public int noAuditing(FsStoreAfterSales fsStoreAfterSales) {
         FsStoreAfterSales order = fsStoreAfterSalesMapper.selectFsStoreAfterSalesById(fsStoreAfterSales.getId());
-        if (order==null)throw new CustomException("订单不存在");
-        if (order.getSalesStatus()!=0){throw new CustomException("非法更改");}
-        if (order.getStatus()==5){throw new CustomException("非法更改");}
+        if (order == null) throw new CustomException("订单不存在");
+        if (order.getSalesStatus() != 0) {
+            throw new CustomException("非法更改");
+        }
+        if (order.getStatus() == 5) {
+            throw new CustomException("非法更改");
+        }
         FsStoreAfterSales fs = new FsStoreAfterSales();
         fs.setId(fsStoreAfterSales.getId());
         fs.setSalesStatus(2);
@@ -276,7 +288,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         logs.setChangeTime(new DateTime());
         logs.setChangeType(FsStoreAfterSalesStatusEnum.STATUS_6.getValue());
         logs.setStoreAfterSalesId(fsStoreAfterSales.getId());
-        logs.setChangeMessage(FsStoreAfterSalesStatusEnum.STATUS_6.getDesc()+" "+fsStoreAfterSales.getRemark());
+        logs.setChangeMessage(FsStoreAfterSalesStatusEnum.STATUS_6.getDesc() + " " + fsStoreAfterSales.getRemark());
         logs.setOperator(fsStoreAfterSales.getOperator());
         fsStoreAfterSalesLogsMapper.insertFsStoreAfterSalesLogs(logs);
 //        FsStoreOrderLogs Logs = new FsStoreOrderLogs();
@@ -289,33 +301,33 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         or.setOrderId(order.getOrderId());
         or.setStatus(order.getOrderStatus());
         fsStoreOrderMapper.updateFsStoreOrder(or);
-        if (order.getOrderStatus()==2){
+        if (order.getOrderStatus() == 2) {
             FsStoreOrder fsStoreOrder = fsStoreOrderMapper.selectFsStoreOrderByOrderId(order.getOrderId());
-            String newOrderSn =  OrderCodeUtils.getOrderSn();
-            if(StringUtils.isEmpty(newOrderSn)){
-                newOrderSn =  OrderCodeUtils.getOrderSn();
+            String newOrderSn = OrderCodeUtils.getOrderSn();
+            if (StringUtils.isEmpty(newOrderSn)) {
+                newOrderSn = OrderCodeUtils.getOrderSn();
             }
             FsStoreOrder newOrder = new FsStoreOrder();
             newOrder.setOrderId(order.getOrderId());
             newOrder.setOrderCode(newOrderSn);
             fsStoreOrderMapper.updateFsStoreOrder(newOrder);
             or.setOrderCode(newOrderSn);
-            if (fsStoreOrder.getPackageOrderId()!=null){
+            if (fsStoreOrder.getPackageOrderId() != null) {
                 FsPackageOrder fsPackageOrder = new FsPackageOrder();
                 fsPackageOrder.setOrderId(fsStoreOrder.getPackageOrderId());
                 fsPackageOrder.setOrderSn(newOrderSn);
                 fsPackageOrderMapper.updateFsPackageOrder(fsPackageOrder);
             }
-            if (fsStoreOrder.getInquiryOrderId()!=null){
+            if (fsStoreOrder.getInquiryOrderId() != null) {
                 FsInquiryOrder fsInquiryOrder = new FsInquiryOrder();
                 fsInquiryOrder.setOrderSn(newOrderSn);
                 fsInquiryOrder.setOrderId(fsStoreOrder.getInquiryOrderId());
                 fsInquiryOrderMapper.updateFsInquiryOrder(fsInquiryOrder);
             }
 
-            List<FsStorePayment> payments = fsStorePaymentMapper.selectFsStorePaymentByPay(2,fsStoreOrder.getOrderId());
-            if (fsStoreOrder.getPackageOrderId()!=null){
-                payments = fsStorePaymentMapper.selectFsStorePaymentByPay(3,fsStoreOrder.getPackageOrderId());
+            List<FsStorePayment> payments = fsStorePaymentMapper.selectFsStorePaymentByPay(2, fsStoreOrder.getOrderId());
+            if (fsStoreOrder.getPackageOrderId() != null) {
+                payments = fsStorePaymentMapper.selectFsStorePaymentByPay(3, fsStoreOrder.getPackageOrderId());
             }
             for (FsStorePayment payment : payments) {
                 FsStorePayment fsStorePayment = new FsStorePayment();
@@ -333,10 +345,6 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         }
 
 
-
-
-
-
         return 1;
     }
 
@@ -345,8 +353,8 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
     public int refundMoney(FsStoreAfterSales fsStoreAfterSales) {
         FsStoreAfterSales order = fsStoreAfterSalesMapper.selectFsStoreAfterSalesById(fsStoreAfterSales.getId());
         FsStoreOrder fsStoreOrder = fsStoreOrderMapper.selectFsStoreOrderByOrderId(order.getOrderId());
-        if (order==null)throw new CustomException("订单不存在");
-        if (order.getStatus()!=3)throw new CustomException("非法更改");
+        if (order == null) throw new CustomException("订单不存在");
+        if (order.getStatus() != 3) throw new CustomException("非法更改");
         //更改售后订单状态
         FsStoreAfterSales fs = new FsStoreAfterSales();
         fs.setId(fsStoreAfterSales.getId());
@@ -373,20 +381,20 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         Logs.setChangeType(FsStoreOrderStatusEnum.REFUND_STATUS_2.getValue().toString());
         fsStoreOrderLogsMapper.insertFsStoreOrderLogs(Logs);
         BigDecimal reMoney = order.getRefundAmount();
-        String orderType="store";
+        String orderType = "store";
         // 开票冲红
         fsStoreOrderBillLogService.billBackByOrderId(fsStoreOrder.getOrderId());
 
 
-        if (fsStoreOrder.getPackageOrderId()!=null){
-            orderType="package";
+        if (fsStoreOrder.getPackageOrderId() != null) {
+            orderType = "package";
             FsPackageOrder fsPackageOrder = fsPackageOrderMapper.selectFsPackageOrderByOrderId(fsStoreOrder.getPackageOrderId());
-            if (fsPackageOrder!=null){
+            if (fsPackageOrder != null) {
                 fsPackageOrder.setStatus(-2);
                 fsPackageOrder.setRefundStatus(2);
                 fsPackageOrderMapper.updateFsPackageOrder(fsPackageOrder);
             }
-            if (fsPackageOrder.getInquiryOrderId()!=null){
+            if (fsPackageOrder.getInquiryOrderId() != null) {
                 FsInquiryOrder fsInquiryOrder = new FsInquiryOrder();
                 fsInquiryOrder.setOrderId(fsPackageOrder.getInquiryOrderId());
                 fsInquiryOrder.setStatus(-2);
@@ -404,7 +412,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
 //                productService.incProductStock(vo.getNum(), vo.getProductId(), vo.getProductAttrValueId());
 //            }
 //        }
-        if (order.getCompanyId()!=null){
+        if (order.getCompanyId() != null) {
             companyService.refundCompanyMoney(fsStoreOrder);
         }
 
@@ -414,15 +422,15 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
 //            fsUserService.subUserIntegral(fsStoreOrder.getUserId(),fsStoreOrder.getOrderId(),2);
 //        }
 
-        List<FsStorePayment> payments = fsStorePaymentMapper.selectFsStorePaymentByPay(2,fsStoreOrder.getOrderId());
-        if (fsStoreOrder.getPackageOrderId()!=null){
+        List<FsStorePayment> payments = fsStorePaymentMapper.selectFsStorePaymentByPay(2, fsStoreOrder.getOrderId());
+        if (fsStoreOrder.getPackageOrderId() != null) {
 
-            payments = fsStorePaymentMapper.selectFsStorePaymentByPay(3,fsStoreOrder.getPackageOrderId());
+            payments = fsStorePaymentMapper.selectFsStorePaymentByPay(3, fsStoreOrder.getPackageOrderId());
         }
-        if(payments!=null&&payments.size()>0){
-            FsStorePayment payment=payments.get(0);
-            String json=configService.selectConfigByKey("his.pay");
-            if(payment.getPayMode().equals("wx")){
+        if (payments != null && payments.size() > 0) {
+            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);
@@ -434,106 +442,111 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
                 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.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();
+                    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());
+                    } else {
+                        throw new CustomException("退款请求失败" + refundQueryResult.getReturnMsg());
                     }
                 } catch (WxPayException e) {
-                    throw new CustomException("退款请求失败"+e.getReturnMsg());
+                    throw new CustomException("退款请求失败" + e.getReturnMsg());
                 }
-            }
-            else if(payment.getPayMode().equals("yb")){
+            } else if (payment.getPayMode().equals("yb")) {
                 //易宝
-                RefundDTO refundDTO=new RefundDTO();
+                RefundDTO refundDTO = new RefundDTO();
                 refundDTO.setRefundMoney(payment.getPayMoney().toString());
-                refundDTO.setLowRefundNo(orderType+"-"+payment.getPayCode());
+                refundDTO.setLowRefundNo(orderType + "-" + payment.getPayCode());
                 refundDTO.setUpOrderId(payment.getTradeNo());
-                com.fs.ybPay.domain.RefundResult result=payService.refund(refundDTO);
-                logger.info("订单退款返回结果:退款订单id:"+order.getOrderId()+result);
-                if(result.getState().equals("5")){
-                    FsStorePayment paymentMap=new FsStorePayment();
+                com.fs.ybPay.domain.RefundResult result = payService.refund(refundDTO);
+                logger.info("订单退款返回结果:退款订单id:" + order.getOrderId() + 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 {
+                    throw new CustomException("退款请求失败" + result.getMessage());
                 }
-            } else if(payment.getPayMode().equals("tz")){
+            } else if (payment.getPayMode().equals("tz")) {
                 RefundParam tzBankResult = new RefundParam();
                 // 使用set方法为对象的字段赋值
                 //商户原支付订单号
-                tzBankResult.setOldPayOutOrderNo(orderType+payment.getPayCode());
+                tzBankResult.setOldPayOutOrderNo(orderType + payment.getPayCode());
                 // 商户退款订单号 对接平台系统里自己生成的退款订单号
-                tzBankResult.setRefundOrderNo(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:"+order.getOrderId()+result);
-                if(result.getBody().getRefundOrdStatus().equals("90")||result.getBody().getRefundOrdStatus().equals("60")){
-                    FsStorePayment paymentMap=new FsStorePayment();
+                logger.info("订单退款返回结果:退款订单id:" + order.getOrderId() + 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 {
+                    throw new CustomException("退款请求失败" + result.getRetMsg());
                 }
-            }
-            else if(payment.getPayMode().equals("hf")){
+            } else if (payment.getPayMode().equals("hf")) {
                 V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
                 request.setOrdAmt(payment.getPayMoney().toString());
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
-                request.setReqSeqId("refund-"+payment.getPayCode());
+                request.setReqSeqId("refund-" + payment.getPayCode());
                 Map<String, Object> extendInfoMap = new HashMap<>();
-                extendInfoMap.put("org_req_seq_id", orderType+"-"+payment.getPayCode());
+                extendInfoMap.put("org_req_seq_id", orderType + "-" + payment.getPayCode());
                 request.setExtendInfo(extendInfoMap);
                 HuiFuRefundResult refund = huiFuService.refund(request);
-                 logger.info("订单退款返回结果:退款订单id:"+order.getOrderId()+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();
+                logger.info("订单退款返回结果:退款订单id:" + order.getOrderId() + 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());
+                } else {
+                    throw new CustomException("退款请求失败" + refund.getResp_desc());
                 }
             }
             //管易作废
-            if(StringUtils.isNotEmpty(fsStoreOrder.getExtendOrderId())){
-                if (!fsStoreOrder.getExtendOrderId().equals("HIS")){
-                    ErpRefundUpdateRequest request=new ErpRefundUpdateRequest();
+            if (StringUtils.isNotEmpty(fsStoreOrder.getExtendOrderId())) {
+                if (!fsStoreOrder.getExtendOrderId().equals("HIS")) {
+                    ErpRefundUpdateRequest request = new ErpRefundUpdateRequest();
                     request.setTid(fsStoreOrder.getOrderCode());
                     request.setOid(fsStoreOrder.getOrderCode());
                     request.setRefund_state(1);
-                    erpOrderService.refundUpdate(request);
+                    request.setStoreAfterSalesId(fsStoreAfterSales.getId());
+                    FsSysConfig sysConfig = configUtil.getSysConfig();
+                    Integer erpType = sysConfig.getErpType();
+                    if (erpType == 1) {
+                        erpOrderService.refundUpdate(request);
+                    } else if (erpType == 3) {
+                        //瀚智
+                        hzOMSerpOrderService.refundUpdate(request);
+                    }
                 }
             }
-        }else {
-            if (order.getRefundAmount().compareTo(BigDecimal.ZERO) == 0){
+        } else {
+            if (order.getRefundAmount().compareTo(BigDecimal.ZERO) == 0) {
 
-            }else {
+            } else {
                 throw new CustomException("未找的支付明细");
             }
         }
@@ -577,14 +590,14 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         if (order.getStatus() == FsStoreOrderStatusEnum.STATUS_NE1.getValue()) {
             return R.error("已提交申请,等待处理");
         }
-        if (order.getPayType()==3&&order.getStatus()>=FsStoreOrderStatusEnum.STATUS_3.getValue() ){
-            return  R.error("货到付款已发货订单,不支持申请售后");
+        if (order.getPayType() == 3 && order.getStatus() >= FsStoreOrderStatusEnum.STATUS_3.getValue()) {
+            return R.error("货到付款已发货订单,不支持申请售后");
         }
 //        if(storeAfterSalesParam.getRefundAmount().compareTo(order.getPayPrice())==1){
 //            return R.error("退款金额不能大于支付金额");
 //        }
         //已完成订单七天后不能申请退款
-        if(order.getIsAfterSales()==0){
+        if (order.getIsAfterSales() == 0) {
             return R.error("此订单已超过售后时间,不能提交售后");
         }
 //        if (order.getStatus().equals(FsStoreOrderStatusEnum.STATUS_4.getValue())) {
@@ -670,13 +683,21 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         logs.setStoreAfterSalesId(storeAfterSales.getId());
         logs.setChangeMessage(FsStoreAfterSalesStatusEnum.STATUS_0.getDesc());
         fsStoreAfterSalesLogsMapper.insertFsStoreAfterSalesLogs(logs);
-        if(order.getExtendOrderId()!=null){
+        if (order.getExtendOrderId() != null) {
             ErpRefundUpdateRequest request = new ErpRefundUpdateRequest();
             request.setTid(order.getOrderCode());
             request.setOid(order.getOrderCode());
             request.setRefund_state(1);
-            BaseResponse response = erpOrderService.refundUpdate(request);
-
+            request.setStoreAfterSalesId(storeAfterSales.getId());
+            FsSysConfig sysConfig = configUtil.getSysConfig();
+            Integer erpType = sysConfig.getErpType();
+            BaseResponse response = null;
+            if (erpType == 1) {
+                response =  erpOrderService.refundUpdate(request);
+            } else if (erpType == 3) {
+                //瀚智
+                response =  hzOMSerpOrderService.refundUpdate(request);
+            }
             if (response.getSuccess()) {
                 return R.ok();
             } else {
@@ -725,32 +746,32 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
         if (storeAfterSales.getOrderStatus().equals(FsStoreOrderStatusEnum.STATUS_2.getValue())) {
             if (StringUtils.isNotEmpty(order.getExtendOrderId())) {
                 //更新订单code
-                String orderSn =  OrderCodeUtils.getOrderSn();
-                if(StringUtils.isEmpty(orderSn)){
+                String orderSn = OrderCodeUtils.getOrderSn();
+                if (StringUtils.isEmpty(orderSn)) {
                     return R.error("订单生成失败,请重试");
                 }
-                FsStoreOrder orderMap=new FsStoreOrder();
+                FsStoreOrder orderMap = new FsStoreOrder();
                 orderMap.setOrderId(order.getOrderId());
                 orderMap.setOrderCode(orderSn);
                 fsStoreOrderMapper.updateFsStoreOrder(orderMap);
                 //生成新的订单
 
-                if (order.getPackageOrderId()!=null){
+                if (order.getPackageOrderId() != null) {
                     FsPackageOrder fsPackageOrder = new FsPackageOrder();
                     fsPackageOrder.setOrderId(order.getPackageOrderId());
                     fsPackageOrder.setOrderSn(orderSn);
                     fsPackageOrderMapper.updateFsPackageOrder(fsPackageOrder);
                 }
-                if (order.getInquiryOrderId()!=null){
+                if (order.getInquiryOrderId() != null) {
                     FsInquiryOrder fsInquiryOrder = new FsInquiryOrder();
                     fsInquiryOrder.setOrderSn(orderSn);
                     fsInquiryOrder.setOrderId(order.getInquiryOrderId());
                     fsInquiryOrderMapper.updateFsInquiryOrder(fsInquiryOrder);
                 }
 
-                List<FsStorePayment> payments = fsStorePaymentMapper.selectFsStorePaymentByPay(2,order.getOrderId());
-                if (order.getPackageOrderId()!=null){
-                    payments = fsStorePaymentMapper.selectFsStorePaymentByPay(3,order.getPackageOrderId());
+                List<FsStorePayment> payments = fsStorePaymentMapper.selectFsStorePaymentByPay(2, order.getOrderId());
+                if (order.getPackageOrderId() != null) {
+                    payments = fsStorePaymentMapper.selectFsStorePaymentByPay(3, order.getPackageOrderId());
                 }
                 for (FsStorePayment payment : payments) {
                     FsStorePayment fsStorePayment = new FsStorePayment();
@@ -762,8 +783,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
 
                 try {
                     fsStoreOrderService.createOmsOrder(order.getOrderId());
-                }
-                catch (Exception e){
+                } catch (Exception e) {
                 }
             }
         }
@@ -803,9 +823,9 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
     @Override
     public int depotAuditing(FsStoreAfterSales fsStoreAfterSales) {
         FsStoreAfterSales order = fsStoreAfterSalesMapper.selectFsStoreAfterSalesById(fsStoreAfterSales.getId());
-        if (order==null)throw new CustomException("订单不存在");
-        if (order.getSalesStatus()!=0)throw new CustomException("非法更改");
-        if (order.getStatus()!=2){
+        if (order == null) throw new CustomException("订单不存在");
+        if (order.getSalesStatus() != 0) throw new CustomException("非法更改");
+        if (order.getStatus() != 2) {
             throw new CustomException("非法更改");
         }
         FsStoreAfterSales fs = new FsStoreAfterSales();
@@ -824,14 +844,14 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
 
     @Override
     public List<FsStoreAfterSalesListUVO> selectFsStoreAfterSalesListUVO(FsStoreAfterSalesListUParam param) {
-        List<FsStoreAfterSalesListUVO>  list=fsStoreAfterSalesMapper.selectFsStoreAfterSalesListUVO(param);
-        for(FsStoreAfterSalesListUVO vo:list){
-            FsStoreAfterSalesItem map=new FsStoreAfterSalesItem();
+        List<FsStoreAfterSalesListUVO> list = fsStoreAfterSalesMapper.selectFsStoreAfterSalesListUVO(param);
+        for (FsStoreAfterSalesListUVO vo : list) {
+            FsStoreAfterSalesItem map = new FsStoreAfterSalesItem();
             map.setAfterSalesId(vo.getId());
-            List<FsStoreAfterSalesItem>  items=fsStoreAfterSalesItemMapper.selectFsStoreAfterSalesItemList(map);
+            List<FsStoreAfterSalesItem> items = fsStoreAfterSalesItemMapper.selectFsStoreAfterSalesItemList(map);
             vo.setItems(items);
         }
-        return  list;
+        return list;
     }
 
     @Override
@@ -865,6 +885,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
     public Long selectFsStoreAfterSalesExcelListVOCount(FsStoreAfterSalesParam fsStoreAfterSales) {
         return fsStoreAfterSalesMapper.selectFsStoreAfterSalesExcelListVOCount(fsStoreAfterSales);
     }
+
     @Async
     @Override
     public void exportData(FsStoreAfterSalesParam fsStoreAfterSales) {
@@ -872,7 +893,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
 
         ExcelUtil<FsStoreAfterSalesExcelVO> util = new ExcelUtil<>(FsStoreAfterSalesExcelVO.class);
         AjaxResult result = util.exportExcel(list, "售后记录数据");
-        FsExportTask task=fsExportTaskMapper.selectFsExportTaskByTaskId(fsStoreAfterSales.getTaskId());
+        FsExportTask task = fsExportTaskMapper.selectFsExportTaskByTaskId(fsStoreAfterSales.getTaskId());
         task.setFinishTime(new Date());
         task.setStatus(1);
         task.setFileUrl(result.get("msg").toString());

+ 98 - 41
fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java

@@ -27,6 +27,7 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.vo.FsStoreOrderStatisticsVO;
 import com.fs.company.vo.FsStoreProductStatisticsVO;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.core.config.WxPayProperties;
 import com.fs.core.utils.OrderCodeUtils;
 import com.fs.erp.domain.*;
@@ -188,6 +189,11 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
     @Autowired
     @Qualifier("wdtErpOrderServiceImpl")
     private IErpOrderService wdtOrderService;
+
+    @Autowired
+    @Qualifier("hzOMSErpOrderServiceImpl")
+    private IErpOrderService hzOMSOrderService;
+
     @Autowired
     private IFsStoreAfterSalesService fsStoreAfterSalesService;
     @Autowired
@@ -240,6 +246,8 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
     private QwUserServiceImpl qwUserServiceImpl;
     @Autowired
     private IFsStoreProductGroupService storeProductGroupService;
+    @Autowired
+    CloudHostProper cloudHostProper;
 
     /**
      * 查询订单
@@ -1617,15 +1625,24 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
                 } else if (erpType == 2){
                     //旺店通
                     erpOrderService =  wdtOrderService;
+                } else if(erpType == 3){
+                    //瀚智
+                    erpOrderService = hzOMSOrderService;
                 }
-                //管易作废
                 if(erpOrderService!= null && StringUtils.isNotEmpty(order.getExtendOrderId())){
                     if (!order.getExtendOrderId().equals("HIS")){
-                        ErpRefundUpdateRequest request=new ErpRefundUpdateRequest();
-                        request.setTid(order.getOrderCode());
-                        request.setOid(order.getOrderCode());
-                        request.setRefund_state(1);
-                        erpOrderService.refundUpdate(request);
+                        if(erpType!=3){
+                            ErpRefundUpdateRequest request=new ErpRefundUpdateRequest();
+                            request.setTid(order.getOrderCode());
+                            request.setOid(order.getOrderCode());
+                            request.setRefund_state(1);
+                            erpOrderService.refundUpdate(request);
+                        }else{
+                            ErpRefundOrder param = new ErpRefundOrder();
+                            param.setOrderCode(order.getOrderCode());
+                            param.setAfterSalesId(fsStoreAfterSales.getId());
+                            erpOrderService.refundOrder(param);
+                        }
                     }
                 }
             }
@@ -1663,7 +1680,10 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
         } else if (erpType == 2){
             //旺店通
             erpOrderService =  wdtOrderService;
-        } else {
+        }  else if (erpType == 3){
+            //瀚智OMS
+            erpOrderService =  hzOMSOrderService;
+        }else {
             return;
         }
         FsStoreOrder order=fsStoreOrderMapper.selectFsStoreOrderByOrderId(orderId);
@@ -1703,34 +1723,35 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
 
 
         //物流代收都发 互联网医院顺丰
-        if (order.getPayRemain().compareTo(new BigDecimal(0)) == 0) {
-            //没有物流代收
-            //大于100发发顺丰云配
-            if(order.getPayMoney().compareTo(new BigDecimal(100))>0){
-                //发互联网医院SF.0235488558_241101
-                FsExpress express=expressService.selectFsExpressByOmsCode("SF.0235488558_241101");
-                erpOrder.setExpress_code(express.getOmsCode());
-                order.setDeliveryName(express.getName());
-                order.setDeliveryCode(express.getCode());
-            }else {
-                //发ztpdd
-                FsExpress express=expressService.selectFsExpressByOmsCode("CDYJFYD.400011111705_241230");
-                erpOrder.setExpress_code(express.getOmsCode());
-                order.setDeliveryName(express.getName());
-                order.setDeliveryCode(express.getCode());
+        if (!("金牛明医".equals(cloudHostProper.getCompanyName()))){
+            if (order.getPayRemain().compareTo(new BigDecimal(0)) == 0) {
+                //没有物流代收
+                //大于100发发顺丰云配
+                if(order.getPayMoney().compareTo(new BigDecimal(100))>0){
+                    //发互联网医院SF.0235488558_241101
+                    FsExpress express=expressService.selectFsExpressByOmsCode("SF.0235488558_241101");
+                    erpOrder.setExpress_code(express.getOmsCode());
+                    order.setDeliveryName(express.getName());
+                    order.setDeliveryCode(express.getCode());
+                }else {
+                    //发ztpdd
+                    FsExpress express=expressService.selectFsExpressByOmsCode("CDYJFYD.400011111705_241230");
+                    erpOrder.setExpress_code(express.getOmsCode());
+                    order.setDeliveryName(express.getName());
+                    order.setDeliveryCode(express.getCode());
                 }
-        } else {
-            FsExpress express = new FsExpress();
-            if (order.getCompanyId()!=null){
-                Company company = companyService.selectCompanyById(order.getCompanyId());
-                if (company != null && StringUtils.isNotEmpty(company.getOmsCode())){
-                    express = expressService.selectFsExpressByOmsCode(company.getOmsCode());
+            } else {
+                FsExpress express = new FsExpress();
+                if (order.getCompanyId()!=null){
+                    Company company = companyService.selectCompanyById(order.getCompanyId());
+                    if (company != null && StringUtils.isNotEmpty(company.getOmsCode())){
+                        express = expressService.selectFsExpressByOmsCode(company.getOmsCode());
+                    }else {
+                        express=expressService.selectFsExpressByOmsCode("SF.0235469535");
+                    }
                 }else {
                     express=expressService.selectFsExpressByOmsCode("SF.0235469535");
                 }
-            }else {
-                express=expressService.selectFsExpressByOmsCode("SF.0235469535");
-            }
                 //物流代发互联网医院顺
                 erpOrder.setExpress_code(express.getOmsCode());
                 order.setDeliveryName(express.getName());
@@ -1744,6 +1765,8 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
                 erpOrder.setCod(true);
 
             }
+        }
+
 
         erpOrder.setPayments(payments);
         if(order.getCompanyId()!=null){
@@ -1799,11 +1822,21 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
         }
         erpOrder.setDetails(details);
         erpOrder.setReceiver_name(order.getUserName().replaceAll("[^\\u4e00-\\u9fa5a-zA-Z0-9]", ""));
-        if (order.getUserPhone().length()>11){
-            String phone = decryptPhone(order.getUserPhone());
+        //2025.6.27 金牛要求erp推送电话可以设置默认 不影响其他推送
+        String phone = null;
+        if (StringUtils.isNotBlank(order.getErpPhone())){
+            phone = order.getErpPhone();
+        } else {
+            if (order.getUserPhone().length()>11){
+                phone = decryptPhone(order.getUserPhone());
+            }else {
+                phone = order.getUserPhone();
+            }
+        }
+        if(phone.length()>11){
+            erpOrder.setReceiver_phone(phone);
+        } else{
             erpOrder.setReceiver_mobile(phone);
-        }else {
-            erpOrder.setReceiver_mobile(order.getUserPhone());
         }
 
         String[] address=order.getUserAddress().split(" ");
@@ -1844,7 +1877,11 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
             //支付成功后 将订单号写入待发货的REDIS中
             redisCache.setCacheObject("delivery"+":"+response.getCode(),order.getOrderCode());
             //写入外部订单号
-            order.setExtendOrderId(response.getCode());
+            if (erpType == 2){
+                order.setExtendOrderId(order.getOrderCode());
+            } else {
+                order.setExtendOrderId(response.getCode());
+            }
             fsStoreOrderMapper.updateFsStoreOrder(order);
         }else {
 //            if (response.getErrorDesc().equals("该订单已创建")){
@@ -3061,7 +3098,7 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
             }
         }
 
-        if(order.getStoreHouseCode().equals("WHSP001")){
+        if("WHSP001".equals(order.getStoreHouseCode())){
             //武汉不备注快递史
             //erpOrder.setSeller_memo(erpOrder.getSeller_memo()+"-"+order.getDeliveryName());
         }
@@ -3128,11 +3165,17 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
         }
         erpOrder.setDetails(details);
         erpOrder.setReceiver_name(order.getUserName());
-        if(order.getUserPhone().length()>11){
-            erpOrder.setReceiver_phone(order.getUserPhone());
+        //2025.6.27 金牛要求erp推送电话可以设置默认 不影响其他推送
+        String phone = null;
+        if (StringUtils.isNotBlank(order.getErpPhone())){
+            phone = order.getErpPhone();
+        } else {
+            phone = order.getUserPhone();
         }
-        else{
-            erpOrder.setReceiver_mobile(order.getUserPhone());
+        if(phone.length()>11){
+            erpOrder.setReceiver_phone(phone);
+        } else{
+            erpOrder.setReceiver_mobile(phone);
         }
         String[] address= order.getUserAddress().split(" ");
         erpOrder.setReceiver_province(address[0]);
@@ -3160,6 +3203,11 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
         return fsStoreOrderMapper.selectFsStoreOrderCountByType(companyId,userId,type);
     }
 
+    @Override
+    public int batchUpdateErpByOrderIds(FsStoreOrderSetErpPhoneParam param) {
+        return fsStoreOrderMapper.batchUpdateErpByOrderIds(param);
+    }
+
     public  boolean containsAddress(String companyName) {
         String[] items= {"新疆","西藏","内蒙古","海南"};
         boolean found = false;
@@ -3172,5 +3220,14 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
         return found;
     }
 
+    @Override
+    public FsStoreOrder getOrderByOrderCodeAndStoreId(String orderCode, Long storeId){
+        return fsStoreOrderMapper.getOrderByOrderCodeAndStoreId(orderCode,storeId);
+    }
+
+    @Override
+    public int updateStoreOrderDeliveryInfo(FsStoreOrder order){
+        return fsStoreOrderMapper.updateStoreOrderDeliveryInfo(order);
+    }
 
 }

+ 15 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsStoreProductAttrValueServiceImpl.java

@@ -98,4 +98,19 @@ public class FsStoreProductAttrValueServiceImpl implements IFsStoreProductAttrVa
     public List<FsStoreProductAttrValueListDVO> selectFsStoreProductAttrValueListDVO(FsStoreProductAttrValueListDParam param) {
         return fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueListDVO(param);
     }
+
+    @Override
+    public FsStoreProductAttrValue selectFsStoreProductAttrValueByIdAndPruductId(Long id, Long productId){
+        return fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueByIdAndPruductId(id,productId);
+    }
+
+    @Override
+    public int updateStoreProductAttrValueStock(Long id , Integer stock){
+        return fsStoreProductAttrValueMapper.updateStoreProductAttrValueStock(id,stock);
+    }
+
+    @Override
+    public List<FsStoreProductAttrValue> selectFsStoreProductAttrValueListByProductId( Long productId){
+        return fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueListByProductId(productId);
+    }
 }

+ 128 - 86
fs-service/src/main/java/com/fs/his/service/impl/FsStoreProductServiceImpl.java

@@ -17,6 +17,9 @@ import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.DateUtils;
+import com.fs.erp.domain.ErpGoods;
+import com.fs.erp.service.IErpGoodsService;
+import com.fs.his.config.FsSysConfig;
 import com.fs.his.domain.*;
 import com.fs.his.dto.FsPackagePruductDTO;
 import com.fs.his.mapper.FsPackageOrderMapper;
@@ -28,10 +31,12 @@ import com.fs.his.mapper.FsStoreProductAttrMapper;
 import com.fs.his.mapper.FsStoreProductAttrValueMapper;
 import com.fs.his.param.FsStoreProductListSParam;
 import com.fs.his.service.IFsPackageService;
+import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.*;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 import com.fs.his.mapper.FsStoreProductMapper;
 import com.fs.his.service.IFsStoreProductService;
@@ -44,8 +49,7 @@ import org.springframework.transaction.annotation.Transactional;
  * @date 2023-06-08
  */
 @Service
-public class FsStoreProductServiceImpl implements IFsStoreProductService
-{
+public class FsStoreProductServiceImpl implements IFsStoreProductService {
     @Autowired
     private FsStoreProductMapper fsStoreProductMapper;
 
@@ -60,6 +64,12 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
 
     @Autowired
     IFsPackageService fsPackageService;
+    @Autowired
+    private ConfigUtil configUtil;
+
+    @Autowired
+    @Qualifier("hzOMSErpGoodsServiceImpl")
+    private IErpGoodsService hzOMSErpGoodsService;
 
     /**
      * 查询商品
@@ -68,8 +78,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
      * @return 商品
      */
     @Override
-    public FsStoreProduct selectFsStoreProductByProductId(Long productId)
-    {
+    public FsStoreProduct selectFsStoreProductByProductId(Long productId) {
         return fsStoreProductMapper.selectFsStoreProductByProductId(productId);
     }
 
@@ -80,8 +89,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
      * @return 商品
      */
     @Override
-    public List<FsStoreProduct> selectFsStoreProductList(FsStoreProduct fsStoreProduct)
-    {
+    public List<FsStoreProduct> selectFsStoreProductList(FsStoreProduct fsStoreProduct) {
         return fsStoreProductMapper.selectFsStoreProductList(fsStoreProduct);
     }
 
@@ -92,8 +100,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
      * @return 结果
      */
     @Override
-    public int insertFsStoreProduct(FsStoreProduct fsStoreProduct)
-    {
+    public int insertFsStoreProduct(FsStoreProduct fsStoreProduct) {
         fsStoreProduct.setCreateTime(DateUtils.getNowDate());
 
         return fsStoreProductMapper.insertFsStoreProduct(fsStoreProduct);
@@ -106,8 +113,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
      * @return 结果
      */
     @Override
-    public int updateFsStoreProduct(FsStoreProduct fsStoreProduct)
-    {
+    public int updateFsStoreProduct(FsStoreProduct fsStoreProduct) {
         fsStoreProduct.setUpdateTime(DateUtils.getNowDate());
         return fsStoreProductMapper.updateFsStoreProduct(fsStoreProduct);
     }
@@ -119,8 +125,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
      * @return 结果
      */
     @Override
-    public int deleteFsStoreProductByProductIds(Long[] productIds)
-    {
+    public int deleteFsStoreProductByProductIds(Long[] productIds) {
         int i = fsStoreProductMapper.deleteFsStoreProductByProductIds(productIds);
         for (Long id : productIds) {
             fsStoreProductAttrMapper.clear(id);
@@ -137,8 +142,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
      * @return 结果
      */
     @Override
-    public int deleteFsStoreProductByProductId(Long productId)
-    {
+    public int deleteFsStoreProductByProductId(Long productId) {
         return fsStoreProductMapper.deleteFsStoreProductByProductId(productId);
     }
 
@@ -151,6 +155,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
     public List<FsStoreProductListSVO> selectFsStoreProductListSVO(FsStoreProductListSParam fsStoreProduct) {
         return fsStoreProductMapper.selectFsStoreProductListSVO(fsStoreProduct);
     }
+
     @Override
     public List<OptionsVO> selectFsStoreProductStoreList() {
         return fsStoreProductMapper.selectFsStoreProductStoreList();
@@ -217,20 +222,20 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
             valueMap.put("groupBarCode", "");
             valueMap.put("weight", 0);
             valueMap.put("volume", 0);
-            valueMap.put("doctorBrokerage",0);
+            valueMap.put("doctorBrokerage", 0);
             valueMap.put("brokerage", 0);
 //            valueMap.put("brokerageTwo", 0);
 //            valueMap.put("brokerageThree", 0);
             valueMap.put("giveIntegral", 0);
             if (productId > 0) {
-                FsStoreProductAttrValue attrValueMap=new FsStoreProductAttrValue();
+                FsStoreProductAttrValue attrValueMap = new FsStoreProductAttrValue();
                 attrValueMap.setProductId(productId);
                 attrValueMap.setSku(sku);
                 List<FsStoreProductAttrValue> values = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueList(attrValueMap);
 
-                if (values != null&&values.size()==1) {
-                    valueMap.put("id",values.get(0).getId());
-                    valueMap.put("sku",values.get(0).getSku());
+                if (values != null && values.size() == 1) {
+                    valueMap.put("id", values.get(0).getId());
+                    valueMap.put("sku", values.get(0).getSku());
                     valueMap.put("image", values.get(0).getImage());
                     valueMap.put("price", values.get(0).getPrice());
                     valueMap.put("cost", values.get(0).getCost());
@@ -245,7 +250,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
                     valueMap.put("brokerage", values.get(0).getBrokerage());
 //                    valueMap.put("brokerageTwo", values.get(0).getBrokerageTwo());
 //                    valueMap.put("brokerageThree", values.get(0).getBrokerageThree());
-                    System.out.println("积分 :"+values.get(0).getGiveIntegral());
+                    System.out.println("积分 :" + values.get(0).getGiveIntegral());
                     valueMap.put("giveIntegral", values.get(0).getGiveIntegral());
                 }
             }
@@ -366,7 +371,6 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
     }
 
 
-
     private DetailVO attrFormat(List<ProductArrtDTO> fromatDetailDTOList) {
         List<String> data = new ArrayList<>();
         List<Map<String, Map<String, String>>> res = new ArrayList<>();
@@ -441,20 +445,21 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
         detailDto.setRes(res);
         return detailDto;
     }
+
     @SuppressWarnings("all")
     @Override
     @Transactional
     public R addOrEdit(FsStoreProductAddEditParam param) {
-        ProductAttrCountDTO countDto=computedProductCount(param.getValues());
+        ProductAttrCountDTO countDto = computedProductCount(param.getValues());
 
-        if(param.getProductId()!=null){
-            FsStoreProduct product=new FsStoreProduct();
-            BeanUtils.copyProperties(param,product);
+        if (param.getProductId() != null) {
+            FsStoreProduct product = new FsStoreProduct();
+            BeanUtils.copyProperties(param, product);
             product.setPrice(countDto.getMinPrice());
             product.setOtPrice(countDto.getMinOtPrice());
             product.setCostPrice(countDto.getMinCost());
             product.setBarCode(param.getValues().get(0).getBarCode());
-           product.setGiveIntegral(  new BigDecimal(countDto.getMinIntegral()));
+            product.setGiveIntegral(new BigDecimal(countDto.getMinIntegral()));
             product.setStock(countDto.getStock());
             fsStoreProductMapper.updateFsStoreProduct(product);
             if (param.getSpecType().equals(0)) {
@@ -462,21 +467,21 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
                         .value("规格")
                         .detail(ListUtil.toList("默认"))
                         .build();
-                List<ProductArrtDTO> items=new ArrayList<>();
+                List<ProductArrtDTO> items = new ArrayList<>();
                 items.add(fromatDetailDto);
                 param.getValues().get(0).setSku("默认");
-                addProductAttr(product.getProductId(),items,param.getValues());
+                addProductAttr(product.getProductId(), items, param.getValues());
             } else {
-                addProductAttr(product.getProductId(),param.getItems(),param.getValues());
+                addProductAttr(product.getProductId(), param.getItems(), param.getValues());
             }
-            List<FsPackage> fsPackage = fsPackageOrderMapper.selectFsPackageOrderListByProd("\"productId\""+":"+param.getProductId());
-            if (fsPackage!=null&&fsPackage.size()>0){
+            List<FsPackage> fsPackage = fsPackageOrderMapper.selectFsPackageOrderListByProd("\"productId\"" + ":" + param.getProductId());
+            if (fsPackage != null && fsPackage.size() > 0) {
                 for (FsPackage pack : fsPackage) {
                     JSONArray objects = JSONUtil.parseArray(pack.getProductJson());
-                    List<FsPackagePruductDTO> products=JSONUtil.toList(objects,FsPackagePruductDTO.class);
-                  for (FsPackagePruductDTO fsPackagePruductDTO : products) {
-                        if (fsPackagePruductDTO.getProductId()-param.getProductId()==0){
-                            FsStoreProductAttrValueVO vo=fsStoreProductMapper.selectFsStoreProductAttrValueVOByProdId(param.getProductId());
+                    List<FsPackagePruductDTO> products = JSONUtil.toList(objects, FsPackagePruductDTO.class);
+                    for (FsPackagePruductDTO fsPackagePruductDTO : products) {
+                        if (fsPackagePruductDTO.getProductId() - param.getProductId() == 0) {
+                            FsStoreProductAttrValueVO vo = fsStoreProductMapper.selectFsStoreProductAttrValueVOByProdId(param.getProductId());
                             fsPackagePruductDTO.setSku(vo.getSku());
                             fsPackagePruductDTO.setStock(vo.getStock());
                             fsPackagePruductDTO.setSales(vo.getSales());
@@ -501,10 +506,9 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
                 }
 
             }
-        }
-        else{
-            FsStoreProduct product=new FsStoreProduct();
-            BeanUtils.copyProperties(param,product);
+        } else {
+            FsStoreProduct product = new FsStoreProduct();
+            BeanUtils.copyProperties(param, product);
             product.setPrice(countDto.getMinPrice());
             product.setOtPrice(countDto.getMinOtPrice());
             product.setCostPrice(countDto.getMinCost());
@@ -516,44 +520,64 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
                         .value("规格")
                         .detail(ListUtil.toList("默认"))
                         .build();
-                List<ProductArrtDTO> items=new ArrayList<>();
+                List<ProductArrtDTO> items = new ArrayList<>();
                 items.add(fromatDetailDto);
                 param.getValues().get(0).setSku("默认");
-                addProductAttr(product.getProductId(),items,param.getValues());
+                addProductAttr(product.getProductId(), items, param.getValues());
+            } else {
+                addProductAttr(product.getProductId(), param.getItems(), param.getValues());
+            }
+            //todo 添加商品HzOMSerp调用
+            //判断是否开启erp
+            FsSysConfig sysConfig = configUtil.getSysConfig();
+            Integer erpOpen = sysConfig.getErpOpen();
+            if (erpOpen == null || erpOpen == 0) {
+                //不作操作
             } else {
-                addProductAttr(product.getProductId(),param.getItems(),param.getValues());
+                //判断erp类型
+                Integer erpType = sysConfig.getErpType();
+                if (erpType == null) {
+                    //不作操作
+                } else if (erpType == 3) {
+                    ErpGoods params = new ErpGoods();
+                    params.setStoreProductId(product.getProductId());
+                    //瀚智OMS 维护添加商品
+                    hzOMSErpGoodsService.addGoods(params);
+                }
             }
+
+
         }
         return R.ok();
     }
 
 
-    private void addProductAttr(Long productId, List<ProductArrtDTO> items, List<FsStoreProductAttrValue> values){
+    private void addProductAttr(Long productId, List<ProductArrtDTO> items, List<FsStoreProductAttrValue> values) {
         //清空attr
         fsStoreProductAttrMapper.clear(productId);
         //清空values
         //查出商品属性所有ID;
-        List<FsStoreProductAttrValue> attrValues=fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueByProductId(productId);
+        List<FsStoreProductAttrValue> attrValues = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueByProductId(productId);
         fsStoreProductAttrValueMapper.deleteFsStoreProductAttrValueByProductId(productId);
         //写入attr
-        for(ProductArrtDTO vo:items){
-            FsStoreProductAttr attr=new FsStoreProductAttr();
+        for (ProductArrtDTO vo : items) {
+            FsStoreProductAttr attr = new FsStoreProductAttr();
             attr.setProductId(productId);
             attr.setAttrName(vo.getValue());
             attr.setAttrValues(StringUtils.join(vo.getDetail(), ","));
             fsStoreProductAttrMapper.insertFsStoreProductAttr(attr);
         }
-        Map<String,Object> map = new LinkedHashMap<>();
-        map.put("attr",items);
+        Map<String, Object> map = new LinkedHashMap<>();
+        map.put("attr", items);
         // map.put("value",values);
 
-        for(FsStoreProductAttrValue val: values){
+        for (FsStoreProductAttrValue val : values) {
             //更新套餐商品属性ID  获取套餐
-            Long id=val.getId();
-            if(val.getDetail()!=null){
+            Long id = val.getId();
+            if (val.getDetail() != null) {
                 List<String> stringList = new ArrayList<>(val.getDetail().values());
                 Collections.sort(stringList);
-                val.setSku(StrUtil.join(",",stringList));
+                val.setSku(StrUtil.join(",", stringList));
             }
             val.setProductId(productId);
             fsStoreProductAttrValueMapper.insertFsStoreProductAttrValue(val);
@@ -563,7 +587,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
     }
 
     private ProductAttrCountDTO computedProductCount(List<FsStoreProductAttrValue> values) {
-        BigDecimal val=new BigDecimal(0);
+        BigDecimal val = new BigDecimal(0);
         //取最小价格
         BigDecimal minPrice = values
                 .stream()
@@ -610,7 +634,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
     @Override
     public void incProductStock(Long num, Long productId, Long productAttrValueId) {
         //处理属性sku
-        if (productAttrValueId!=null) {
+        if (productAttrValueId != null) {
             fsStoreProductMapper.incProductAttrStock(num, productId, productAttrValueId);
         }
         //处理商品库存
@@ -620,8 +644,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
 
     @Override
     public String importSrorePeoduct(List<FsStoreProductExcelVO> productList, boolean updateSupport) {
-        if (com.fs.common.utils.StringUtils.isNull(productList) || productList.size() == 0)
-        {
+        if (com.fs.common.utils.StringUtils.isNull(productList) || productList.size() == 0) {
             throw new ServiceException("导入商品数据不能为空!");
         }
         int successNum = 0;
@@ -629,10 +652,8 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
         StringBuilder successMsg = new StringBuilder();
         StringBuilder failureMsg = new StringBuilder();
 
-        for (FsStoreProductExcelVO productVO : productList)
-        {
-            try
-            {
+        for (FsStoreProductExcelVO productVO : productList) {
+            try {
 
 //                if (1==1){
 //                    FsStoreProductAttrValue v = new FsStoreProductAttrValue();
@@ -652,7 +673,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
 //                product.setProductType(1);
 //                product.setSpecType(0);
 //                productVO.setDoctorBrokerage(new BigDecimal(0));
-                if (product.getBarCode()==null || product.getBarCode()==""){
+                if (product.getBarCode() == null || product.getBarCode() == "") {
                     throw new CustomException("商品编号为空");
                 }
                 //
@@ -662,38 +683,38 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
                         .value("规格")
                         .detail(ListUtil.toList("默认"))
                         .build();
-                List<ProductArrtDTO> items=new ArrayList<>();
+                List<ProductArrtDTO> items = new ArrayList<>();
                 items.add(fromatDetailDto);
                 FsStoreProductAttrValue p = new FsStoreProductAttrValue();
                 p.setProductId(product.getProductId());
                 p.setSku("默认");
                 p.setStock(product.getStock());
-                if (product.getStock()==null){
+                if (product.getStock() == null) {
                     p.setStock(0);
                 }
                 p.setSales(product.getSales());
-                if (product.getSales()==null){
+                if (product.getSales() == null) {
                     p.setStock(0);
                 }
                 p.setPrice(product.getPrice());
-                if (product.getPrice()==null){
+                if (product.getPrice() == null) {
                     p.setPrice(new BigDecimal(0));
                 }
                 p.setImage(product.getImgUrl());
                 p.setCost(product.getCostPrice());
-                if (product.getCostPrice()==null){
+                if (product.getCostPrice() == null) {
                     p.setCost(new BigDecimal(0));
                 }
                 p.setBarCode(product.getBarCode());
                 p.setGroupBarCode(product.getBarCode());
                 p.setOtPrice(product.getOtPrice());
-                if (product.getOtPrice()==null){
+                if (product.getOtPrice() == null) {
                     p.setOtPrice(new BigDecimal(0));
                 }
 
                 p.setDoctorBrokerage(productVO.getDoctorBrokerage());
                 p.setWeight(productVO.getWeight());
-                if (productVO.getWeight()==null){
+                if (productVO.getWeight() == null) {
                     p.setWeight(new BigDecimal(0));
                 }
 
@@ -701,9 +722,9 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
                 p.setBrokerage(new BigDecimal(0));
                 p.setBrokerageTwo(new BigDecimal(0));
                 p.setBrokerageThree(new BigDecimal(0));
-                if (product.getGiveIntegral()==null){
+                if (product.getGiveIntegral() == null) {
                     p.setGiveIntegral(0);
-                }else {
+                } else {
                     p.setGiveIntegral(product.getGiveIntegral().intValue());
                 }
                 p.setAgentPrice(new BigDecimal(0));
@@ -715,25 +736,37 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
                 attr.setAttrName("规格");
                 attr.setAttrValues("默认");
                 fsStoreProductAttrMapper.insertFsStoreProductAttr(attr);
-                    successNum++;
-                    successMsg.append("<br/>" + successNum + "、商品 " + product.getProductName() + " 导入成功");
-
-            }
-            catch (Exception e)
-            {
+                successNum++;
+                successMsg.append("<br/>" + successNum + "、商品 " + product.getProductName() + " 导入成功");
+                //todo 添加商品HzOMSerp调用
+                //判断是否开启erp
+                FsSysConfig sysConfig = configUtil.getSysConfig();
+                Integer erpOpen = sysConfig.getErpOpen();
+                if (erpOpen == null || erpOpen == 0) {
+                    //不作操作
+                } else{
+                    //判断erp类型
+                    Integer erpType = sysConfig.getErpType();
+                    if (erpType == null) {
+                        //不作操作
+                    }else if (erpType == 3) {
+                        ErpGoods params = new ErpGoods();
+                        params.setStoreProductId(product.getProductId());
+                        //瀚智OMS 维护添加商品
+                        hzOMSErpGoodsService.addGoods(params);
+                    }
+                }
+            } catch (Exception e) {
 
                 failureNum++;
-                String msg = "<br/>" + failureNum + "、商品 " + productVO.getProductName()  + " 导入失败:";
+                String msg = "<br/>" + failureNum + "、商品 " + productVO.getProductName() + " 导入失败:";
                 failureMsg.append(msg + e.getMessage());
             }
         }
-        if (failureNum > 0)
-        {
+        if (failureNum > 0) {
             failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
             throw new ServiceException(failureMsg.toString());
-        }
-        else
-        {
+        } else {
             successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
         }
         return successMsg.toString();
@@ -743,8 +776,8 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
     public int updateFsStoreProductPrice(FsStoreProduct fsStoreProduct, Integer price) {
 
         List<Long> ids = fsStoreProductMapper.selectFsStoreProductIdList(fsStoreProduct);
-        fsStoreProductAttrValueMapper.updateFsStoreProductAttrValuePrice(ids,(100+price)/100.0);
-        return fsStoreProductMapper.updateFsStoreProductPrice(ids,(100+price)/100.0);
+        fsStoreProductAttrValueMapper.updateFsStoreProductAttrValuePrice(ids, (100 + price) / 100.0);
+        return fsStoreProductMapper.updateFsStoreProductPrice(ids, (100 + price) / 100.0);
     }
 
     @Override
@@ -754,7 +787,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
             FsStoreProductAttrValue va = new FsStoreProductAttrValue();
             va.setProductId(vo.getProductId());
             List<FsStoreProductAttrValue> list = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueList(va);
-            if (!list.isEmpty()){
+            if (!list.isEmpty()) {
                 vo.setBrokerage(list.get(0).getBrokerage());
                 vo.setDoctorBrokerage(list.get(0).getDoctorBrokerage());
                 vo.setWeight(list.get(0).getWeight());
@@ -775,7 +808,7 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
 
     @Override
     public Long selectFsStoreProductCount(int type, Long companyId) {
-        return fsStoreProductMapper.selectFsStoreProductCompanyCount(type,companyId);
+        return fsStoreProductMapper.selectFsStoreProductCompanyCount(type, companyId);
     }
 
     @Override
@@ -783,5 +816,14 @@ public class FsStoreProductServiceImpl implements IFsStoreProductService
         return fsStoreProductMapper.selectFsStoreProductCount(type);
     }
 
+    @Override
+    public FsStoreProduct getStoreProductByProductIdAndStoreId(Long productId, Long storeId) {
+        return fsStoreProductMapper.getStoreProductByProductIdAndStoreId(productId, storeId);
+    }
+
+    @Override
+    public int updateStoreProductStock(Long productId, Integer num) {
+        return fsStoreProductMapper.updateStoreProductStock(productId, num);
+    }
 
 }

+ 238 - 0
fs-service/src/main/java/com/fs/his/service/impl/HzOMSErpApiServiceImpl.java

@@ -0,0 +1,238 @@
+package com.fs.his.service.impl;
+
+import cn.hutool.core.codec.Base64Decoder;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.erp.dto.sdk.HzOMS.utils.HzOMSUtils;
+import com.fs.his.config.FsSysConfig;
+import com.fs.his.domain.FsStoreOrder;
+import com.fs.his.domain.FsStoreProduct;
+import com.fs.his.domain.FsStoreProductAttrValue;
+import com.fs.his.param.HzOMSErpApiParam;
+import com.fs.his.service.ErpApiService;
+import com.fs.his.service.IFsStoreOrderService;
+import com.fs.his.service.IFsStoreProductAttrValueService;
+import com.fs.his.service.IFsStoreProductService;
+import com.fs.his.utils.ConfigUtil;
+import com.fs.his.vo.HzOMSErpResponseDetailVO;
+import com.fs.his.vo.HzOMSErpResponseErrorItemVO;
+import com.fs.his.vo.HzOMSErpResponseVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Base64Utils;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class HzOMSErpApiServiceImpl implements ErpApiService {
+
+    @Autowired
+    private ConfigUtil configUtil;
+
+    private final Integer HZOMS_ERP_TYPE = 3;
+
+    private final String CODE_SUCCESS = "200";
+
+    @Autowired
+    IFsStoreProductService fsStoreProductService;
+
+    @Autowired
+    IFsStoreProductAttrValueService fsStoreProductAttrValueService;
+
+    @Autowired
+    IFsStoreOrderService fsStoreOrderService;
+
+    /**
+     * 平台商品库存同步到第三方
+     *
+     * @param param
+     * @return
+     */
+    @Override
+    public HzOMSErpResponseVO stockSync(HzOMSErpApiParam param) {
+        HzOMSErpResponseVO res = new HzOMSErpResponseVO();
+        //校验数据合法性
+        String v = legitimateVerification(param);
+        if (!"".equals(v)) {
+            res.setSuccess(Boolean.FALSE);
+            res.setMessage(v);
+            return res;
+        }
+        //对方传输的datapacage是base64编码的 先解码得到 json
+        JSONObject dataObj = getDecodeDataJSON(param.getDatapakege());
+        res.setCode(CODE_SUCCESS);
+        res.setMessage(dataObj.toJSONString());
+        Long storeId = dataObj.getLong("cptshopcode");
+        JSONArray syncstocklist = dataObj.getJSONArray("syncstocklist");
+        List<HzOMSErpResponseErrorItemVO> errorlist = new ArrayList<>();
+        syncstocklist.forEach(item -> {
+            JSONObject o = (JSONObject) item;
+            //判断当前店铺是否存在此商品
+            FsStoreProduct fsStoreProduct = fsStoreProductService.getStoreProductByProductIdAndStoreId(o.getLong("cptgoodsid"), storeId);
+            if (fsStoreProduct != null) {
+                fsStoreProductService.updateStoreProductStock(o.getLong("cptgoodsid"), o.getInteger("fptstock"));
+            } else {
+                HzOMSErpResponseErrorItemVO errorItemVO = new HzOMSErpResponseErrorItemVO();
+                errorItemVO.setCptgoodsid(o.getLong("cptgoodsid").toString());
+                errorItemVO.setMsg("商品不存在");
+                errorlist.add(errorItemVO);
+            }
+        });
+        res.setSuccess(Boolean.TRUE);
+        HzOMSErpResponseDetailVO resData = new HzOMSErpResponseDetailVO();
+        resData.setErrorlist(errorlist);
+        res.setData(resData);
+        return res;
+    }
+
+    /**
+     * 用于将瀚智商品库存同步到第三方
+     * @param param
+     * @return
+     */
+    @Override
+    public HzOMSErpResponseVO skuStockSync(HzOMSErpApiParam param) {
+        HzOMSErpResponseVO res = new HzOMSErpResponseVO();
+
+        String v = legitimateVerification(param);
+        if (!"".equals(v)) {
+            res.setSuccess(Boolean.FALSE);
+            res.setMessage(v);
+            return res;
+        }
+        //对方传输的datapacage是base64编码的 先解码得到 json
+        JSONObject dataObj = getDecodeDataJSON(param.getDatapakege());
+        res.setCode(CODE_SUCCESS);
+        res.setMessage("同步商品规格库存成功");
+
+        Long storeId = dataObj.getLong("cptshopcode");
+        JSONArray syncstocklist = dataObj.getJSONArray("syncstocklist");
+        List<HzOMSErpResponseErrorItemVO> errorlist = new ArrayList<>();
+        syncstocklist.forEach(item -> {
+            //商品
+            JSONObject o = (JSONObject) item;
+            Long productId = o.getLong("cptgoodsid");
+            JSONArray skustocklist = o.getJSONArray("skustocklist");
+            //规格list
+            skustocklist.forEach(skuItem -> {
+                JSONObject skuO = (JSONObject) skuItem;
+                Long skuId = skuO.getLong("cptspeccode");
+                //判断当前店铺是否存在此商品规格
+                FsStoreProductAttrValue fsStoreProductAttrValue = fsStoreProductAttrValueService.selectFsStoreProductAttrValueByIdAndPruductId(productId, skuId);
+                if (fsStoreProductAttrValue != null) {
+                    fsStoreProductAttrValueService.updateStoreProductAttrValueStock(productId, o.getInteger("fptstock"));
+                } else {
+                    HzOMSErpResponseErrorItemVO errorItemVO = new HzOMSErpResponseErrorItemVO();
+                    errorItemVO.setCptgoodsid(productId.toString());
+                    errorItemVO.setCptspeccode(skuId.toString());
+                    errorItemVO.setMsg("规格不存在");
+                    errorlist.add(errorItemVO);
+                }
+            });
+        });
+        res.setSuccess(Boolean.TRUE);
+        HzOMSErpResponseDetailVO resData = new HzOMSErpResponseDetailVO();
+        resData.setErrorlist(errorlist);
+        res.setData(resData);
+        return res;
+    }
+
+    /**
+     * 平台订单物流信息回传
+     *
+     * @param param
+     * @return
+     */
+    @Override
+    public HzOMSErpResponseVO logisticsAdd(HzOMSErpApiParam param) {
+        HzOMSErpResponseVO res = new HzOMSErpResponseVO();
+        //校验方法调用合法性
+        String v = legitimateVerification(param);
+        if (!"".equals(v)) {
+            res.setSuccess(Boolean.FALSE);
+            res.setMessage(v);
+            return res;
+        }
+        //对方传输的datapacage是base64编码的 先解码得到 json
+        JSONArray dataArr = getDecodeDataJSONArr(param.getDatapakege());
+        List<HzOMSErpResponseErrorItemVO> errorlist = new ArrayList<>();
+        dataArr.forEach(oItem -> {
+            JSONObject item = (JSONObject) oItem;
+            //判断订单是否存在
+            FsStoreOrder fsOrder = fsStoreOrderService.getOrderByOrderCodeAndStoreId(item.getString("cptordercode"), item.getLong("cptshopcode"));
+            if (null != fsOrder) {
+                fsOrder.setDeliveryCode(item.getString("cexpresscode"));
+                fsOrder.setDeliveryName(item.getString("cexpressname"));
+                fsOrder.setDeliverySn(item.getString("clogisticsno"));
+                fsStoreOrderService.updateStoreOrderDeliveryInfo(fsOrder);
+            } else {
+                HzOMSErpResponseErrorItemVO addErrItem = new HzOMSErpResponseErrorItemVO();
+                addErrItem.setCptordercode(item.getString("cptordercode"));
+                addErrItem.setCptshopcode(item.getString("cptshopcode"));
+                addErrItem.setMsg("订单不存在");
+                errorlist.add(addErrItem);
+            }
+        });
+        res.setCode(CODE_SUCCESS);
+        res.setMessage("平台订单物流信息回传成功");
+        HzOMSErpResponseDetailVO resData = new HzOMSErpResponseDetailVO();
+        resData.setErrorlist(errorlist);
+        return res;
+    }
+
+    /**
+     * 校验方法调用合法性
+     *
+     * @param param
+     * @return
+     */
+    public String legitimateVerification(HzOMSErpApiParam param) {
+        FsSysConfig sysConfig = configUtil.getSysConfig();
+        Integer erpType = sysConfig.getErpType();
+        //校验系统配置
+        if (!HZOMS_ERP_TYPE.equals(erpType)) {
+            return "系统配置ERP不匹配,请联系管理员。";
+        }
+        String erpHzOMSAppKey = sysConfig.getErpHzOMSAppKey();
+        //校验传参appKey是否相符
+        if (!erpHzOMSAppKey.equals(param.getAppkey())) {
+            return "系统配置appKey不匹配,请联系管理员。";
+        }
+        //校验数据签名是否合法
+        try {
+            Boolean b = HzOMSUtils.signValidate(param.getTimestamp(), param.getDatapakege(), sysConfig.getErpHzOMSAppsecret(), param.getSign());
+            if (!b) {
+                return "非法数据,签名校验失败。";
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            return "数据签名校验失败。";
+        }
+        return "";
+    }
+
+    /**
+     * 获取解码后的JSON数据
+     *
+     * @param s
+     * @return
+     */
+    public JSONObject getDecodeDataJSON(String s) {
+        JSONObject dataObj = JSONObject.parseObject(new String(Base64Utils.decode(s.getBytes(StandardCharsets.UTF_8))));
+        return dataObj;
+    }
+
+    /**
+     * 获取解码后的JSONArray数据
+     *
+     * @param s
+     * @return
+     */
+    public JSONArray getDecodeDataJSONArr(String s) {
+        JSONArray arr = JSONArray.parseArray(new String(Base64Utils.decode(s.getBytes(StandardCharsets.UTF_8))));
+        return arr;
+    }
+}

+ 3 - 0
fs-service/src/main/java/com/fs/his/vo/FsStoreOrderListVO.java

@@ -60,4 +60,7 @@ public class FsStoreOrderListVO {
     private Integer isFirst;
 
     private String source;
+
+    //erp推送号码
+    private String erpPhone;
 }

+ 11 - 0
fs-service/src/main/java/com/fs/his/vo/HzOMSErpResponseDetailVO.java

@@ -0,0 +1,11 @@
+package com.fs.his.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class HzOMSErpResponseDetailVO {
+
+    private List<HzOMSErpResponseErrorItemVO> errorlist;
+}

+ 19 - 0
fs-service/src/main/java/com/fs/his/vo/HzOMSErpResponseErrorItemVO.java

@@ -0,0 +1,19 @@
+package com.fs.his.vo;
+
+import lombok.Data;
+
+@Data
+public class HzOMSErpResponseErrorItemVO {
+
+    //平台商品编码
+    private String cptgoodsid;
+    //平台规格编码
+    private String cptspeccode;
+    //平台订单号
+    private String cptordercode;
+    //平台店铺编码
+    private String cptshopcode;
+    //错误描述
+    private String msg;
+
+}

+ 20 - 0
fs-service/src/main/java/com/fs/his/vo/HzOMSErpResponseVO.java

@@ -0,0 +1,20 @@
+package com.fs.his.vo;
+
+
+import lombok.Data;
+
+@Data
+public class HzOMSErpResponseVO {
+
+    private String code;
+
+    private HzOMSErpResponseDetailVO data;
+
+    private String message;
+
+    private String messageList;
+
+    private String requestId;
+
+    private Boolean success;
+}

+ 2 - 2
fs-service/src/main/java/com/fs/qw/domain/QwAutoTags.java

@@ -18,8 +18,8 @@ public class QwAutoTags extends BaseEntity
     /** $column.columnComment */
     private Long id;
 
-    /** 组别 1关键词打标签 2客户入群行为打标签 3分时段打标签 */
-    @Excel(name = "组别 1关键词打标签 2客户入群行为打标签 3分时段打标签")
+    /** 组别 1关键词打标签 2客户入群行为打标签 3分时段打标签 4自动回复打标签*/
+    @Excel(name = "组别 1关键词打标签 2客户入群行为打标签 3分时段打标签 4回复消息打标签")
     private Long type;
 
     /** 创建人 */

+ 14 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwAutoTagsMapper.java

@@ -40,6 +40,19 @@ public interface QwAutoTagsMapper
             "LIMIT 1;")
     public QwAutoTags selectQwAutoTagsByIdJSON(@Param("corpId")String corpId, @Param("qwUserId") Long qwUserId);
 
+
+
+    @Select("SELECT * FROM  qw_auto_tags " +
+            "WHERE " +
+            "corp_id=#{corpId} " +
+            "AND is_apply='1' " +
+            "AND JSON_CONTAINS(apply_users, CAST(#{qwUserId} AS JSON), '$') " +
+            "AND type='4' " +
+            "ORDER BY create_time DESC " +
+            "LIMIT 1;")
+    public QwAutoTags selectQwAutoReplyTagsByIdJSON(@Param("corpId")String corpId, @Param("qwUserId") Long qwUserId);
+
+
     /**
      * 根据群id 匹配最新的符合条件的标签
      */
@@ -113,6 +126,7 @@ public interface QwAutoTagsMapper
             "            <if test=\"isApply != null \"> and qat.is_apply = #{isApply}</if> " +
             "            <if test=\"companyId != null \"> and qat.company_id = #{companyId}</if> "+
             "            <if test=\"corpId != null \"> and qat.corp_id = #{corpId}</if> "+
+            "            <if test=\"isType != null \"> and (qat.type = 3 or  qat.type = 4) </if> "+
         "</where> " +
         "GROUP BY qat.id " +
         "ORDER BY qat.create_time DESC " +

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

@@ -40,6 +40,8 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     @Select("select * from qw_external_contact where name='待同步客户' AND create_time >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) and status !=4 ")
     public List<QwExternalContact> synchronizeQwExternalContactTask();
 
+    public List<QwExternalContact> selectQwExternalContactByIds(@Param("ids") List<Long> ids);
+
     @Select("SELECT id,stage_status,fs_user_id from qw_external_contact where id=#{id}")
     public QwExternalContact selectQwExternalContactByIdForStageStatus(@Param("id") Long id);
 
@@ -185,6 +187,8 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
 
     public int batchUpdateQwExternalContact(List<QwExternalContact> qwExternalContact);
 
+    public int batchUpdateQwExternalContactByTags(List<QwExternalContact> qwExternalContact);
+
     @Update("update qw_external_contact set qw_user_id=#{qwUserId} , company_user_id =#{companyUserId} " +
             "where user_id=#{userId}  and corp_id=#{corpId} ")
     public int updateQwExternalContactByQwUserId(QwExternalContact qwExternalContact);

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

@@ -1,6 +1,7 @@
 package com.fs.qw.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwWatchLog;
 import com.fs.qw.param.QwWatchLogStatisticsListParam;
 import com.fs.qw.vo.QwWatchLogAllStatisticsListVO;
@@ -235,4 +236,5 @@ public interface QwWatchLogMapper extends BaseMapper<QwWatchLog>{
     @Select("SELECT count(1) from qw_watch_log where fs_user_id=#{userId} and `day`=0")
     int selectQwWatchLogIsFirstByUserId(Long userId);
 
+
 }

+ 7 - 2
fs-service/src/main/java/com/fs/qw/param/QwAutoTagsParam.java

@@ -9,8 +9,8 @@ public class QwAutoTagsParam {
     /** $column.columnComment */
     private Long id;
 
-    /** 组别 1关键词打标签 2客户入群行为打标签 3分时段打标签 */
-    @Excel(name = "组别 1关键词打标签 2客户入群行为打标签 3分时段打标签")
+    /** 组别 1关键词打标签 2客户入群行为打标签 3分时段打标签 4自动回复打标签 */
+    @Excel(name = "组别 1关键词打标签 2客户入群行为打标签 3分时段打标签 4自动回复打标签")
     private Long type;
 
     /** 创建人 */
@@ -53,4 +53,9 @@ public class QwAutoTagsParam {
     /** 公司员工id */
     @Excel(name = "公司员工id")
     private Long companyUserId;
+
+    /**
+     * 是否查询分时段打标签 和 自动回复打标签 0 是 默认null
+     */
+    private String isType;
 }

+ 18 - 0
fs-service/src/main/java/com/fs/qw/param/QwExtContactAddTagByWatchParam.java

@@ -0,0 +1,18 @@
+package com.fs.qw.param;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+* 给看课记录上的用户打标签
+*/
+@Data
+public class QwExtContactAddTagByWatchParam {
+
+    /**
+    * 看课记录上的logId
+    */
+    List<Long> logIds;
+    List<String> tagIds;
+}

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

@@ -107,8 +107,10 @@ public interface IQwExternalContactService extends IService<QwExternalContact> {
     R transfer(TransferParam param);
 
     R addUserTag(QwExternalContactAddTagParam param) throws JSONException;
+    R addTagByWatch(QwExtContactAddTagByWatchParam param) throws JSONException;
 
     R delUserTag(QwExternalContactAddTagParam param);
+    R delTagByWatch(QwExtContactAddTagByWatchParam param);
 
     QwUser getQwUserByRedis(String corpId, String userID);
 

+ 587 - 111
fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java

@@ -72,6 +72,7 @@ import java.time.*;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -190,9 +191,6 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
     @Autowired
     private QwTagMapper qwTagMapper;
 
-    @Autowired
-    private QwExternalContactServiceImpl qwExternalContactService;
-
 
     org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());
 
@@ -416,7 +414,7 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
 
             // 获取组公共信息(每组只需查一次)
             QwExternalContact sample = group.get(0);
-            QwUser qwUser = qwExternalContactService.getQwUserByRedis(sample.getCorpId(), sample.getUserId());
+            QwUser qwUser = this.getQwUserByRedis(sample.getCorpId(), sample.getUserId());
             if (qwUser == null) {
                 logger.error("无企微员工信息: {}|{}", sample.getUserId(), sample.getCorpId());
                 return;
@@ -1189,142 +1187,593 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
 
     @Override
     public R addUserTag(QwExternalContactAddTagParam param) throws JSONException {
-        int err = 0;
-        int suc = 0;
-        // 获取当前日期(只包含年月日)
+        // 获取当前日期和时间
         LocalDate currentDate = LocalDate.now();
-        // 获取当前系统时间 (HH:mm)
         LocalTime localTime = LocalTime.now();
 
-        List<Long> userIds = param.getUserIds();
-        for (Long userId : userIds) {
+        // 使用线程安全的计数器
+        AtomicInteger suc = new AtomicInteger(0);
 
-            QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalContactById(userId);
-            QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
-            qwEditUserTagParam.setAdd_tag(param.getTagIds());
-            qwEditUserTagParam.setUserid(qwExternalContact.getUserId());
-            qwEditUserTagParam.setExternal_userid(qwExternalContact.getExternalUserId());
+        List<String> failList = new CopyOnWriteArrayList<>(); // 记录失败客户的名字
 
+        // 创建线程池
+        int threadCount = Math.min(8, Runtime.getRuntime().availableProcessors() * 2);
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+
+
+        try {
+            // 使用线程安全的List来收集需要批量更新的数据
+            List<QwExternalContact> batchUpdateList = Collections.synchronizedList(new ArrayList<>());
+
+            // 存储Future对象以便检查所有任务完成情况
+            List<Future<?>> futures = new ArrayList<>();
+
+
+            // 1. 批量查询所有用户数据
+            List<QwExternalContact> contacts;
             try {
+                contacts = qwExternalContactMapper.selectQwExternalContactByIds(param.getUserIds());
+                if (contacts == null || contacts.isEmpty()) {
+                    return R.error("成功:0,失败:" + param.getUserIds().size());
+                }
+            } catch (Exception e) {
+                return R.error("批量查询用户数据失败:"+e.getMessage());
+            }
 
-                QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, param.getCorpId());
-                if (qwResult.getErrcode() == 0) {
+            // 直接遍历contacts而不是userIds
+            for (QwExternalContact qwExternalContact : contacts) {
+                futures.add(executor.submit(() -> {
+                    try {
+                        QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
+                        qwEditUserTagParam.setAdd_tag(param.getTagIds());
+                        qwEditUserTagParam.setUserid(qwExternalContact.getUserId());
+                        qwEditUserTagParam.setExternal_userid(qwExternalContact.getExternalUserId());
+
+                        QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, param.getCorpId());
+                        if (qwResult.getErrcode() == 0) {
+                            // 处理标签
+                            String tagIds = qwExternalContact.getTagIds();
+                            Set<String> uniqueIds = new HashSet<>();
+
+                            if (tagIds != null && !tagIds.isEmpty()) {
+                                List<String> parsedTags = JSON.parseArray(tagIds, String.class);
+                                if (parsedTags != null && !parsedTags.isEmpty()) {
+                                    uniqueIds.addAll(parsedTags);
+                                }
+                            }
 
-                    //客户本身的标签集合+打的标签
-                    QwExternalContact qwExternal = new QwExternalContact();
-                    String tagIds = qwExternalContact.getTagIds();
+                            if (param.getTagIds() != null && !param.getTagIds().isEmpty()) {
+                                uniqueIds.addAll(param.getTagIds());
+                            }
+
+                            QwExternalContact qwExternal = new QwExternalContact();
+                            qwExternal.setTagIds(JSON.toJSONString(uniqueIds));
+                            qwExternal.setId(qwExternalContact.getId());
+
+                            List<String> tagIdsList = new ArrayList<>();
+                            if (qwExternal.getTagIds() != null && !qwExternal.getTagIds().isEmpty()) {
+                                tagIdsList = JSON.parseArray(qwExternal.getTagIds(), String.class);
+                            }
 
-                    // 将标签字符串解析为 List    //客户总标签
-                    List<String> tagIdsList = new ArrayList<>();
+                            logger.info("客户添加标签addUserTag:" + qwExternalContact.getName() +
+                                    "|公司" + qwExternalContact.getCorpId() +
+                                    "|员工" + qwExternalContact.getUserId() +
+                                    "|总标签" + tagIdsList);
 
-                    Set<String> uniqueIds = new HashSet<>();
-                    if (tagIds != null && !tagIds.isEmpty()) {
-                        List<String> parsedTags = JSON.parseArray(tagIds, String.class);
-                        if (parsedTags != null && !parsedTags.isEmpty()) {
-                            uniqueIds.addAll(parsedTags);
+                            // 插件sop处理
+                            processTagsAll(qwExternalContact, qwExternalContact.getCorpId(),
+                                    tagIdsList, currentDate, localTime);
+
+                            // 添加到批量更新列表
+                            batchUpdateList.add(qwExternal);
+                            suc.incrementAndGet();
+                        } else {
+                            failList.add("【"+qwExternalContact.getName()+"】" + "原因:(" + getErrorMsg(qwResult.getErrcode()) + ")\n");
                         }
+                    } catch (Exception e) {
+                        logger.error("客户添加标签失败,userId: " + qwExternalContact.getId() +
+                                ", externalUserId: " + qwExternalContact.getExternalUserId() +
+                                ", 错误信息: " + e.getMessage());
+                        failList.add("【"+qwExternalContact.getName()+"】" + "原因:(" + e.getMessage() + ")\n");
                     }
+                }));
+            }
+
+
+            // 等待所有任务完成
+            for (Future<?> future : futures) {
+                try {
+                    future.get();
+                } catch (InterruptedException | ExecutionException e) {
+                    logger.error("任务执行异常: " + e.getMessage());
+                    Thread.currentThread().interrupt();
+                }
+            }
 
-                    // 将 param 中的标签 ID 加入集合
-                    if (param.getTagIds() != null  && !param.getTagIds().isEmpty()) {
-                        uniqueIds.addAll(param.getTagIds());
+            // 批量更新数据库
+            if (!batchUpdateList.isEmpty()) {
+                try {
+                    // 分批处理,避免单次批量过大
+                    int batchSize = 500; // 根据数据库性能调整
+                    for (int i = 0; i < batchUpdateList.size(); i += batchSize) {
+                        int end = Math.min(i + batchSize, batchUpdateList.size());
+                        List<QwExternalContact> subList = batchUpdateList.subList(i, end);
+                        qwExternalContactMapper.batchUpdateQwExternalContactByTags(subList);
                     }
+                } catch (Exception e) {
+                    logger.error("批量更新失败: " + e.getMessage());
+                }
+            }
 
-                    // 将唯一标签集合设置回 qwExternal
-                    qwExternal.setTagIds(JSON.toJSONString(uniqueIds));
+            // 关闭线程池
+            executor.shutdown();
 
+        }catch (Exception e){
+            failList.add("处理过程中发生异常:"+e.getMessage());
+        }finally {
+            // 7. 确保线程池关闭
+            try {
+                // 先尝试正常关闭
+                executor.shutdown();
+                if (!executor.awaitTermination(15, TimeUnit.SECONDS)) {
+                    // 超时后强制关闭
+                    executor.shutdownNow();
+                    logger.warn("线程池强制关闭");
+                }
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                executor.shutdownNow();
+            }
+        }
 
-                    if (qwExternal.getTagIds() != null && !qwExternal.getTagIds().isEmpty()) {
-                        tagIdsList = JSON.parseArray(qwExternal.getTagIds(), String.class);
-                    }
+        return R.ok("成功:" + suc.get() + ",失败:" + failList);
+    }
+
+
+    private String getErrorMsg(Integer code){
+
+        String msg="";
+        switch (code){
+            case 40003:
+                msg="无效的UserID(员工账号)";
+                break;
+            case 40096:
+                msg="不合法的外部联系人userid";
+                break;
+            case 45033:
+                msg="接口并发调用超过限制(**调用过于频繁请稍后再试**)";
+                break;
+            case 45035:
+                msg="当前客户有其他修改操作,请稍后重试(**比如其他地方修改客户信息**)";
+                break;
+            case 60020:
+                msg="不安全的访问IP";
+                break;
+            case 84061:
+                msg="不存在外部联系人的关系(**客户【不存在】于员工的好友列表中**)";
+                break;
+            default:
+                msg=code.toString();
+                break;
+
+        }
+        return msg;
+    }
+
+    @Override
+    public R addTagByWatch(QwExtContactAddTagByWatchParam param) throws org.codehaus.jettison.json.JSONException {
+
+
+
+        // 获取当前日期和时间
+        LocalDate currentDate = LocalDate.now();
+        LocalTime localTime = LocalTime.now();
+
+        // 使用线程安全的计数器
+        AtomicInteger suc = new AtomicInteger(0);
+
+        List<String> failList = new CopyOnWriteArrayList<>(); // 记录失败客户的名字
 
-                    qwExternal.setId(userId);
+        // 创建线程池
+        int threadCount = Math.min(8, Runtime.getRuntime().availableProcessors() * 2);
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
 
-                    logger.info("客户添加标签addUserTag:"+qwExternalContact.getName()+"|公司"+qwExternalContact.getCorpId()+"|员工"+qwExternalContact.getUserId()+"|总标签"+tagIdsList);
 
-                    //插件sop
-                    processTagsAll(qwExternalContact,qwExternalContact.getCorpId(),tagIdsList,currentDate,localTime);
+        try {
+            // 使用线程安全的List来收集需要批量更新的数据
+            List<QwExternalContact> batchUpdateList = Collections.synchronizedList(new ArrayList<>());
+
+            // 存储Future对象以便检查所有任务完成情况
+            List<Future<?>> futures = new ArrayList<>();
 
-                    //新客对话
-//                    processTagsAllByAiChat(qwExternalContact,param.getCorpId(),tagIdsList);
 
-                    suc++;
-                    qwExternalContactMapper.updateQwExternalContact(qwExternal);
+            // 1. 批量查询所有用户数据
+            List<QwExternalContact> contacts;
+            try {
+                 contacts = fsCourseWatchLogMapper.selectQwWatchLogFomExtContact(param.getLogIds());
+                if (contacts == null || contacts.isEmpty()) {
+                    return R.error("成功:0,失败:" + param.getLogIds().size());
                 }
-                else {
-                    err++;
+            } catch (Exception e) {
+                return R.error("批量查询用户数据失败:"+e.getMessage());
+            }
+
+            // 直接遍历contacts而不是userIds
+            for (QwExternalContact qwExternalContact : contacts) {
+                futures.add(executor.submit(() -> {
+                    try {
+                        QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
+                        qwEditUserTagParam.setAdd_tag(param.getTagIds());
+                        qwEditUserTagParam.setUserid(qwExternalContact.getUserId());
+                        qwEditUserTagParam.setExternal_userid(qwExternalContact.getExternalUserId());
+
+                        QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, qwExternalContact.getCorpId());
+                        if (qwResult.getErrcode() == 0) {
+                            // 处理标签
+                            String tagIds = qwExternalContact.getTagIds();
+                            Set<String> uniqueIds = new HashSet<>();
+
+                            if (tagIds != null && !tagIds.isEmpty()) {
+                                List<String> parsedTags = JSON.parseArray(tagIds, String.class);
+                                if (parsedTags != null && !parsedTags.isEmpty()) {
+                                    uniqueIds.addAll(parsedTags);
+                                }
+                            }
+
+                            if (param.getTagIds() != null && !param.getTagIds().isEmpty()) {
+                                uniqueIds.addAll(param.getTagIds());
+                            }
+
+                            QwExternalContact qwExternal = new QwExternalContact();
+                            qwExternal.setTagIds(JSON.toJSONString(uniqueIds));
+                            qwExternal.setId(qwExternalContact.getId());
+
+                            List<String> tagIdsList = new ArrayList<>();
+                            if (qwExternal.getTagIds() != null && !qwExternal.getTagIds().isEmpty()) {
+                                tagIdsList = JSON.parseArray(qwExternal.getTagIds(), String.class);
+                            }
+
+                            logger.info("看课处客户添加标签addUserTag:" + qwExternalContact.getName() +
+                                    "|公司" + qwExternalContact.getCorpId() +
+                                    "|员工" + qwExternalContact.getUserId() +
+                                    "|总标签" + tagIdsList);
+
+                            // 插件sop处理
+                            processTagsAll(qwExternalContact, qwExternalContact.getCorpId(),
+                                    tagIdsList, currentDate, localTime);
+
+                            // 添加到批量更新列表
+                            batchUpdateList.add(qwExternal);
+                            suc.incrementAndGet();
+                        } else {
+                            failList.add("【"+qwExternalContact.getName()+"】" + "原因:(" + getErrorMsg(qwResult.getErrcode()) + ")\n");
+                        }
+                    } catch (Exception e) {
+                        logger.error("看课处客户添加标签失败,userId: " + qwExternalContact.getId() +
+                                ", externalUserId: " + qwExternalContact.getExternalUserId() +
+                                ", 错误信息: " + e.getMessage());
+                        failList.add("【"+qwExternalContact.getName()+"】" + "原因:(" + e.getMessage() + ")\n");
+                    }
+                }));
+            }
+
+
+            // 等待所有任务完成
+            for (Future<?> future : futures) {
+                try {
+                    future.get();
+                } catch (InterruptedException | ExecutionException e) {
+                    logger.error("任务执行异常: " + e.getMessage());
+                    Thread.currentThread().interrupt();
                 }
-            }catch (Exception e){
-                logger.error("客户添加标签失败:"+qwEditUserTagParam+e.getMessage());
             }
 
+            // 批量更新数据库
+            if (!batchUpdateList.isEmpty()) {
+                try {
+                    // 分批处理,避免单次批量过大
+                    int batchSize = 500; // 根据数据库性能调整
+                    for (int i = 0; i < batchUpdateList.size(); i += batchSize) {
+                        int end = Math.min(i + batchSize, batchUpdateList.size());
+                        List<QwExternalContact> subList = batchUpdateList.subList(i, end);
+                        qwExternalContactMapper.batchUpdateQwExternalContactByTags(subList);
+                    }
+                } catch (Exception e) {
+                    logger.error("批量更新失败: " + e.getMessage());
+                }
+            }
+
+            // 关闭线程池
+            executor.shutdown();
 
+        }catch (Exception e){
+            failList.add("处理过程中发生异常:"+e.getMessage());
+        }finally {
+            // 7. 确保线程池关闭
+            try {
+                // 先尝试正常关闭
+                executor.shutdown();
+                if (!executor.awaitTermination(15, TimeUnit.SECONDS)) {
+                    // 超时后强制关闭
+                    executor.shutdownNow();
+                    logger.warn("线程池强制关闭");
+                }
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                executor.shutdownNow();
+            }
         }
 
-        return R.ok("成功:" + suc + ",失败:" + err);
+        return R.ok("成功:" + suc.get() + ",失败:" + failList);
+
     }
 
 
+
     @Override
     public R delUserTag(QwExternalContactAddTagParam param) {
-
-        // 获取当前日期(只包含年月日)
+        // 获取当前日期和时间
         LocalDate currentDate = LocalDate.now();
-        // 获取当前系统时间 (HH:mm)
         LocalTime localTime = LocalTime.now();
 
-        int err = 0;
-        int suc = 0;
-        List<Long> userIds = param.getUserIds();
-        for (Long userId : userIds) {
+        // 使用线程安全的计数器
+        AtomicInteger suc = new AtomicInteger(0);
+
+        List<String> failList = new CopyOnWriteArrayList<>(); // 记录失败客户的名字
+
+        // 创建线程池
+        int threadCount = Math.min(8, Runtime.getRuntime().availableProcessors() * 2);
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+
+
+        try {
+            // 使用线程安全的List来收集需要批量更新的数据
+            List<QwExternalContact> batchUpdateList = Collections.synchronizedList(new ArrayList<>());
 
-            QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalContactById(userId);
+            // 存储Future对象以便检查所有任务完成情况
+            List<Future<?>> futures = new ArrayList<>();
 
-            QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
-            qwEditUserTagParam.setRemove_tag(param.getTagIds());
-            qwEditUserTagParam.setUserid(qwExternalContact.getUserId());
-            qwEditUserTagParam.setExternal_userid(qwExternalContact.getExternalUserId());
 
+            // 1. 批量查询所有用户数据
+            List<QwExternalContact> contacts;
             try {
-                QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, param.getCorpId());
-                if (qwResult.getErrcode() == 0) {
-
-                    String tagIds = qwExternalContact.getTagIds();
-                    if (tagIds != null && tagIds != "") {
-                        List<String> ids = JSON.parseArray(tagIds, String.class);
-                        for (String tagId : param.getTagIds()) {
-                            ids.removeIf(str -> str.equals(tagId));
+                contacts = qwExternalContactMapper.selectQwExternalContactByIds(param.getUserIds());
+                if (contacts == null || contacts.isEmpty()) {
+                    return R.error("成功:0,失败:" + param.getUserIds().size());
+                }
+            } catch (Exception e) {
+                return R.error("批量查询用户数据失败:"+e.getMessage());
+            }
+
+            // 直接遍历contacts而不是userIds
+            for (QwExternalContact qwExternalContact : contacts) {
+                futures.add(executor.submit(() -> {
+                    try {
+                        QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
+                        qwEditUserTagParam.setRemove_tag(param.getTagIds());
+                        qwEditUserTagParam.setUserid(qwExternalContact.getUserId());
+                        qwEditUserTagParam.setExternal_userid(qwExternalContact.getExternalUserId());
+
+                        QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, param.getCorpId());
+                        if (qwResult.getErrcode() == 0) {
+
+                            String tagIds = qwExternalContact.getTagIds();
+                            if (tagIds != null && tagIds != "") {
+                                List<String> ids = JSON.parseArray(tagIds, String.class);
+                                for (String tagId : param.getTagIds()) {
+                                    ids.removeIf(str -> str.equals(tagId));
+                                }
+
+                                QwExternalContact qwExternal = new QwExternalContact();
+                                qwExternal.setId(qwExternalContact.getId());
+                                qwExternal.setTagIds(JSON.toJSONString(ids));
+
+
+                                logger.info("客户移除标签delUserTag:"+qwExternalContact.getName()+"|公司"+qwExternalContact.getCorpId()+"|员工"+qwExternalContact.getUserId()+"|总标签"+ids);
+
+                                //检查sop
+                                processTagsAll(qwExternalContact,param.getCorpId(),ids,currentDate,localTime);
+
+                                // 添加到批量更新列表
+                                batchUpdateList.add(qwExternal);
+                            }
+
+                            suc.incrementAndGet();
+                        } else {
+                            failList.add("【"+qwExternalContact.getName()+"】" + "原因:(" + getErrorMsg(qwResult.getErrcode()) + ")\n");
                         }
+                    } catch (Exception e) {
+                        logger.error("客户移除标签delUserTag失败,userId: " + qwExternalContact.getId() +
+                                ", externalUserId: " + qwExternalContact.getExternalUserId() +
+                                ", 错误信息: " + e.getMessage());
+                        failList.add("【"+qwExternalContact.getName()+"】" + "原因:(" + e.getMessage() + ")\n");
+                    }
+                }));
+            }
+
+
+            // 等待所有任务完成
+            for (Future<?> future : futures) {
+                try {
+                    future.get();
+                } catch (InterruptedException | ExecutionException e) {
+                    logger.error("任务执行异常: " + e.getMessage());
+                    Thread.currentThread().interrupt();
+                }
+            }
+
+            // 批量更新数据库
+            if (!batchUpdateList.isEmpty()) {
+                try {
+                    // 分批处理,避免单次批量过大
+                    int batchSize = 500; // 根据数据库性能调整
+                    for (int i = 0; i < batchUpdateList.size(); i += batchSize) {
+                        int end = Math.min(i + batchSize, batchUpdateList.size());
+                        List<QwExternalContact> subList = batchUpdateList.subList(i, end);
+                        qwExternalContactMapper.batchUpdateQwExternalContactByTags(subList);
+                    }
+                } catch (Exception e) {
+                    logger.error("批量更新失败: " + e.getMessage());
+                }
+            }
+
+            // 关闭线程池
+            executor.shutdown();
+
+        }catch (Exception e){
+            failList.add("处理过程中发生异常:"+e.getMessage());
+        }finally {
+
+            // 7. 确保线程池关闭
+            try {
+                // 先尝试正常关闭
+                executor.shutdown();
+                if (!executor.awaitTermination(15, TimeUnit.SECONDS)) {
+                    // 超时后强制关闭
+                    executor.shutdownNow();
+                    logger.warn("线程池强制关闭");
+                }
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                executor.shutdownNow();
+            }
+        }
+
+
+        return R.ok("成功:" + suc.get() + ",失败:" + failList);
+    }
+
+    @Override
+    public R delTagByWatch(QwExtContactAddTagByWatchParam param) {
+        // 获取当前日期和时间
+        LocalDate currentDate = LocalDate.now();
+        LocalTime localTime = LocalTime.now();
+
+        // 使用线程安全的计数器
+        AtomicInteger suc = new AtomicInteger(0);
+
+        List<String> failList = new CopyOnWriteArrayList<>(); // 记录失败客户的名字
+
+        // 创建线程池
+        int threadCount = Math.min(8, Runtime.getRuntime().availableProcessors() * 2);
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+
 
-                        QwExternalContact qwExternal = new QwExternalContact();
-                        qwExternal.setId(userId);
-                        qwExternal.setTagIds(JSON.toJSONString(ids));
+        try {
+            // 使用线程安全的List来收集需要批量更新的数据
+            List<QwExternalContact> batchUpdateList = Collections.synchronizedList(new ArrayList<>());
 
-                        qwExternalContactMapper.updateQwExternalContact(qwExternal);
+            // 存储Future对象以便检查所有任务完成情况
+            List<Future<?>> futures = new ArrayList<>();
 
-                        logger.info("客户移除标签delUserTag:"+qwExternalContact.getName()+"|公司"+qwExternalContact.getCorpId()+"|员工"+qwExternalContact.getUserId()+"|总标签"+ids);
 
-                        //检查sop
-                        processTagsAll(qwExternalContact,param.getCorpId(),ids,currentDate,localTime);
+            // 1. 批量查询所有用户数据
+            List<QwExternalContact> contacts;
+            try {
+                contacts = fsCourseWatchLogMapper.selectQwWatchLogFomExtContact(param.getLogIds());
+                if (contacts == null || contacts.isEmpty()) {
+                    return R.error("成功:0,失败:" + param.getLogIds().size());
+                }
+            } catch (Exception e) {
+                return R.error("批量查询用户数据失败:"+e.getMessage());
+            }
 
-                        //新客对话
-//                        processTagsAllByAiChat(qwExternalContact,param.getCorpId(),ids);
+            // 直接遍历contacts而不是userIds
+            for (QwExternalContact qwExternalContact : contacts) {
+                futures.add(executor.submit(() -> {
+                    try {
+                        QwEditUserTagParam qwEditUserTagParam = new QwEditUserTagParam();
+                        qwEditUserTagParam.setRemove_tag(param.getTagIds());
+                        qwEditUserTagParam.setUserid(qwExternalContact.getUserId());
+                        qwEditUserTagParam.setExternal_userid(qwExternalContact.getExternalUserId());
+
+                        QwResult qwResult = qwApiService.editUserTag(qwEditUserTagParam, qwExternalContact.getCorpId());
+                        if (qwResult.getErrcode() == 0) {
+
+                            String tagIds = qwExternalContact.getTagIds();
+                            if (tagIds != null && tagIds != "") {
+                                List<String> ids = JSON.parseArray(tagIds, String.class);
+                                for (String tagId : param.getTagIds()) {
+                                    ids.removeIf(str -> str.equals(tagId));
+                                }
+
+                                QwExternalContact qwExternal = new QwExternalContact();
+                                qwExternal.setId(qwExternalContact.getId());
+                                qwExternal.setTagIds(JSON.toJSONString(ids));
+
+
+                                logger.info("看课处客户移除标签delUserTag:"+qwExternalContact.getName()+"|公司"+qwExternalContact.getCorpId()+"|员工"+qwExternalContact.getUserId()+"|总标签"+ids);
+
+                                //检查sop
+                                processTagsAll(qwExternalContact,qwExternalContact.getCorpId(),ids,currentDate,localTime);
+
+                                // 添加到批量更新列表
+                                batchUpdateList.add(qwExternal);
+                            }
+
+                            suc.incrementAndGet();
+                        } else {
+                            failList.add("【"+qwExternalContact.getName()+"】" + "原因:(" + getErrorMsg(qwResult.getErrcode()) + ")\n");
+                        }
+                    } catch (Exception e) {
+                        logger.error("看课处客户移除标签delUserTag失败,userId: " + qwExternalContact.getId() +
+                                ", externalUserId: " + qwExternalContact.getExternalUserId() +
+                                ", 错误信息: " + e.getMessage());
+                        failList.add("【"+qwExternalContact.getName()+"】" + "原因:(" + e.getMessage() + ")\n");
                     }
-                    suc++;
+                }));
+            }
 
-                } else {
-                    err++;
+
+            // 等待所有任务完成
+            for (Future<?> future : futures) {
+                try {
+                    future.get();
+                } catch (InterruptedException | ExecutionException e) {
+                    logger.error("任务执行异常: " + e.getMessage());
+                    Thread.currentThread().interrupt();
                 }
+            }
 
-            }catch (Exception e){
-                logger.error("移除标签失败:"+qwEditUserTagParam+e.getMessage());
+            // 批量更新数据库
+            if (!batchUpdateList.isEmpty()) {
+                try {
+                    // 分批处理,避免单次批量过大
+                    int batchSize = 500; // 根据数据库性能调整
+                    for (int i = 0; i < batchUpdateList.size(); i += batchSize) {
+                        int end = Math.min(i + batchSize, batchUpdateList.size());
+                        List<QwExternalContact> subList = batchUpdateList.subList(i, end);
+                        qwExternalContactMapper.batchUpdateQwExternalContactByTags(subList);
+                    }
+                } catch (Exception e) {
+                    logger.error("批量更新失败: " + e.getMessage());
+                }
             }
 
+            // 关闭线程池
+            executor.shutdown();
 
+        }catch (Exception e){
+            failList.add("处理过程中发生异常:"+e.getMessage());
+        }finally {
+
+            // 7. 确保线程池关闭
+            try {
+                // 先尝试正常关闭
+                executor.shutdown();
+                if (!executor.awaitTermination(15, TimeUnit.SECONDS)) {
+                    // 超时后强制关闭
+                    executor.shutdownNow();
+                    logger.warn("线程池强制关闭");
+                }
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                executor.shutdownNow();
+            }
         }
 
 
-        return R.ok("成功:" + suc + ",失败:" + err);
+        return R.ok("成功:" + suc.get() + ",失败:" + failList);
     }
 
     public void insertQwExternalByMq(Long ExtId){
@@ -2796,8 +3245,18 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
             if (i == 1) {
                 return;
             }
-            // 判断 SOP 任务的开始时间
-            qwSopRuleTimeToolsCheck(sopStartLocalDate,currentDate,dateFormatter,userLogs,logsInfo,userLogsParamByDate,qwAutoSopTimeParam,timeFormatter,localTime);
+
+            if (Integer.valueOf(1).equals(ruleTimeVO.getIsFixed())){
+
+                qwSopRuleTimeToolsCheckByIsFixed(sopStartLocalDate,dateFormatter,userLogs,logsInfo,userLogsParamByDate);
+
+            }else {
+                // 判断 SOP 任务的开始时间
+                qwSopRuleTimeToolsCheck(sopStartLocalDate,currentDate,dateFormatter,userLogs,logsInfo,userLogsParamByDate,qwAutoSopTimeParam,timeFormatter,localTime);
+
+            }
+
+
         });
     }
 
@@ -3407,8 +3866,17 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
                             logsInfo.setExternalUserName(qwExternalContact.getName());
                             logsInfo.setSopId(ruleTimeVO.getId());
 
-                            //检查营期
-                            qwSopRuleTimeToolsCheck(sopStartLocalDate,currentDate,dateFormatter,userLogs,logsInfo,userLogsParamByDate,qwAutoSopTimeParam,timeFormatter,localTime);
+
+                            //如果是固定营期
+                            if (Integer.valueOf(1).equals(ruleTimeVO.getIsFixed())){
+
+                                qwSopRuleTimeToolsCheckByIsFixed(sopStartLocalDate,dateFormatter,userLogs,logsInfo,userLogsParamByDate);
+
+                            }else {
+                                //检查营期
+                                qwSopRuleTimeToolsCheck(sopStartLocalDate,currentDate,dateFormatter,userLogs,logsInfo,userLogsParamByDate,qwAutoSopTimeParam,timeFormatter,localTime);
+                            }
+
 
                         }
 
@@ -3423,30 +3891,6 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
                         });
                     }
 
-//                    if (!runRuleTime.isEmpty() && !qwSopRuleTimeVOS.isEmpty()){
-//
-//                        //剔除掉 销售的其他任务里的,这个人的信息ps:可能没有
-//                        Set<String> qwSopRuleTimeVOIds = qwSopRuleTimeVOS.stream()
-//                                .map(QwSopRuleTimeVO::getId)
-//                                .filter(Objects::nonNull)       // 过滤掉 null
-//                                .map(String::trim)              // 去除前后空格
-//                                .collect(Collectors.toSet());
-//
-//                        if (!qwSopRuleTimeVOIds.isEmpty()){
-//
-//                            //在 qwSopRuleTimeVOS 中过滤掉包含在 runRuleTimeIds 中的 id
-//                            List<QwSopRuleTimeVO> notOverlapList = runRuleTime.stream()
-//                                    .filter(item -> item.getId() != null &&!qwSopRuleTimeVOIds.contains(item.getId().trim()))
-//                                    .collect(Collectors.toList());
-//
-//                            notOverlapList.forEach(notSop->{
-//                                logger.info("删除这个客户在这个销售的其他不符合的sop"+notSop.getId()+"标签:"+tagArr+"id:"+qwExternalContact.getQwUserId()+"排除的标签:"+qwSopRuleTimeVOIds);
-//
-//                                deleteBySopIdToContactIdTools(notSop.getId(),qwExternalContact.getUserId(),qwExternalContact.getCorpId(),qwExternalContact.getId());
-//
-//                            });
-//                        }
-//                    }
                 }else {
                     //没匹配上任意一个(即这个剩下的标签匹配不上任意的sop),但是销售有任务-以防万一-删除这个客户在这个销售的sop
                     if (!runRuleTime.isEmpty()){
@@ -3466,6 +3910,38 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
 
     };
 
+
+    //固定营期  以 SOP 任务的开始时间为营期
+    private void qwSopRuleTimeToolsCheckByIsFixed(LocalDate sopStartLocalDate,DateTimeFormatter dateFormatter,
+                                                  SopUserLogs userLogs,SopUserLogsInfo logsInfo,
+                                                  SopUserLogsParamByDate userLogsParamByDate){
+
+        String sopStartLocalDateStr = sopStartLocalDate.format(dateFormatter);
+
+        userLogs.setStartTime(sopStartLocalDateStr);
+        // 查询开始时间营期表
+        String unionSopStartId = sopUserLogsService.selectSopUserLogsByUnionSopId(userLogs);
+
+        // 如果营期表里有,则加入;否则创建新营期
+        if (!StringUtil.strIsNullOrEmpty(unionSopStartId)) {
+            logsInfo.setUserLogsId(unionSopStartId);
+            // 查询这个人是否已在该营期里
+            handleInsertSopUserLogsInfo(logsInfo);
+        }
+        else {
+            // 没有营期记录就先插入
+            userLogsParamByDate.setStartTime(sopStartLocalDateStr);
+
+            sopUserLogsService.insertSopUserLogsByDate(userLogsParamByDate);
+
+            // 再次查询 拿营期主键
+            String unionSopStartIdNew = sopUserLogsService.selectSopUserLogsByUnionSopId(userLogs);
+            logsInfo.setUserLogsId(unionSopStartIdNew);
+
+            // 查询是否在营期里,如果没有再插入
+            handleInsertSopUserLogsInfo(logsInfo);
+        }
+    }
     /**
      * 新客对话
      */

+ 13 - 0
fs-service/src/main/java/com/fs/qw/vo/QwSopTempSetting2.java

@@ -113,6 +113,19 @@ public class QwSopTempSetting2 implements Serializable{
             //链接过期时间
             private String expiresDays;
 
+            /** 小程序appid */
+            private String miniprogramAppid;
+
+            /** 小程序消息标题 */
+            private String miniprogramTitle;
+
+            /** 小程序封面地址 */
+            private String miniprogramPicUrl;
+
+
+            /** 小程序page路径 */
+            private String miniprogramPage;
+
             @Override
             public Setting clone() {
                 try {

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

@@ -64,7 +64,8 @@ public class QwSopTemp implements Serializable
     private String createBy;
     private Long project;
     private Long courseId;
-
+    @TableField(exist = false)
+    private String modeContent;
     @TableField(exist = false)
     private List<Map<String, Object>> rules;
     @TableField(exist = false)

+ 4 - 0
fs-service/src/main/java/com/fs/sop/params/SendSopParamDetailsC.java

@@ -14,5 +14,9 @@ public class SendSopParamDetailsC {
     private Long nextExternalId;//下一个外部联系人ID
     private String appKey;
     private int type;
+    /**
+     * 延时时间
+     */
+    private Long sendDelayTime;
 
 }

+ 25 - 1
fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java

@@ -2,11 +2,14 @@ package com.fs.sop.service.impl;
 
 
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.CompanyUser;
+import com.fs.company.domain.CompanyUserDelayTime;
+import com.fs.company.mapper.CompanyUserDelayTimeMapper;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseWatchLog;
@@ -72,6 +75,8 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
     private RedisCache redisCache;
     @Autowired
     private QwSopLogsMapper qwSopLogsMapper;
+    @Autowired
+    private CompanyUserDelayTimeMapper companyUserDelayTimeMapper;
 
     @Autowired
     private ISysConfigService configService;
@@ -738,6 +743,7 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
 
         // 封装消息体
         SendSopParamDetailsC sopParamDetailsC = new SendSopParamDetailsC();
+        sendDelayTime(sopParamDetailsC,param);
         // 使用线程安全的集合
         List<QwSopLogsDoSendListTVO> sendJsApiList = Collections.synchronizedList(new ArrayList<>());
         try {
@@ -973,6 +979,24 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
         return sopParamDetailsC;
     }
 
+    /**
+     * 查询是否存在自定义延时时间并赋值
+     *
+     * @param sopParamDetailsC
+     * @param param
+     */
+    private void sendDelayTime(SendSopParamDetailsC sopParamDetailsC, GetQwSopLogsByJsApiParam param) {
+        QwUser qwUser  = qwUserMapper.selectQwUserByQwUserId(param.getQwUserId());
+        CompanyUserDelayTime companyUserDelayTime = companyUserDelayTimeMapper.selectUserDelayTimeByCompanyUserId(qwUser.getCompanyUserId(),qwUser.getCompanyId());
+        if (ObjectUtil.isEmpty(companyUserDelayTime)){
+            String json = configService.selectConfigByKey("course.config");
+            CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+            sopParamDetailsC.setSendDelayTime(config.getSendDelayTime());
+        }else {
+            sopParamDetailsC.setSendDelayTime(companyUserDelayTime.getSendDelayTime());
+        }
+    }
+
     // 逻辑封装方法
     private boolean isCourseTypeValid(int courseType, Integer logType) {
         switch (courseType) {
@@ -1453,7 +1477,7 @@ public class QwSopLogsServiceImpl implements IQwSopLogsService
                                                         updateList.add(log);
                                                     }
 
-                                                    logger.error("企业微信接口-消息发送失败-进入sop补偿,corpId:{},errCode:{},errMsg:{},key:{}", corpId, result.getErrCode(), result.getErrMsg(),key);
+                                                    logger.error("企业微信接口-消息发送失败-进入sop补偿,corpId:{},errCode:{},errMsg:{},key:{},{}", corpId, result.getErrCode(), result.getErrMsg(),key,externalUserId);
                                                 }
                                             } catch (Exception e) {
                                                 logger.error("消息发送失败,user:{},{},{}", externalUserId, e,key);

+ 28 - 11
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -30,6 +30,7 @@ import com.fs.sop.vo.QwSopTempRedPackageVo;
 import com.fs.sop.vo.VoiceVo;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
+import io.netty.util.internal.StringUtil;
 import lombok.AllArgsConstructor;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
@@ -388,33 +389,49 @@ public class QwSopTempServiceImpl implements IQwSopTempService
                 QwSopTempRules rules = new QwSopTempRules();
                 rules.setTempId(temp.getId());
                 rules.setName(day.getName());
+                rules.setIsOfficial(sorts.get() == 0 ? "1" : "0");
                 rules.setTime(time);
                 rules.setContentType(2);
                 rules.setType(2);
                 rules.setCourseType(0);
                 rules.setCourseId(e.getCourseId());
                 rules.setVideoId(e.getVideoId());
-                rules.setSorts(sorts.getAndIncrement());
                 // 设置消息类型
-                if(rules.getSorts() == 0){
-                    rules.setCourseType(0);
-                }else if(rules.getSorts() == 1){
-                    rules.setCourseType(1);
-                }else{
-                    rules.setCourseType(4);
-                }
+
                 QwSopTempContent content = new QwSopTempContent();
                 content.setTempId(temp.getId());
                 content.setContentType(3);
                 QwSopTempSetting2.Content.Setting setting = new QwSopTempSetting2.Content.Setting();
-                setting.setLinkTitle(fsUserCourse.getCourseName());
+                setting.setLinkTitle(e.getTitle());
+                setting.setMiniprogramTitle(e.getTitle());
+                setting.setMiniprogramPicUrl(fsUserCourse.getImgUrl());
                 setting.setIsBindUrl(1);
                 setting.setLinkDescribe(e.getTitle());
                 setting.setLinkImageUrl(fsUserCourse.getImgUrl());
-                setting.setContentType("3");
+                setting.setContentType("4");
                 content.setContent(JSON.toJSONString(setting));
                 content.setIsBindUrl(1);
-                rules.setList(Collections.singletonList(content));
+                List<QwSopTempContent> qwSopTempContents = new ArrayList<>() ;
+                qwSopTempContents.add(content);
+                if (sorts.get() == 0 && !StringUtil.isNullOrEmpty(temp.getModeContent())) {
+                    QwSopTempContent content2 = new QwSopTempContent();
+                    content2.setTempId(temp.getId());
+                    content2.setContentType(3);
+                    QwSopTempSetting2.Content.Setting setting2 = new QwSopTempSetting2.Content.Setting();
+                    setting2.setValue(temp.getModeContent());
+                    setting2.setContentType("1");
+                    content2.setContent(JSON.toJSONString(setting2));
+                    qwSopTempContents.add(content2);
+                }
+                rules.setSorts(sorts.getAndIncrement());
+                if(rules.getSorts() == 0){
+                    rules.setCourseType(0);
+                }else if(rules.getSorts() == 1){
+                    rules.setCourseType(1);
+                }else{
+                    rules.setCourseType(4);
+                }
+                rules.setList(qwSopTempContents);
                 return rules;
             }).collect(Collectors.toList());
             day.getList().addAll(rulesList);

+ 13 - 0
fs-service/src/main/java/com/fs/store/config/CompanyMenuConfig.java

@@ -0,0 +1,13 @@
+package com.fs.store.config;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class CompanyMenuConfig implements Serializable {
+
+    private Long [] menuIds;
+
+
+}

+ 7 - 7
fs-service/src/main/resources/application-config-druid-lmjy.yml

@@ -26,12 +26,13 @@ wx:
   mp:
     useRedis: false
     redisConfig:
-      host: 127.0.0.1
+      host: 192.168.0.161
       port: 6379
       timeout: 2000
+      password: Ylrztek250218!3@.
     configs:
-      - appId: wx5d3096e20e4bd8bb # 第一个公众号的appid  //公众号名称:成都九州在线互联网医院
-        secret: 1afa05f0c71beff0d52fb849c62e479a # 公众号的appsecret
+      - appId: wxe0b82a0018449a62 # 第一个公众号的appid  //公众号名称:成都九州在线互联网医院
+        secret: 9d55faa03111d3c352d99f45dd4be3eb # 公众号的appsecret
         token: PPKOdAlCoMO # 接口配置里的Token值
         aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
 aifabu:  #爱链接
@@ -48,9 +49,8 @@ watch:
   password3: v9xsKuqn_$d2y
 
 fs :
-  commonApi: http://172.27.0.7:8010
-#  commonApi: http://127.0.0.1:8010
-  h5CommonApi: http://119.29.195.254:8010
+  commonApi: http://127.0.0.1:8010
+  h5CommonApi: http://127.0.0.1:8010
 nuonuo:
   key:
   secret:
@@ -68,7 +68,7 @@ cloud_host:
   company_name: 良苗教育
 #看课授权时显示的头像
 headerImg:
-  imgUrl:
+  imgUrl: https://liangmiao.obs.cn-southwest-2.myhuaweicloud.com/fs/20250626/1750922536598.png
 ipad:
   ipadUrl:
 

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

@@ -60,8 +60,8 @@ watch:
   password3: v9xsKuqn_$d2y
 
 fs :
-  commonApi: http://172.16.0.16:8010
-  h5CommonApi: http://119.29.195.254:8010
+  commonApi: http://10.206.0.16:8010
+  h5CommonApi: http://10.206.0.16:8010
 nuonuo:
   key: 10924508
   secret: A2EB20764D304D16

+ 1 - 1
fs-service/src/main/resources/application-config-druid-yzt.yml

@@ -42,7 +42,7 @@ wx:
       port: 6379
       timeout: 2000
     configs:
-      - appId: wx5b979cb3448d6fa5 # 第一个公众号的appid  //公众号名称:成都九州在线互联网医院
+      - appId: wx5b979cb3448d6fa5 # 第一个公众号的appid
         secret: c66f99034e06e4ae1f857895e3868bb0 # 公众号的appsecret
         token: PPKOdAlCoMO # 接口配置里的Token值
         aesKey: Eswa6VjwtVcw03qZy6Wllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值

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

@@ -0,0 +1,150 @@
+# 数据源配置
+spring:
+    profiles:
+        include: config-druid-jnmy,common
+    # redis 配置
+    redis:
+        # 地址
+        host: 127.0.0.1
+        # 端口,默认为6379
+        port: 6379
+        # 数据库索引
+        database: 0
+        # 密码
+        password:
+        # 连接超时时间
+        timeout: 20s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        #        clickhouse:
+        #            type: com.alibaba.druid.pool.DruidDataSource
+        #            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+        #            url: jdbc:clickhouse://cc-2vc8zzo26w0l7m2l6.public.clickhouse.ads.aliyuncs.com/sop?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+        #            username: rt_2024
+        #            password: Yzx_19860213
+        #            initialSize: 10
+        #            maxActive: 100
+        #            minIdle: 10
+        #            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://120.46.174.121:2345/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://120.46.174.121:2345/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 151 - 0
fs-service/src/main/resources/application-druid-jzzx-test.yml

@@ -0,0 +1,151 @@
+# 数据源配置
+spring:
+    profiles:
+        include: config-druid-jzzx,common
+    # redis 配置
+    redis:
+        # 地址
+        host: 127.0.0.1
+        # 端口,默认为6379
+        port: 6379
+        # 数据库索引
+        database: 0
+        # 密码
+        password:
+#        password: Ylrztek250218!3@.
+        # 连接超时时间
+        timeout: 20s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        #        clickhouse:
+        #            type: com.alibaba.druid.pool.DruidDataSource
+        #            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+        #            url: jdbc:clickhouse://cc-2vc8zzo26w0l7m2l6.public.clickhouse.ads.aliyuncs.com/sop?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+        #            username: rt_2024
+        #            password: Yzx_19860213
+        #            initialSize: 10
+        #            maxActive: 100
+        #            minIdle: 10
+        #            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://1.95.34.221:2345/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://1.95.34.221:2345/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 150 - 0
fs-service/src/main/resources/application-druid-lmjy-test.yml

@@ -0,0 +1,150 @@
+# 数据源配置
+spring:
+    profiles:
+        include: config-druid-lmjy,common
+    # redis 配置
+    redis:
+        # 地址
+        host: 127.0.0.1
+        # 端口,默认为6379
+        port: 6379
+        # 数据库索引
+        database: 0
+        # 密码
+        password:
+        # 连接超时时间
+        timeout: 20s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        #        clickhouse:
+        #            type: com.alibaba.druid.pool.DruidDataSource
+        #            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+        #            url: jdbc:clickhouse://cc-2vc8zzo26w0l7m2l6.public.clickhouse.ads.aliyuncs.com/sop?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+        #            username: rt_2024
+        #            password: Yzx_19860213
+        #            initialSize: 10
+        #            maxActive: 100
+        #            minIdle: 10
+        #            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://1.95.62.166:2345/lm_scrm?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://1.95.62.166:2345/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrztek250218!3@.
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: a # 替换为实际的 accessKey
+        secret-key: k # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: l # 替换为实际的 accessKey
+        secret-key: l # 替换为实际的 secretKey

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

@@ -5,13 +5,13 @@ spring:
     # redis 配置
     redis:
         # 地址
-        host: 127.0.0.1
+        host: 192.168.0.161
         # 端口,默认为6379
         port: 6379
         # 数据库索引
         database: 0
         # 密码
-        password:
+        password: Ylrztek250218!3@.
         # 连接超时时间
         timeout: 20s
         lettuce:

+ 103 - 0
fs-service/src/main/resources/mapper/company/CompanyUserDelayTimeMapper.xml

@@ -0,0 +1,103 @@
+<?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.company.mapper.CompanyUserDelayTimeMapper">
+
+    <resultMap type="CompanyUserDelayTime" id="CompanyUserDelayTimeResult">
+        <result property="id"    column="id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="sendDelayTime"    column="send_delay_time"    />
+        <result property="nickName"    column="nick_name"    />
+        <result property="delFlag"    column="del_flag"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="remark"    column="remark"    />
+    </resultMap>
+
+    <sql id="selectCompanyUserDelayTimeVo">
+        select id, company_id, company_user_id, send_delay_time, nick_name, del_flag, create_by, create_time, update_by, update_time, remark from company_user_delay_time
+    </sql>
+
+    <select id="selectCompanyUserDelayTimeList" parameterType="CompanyUserDelayTime" resultMap="CompanyUserDelayTimeResult">
+        <include refid="selectCompanyUserDelayTimeVo"/>
+        <where>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
+            <if test="sendDelayTime != null "> and send_delay_time = #{sendDelayTime}</if>
+            <if test="nickName != null  and nickName != ''"> and nick_name like concat('%', #{nickName}, '%')</if>
+        </where>
+    </select>
+
+    <select id="selectCompanyUserDelayTimeById" parameterType="Long" resultMap="CompanyUserDelayTimeResult">
+        <include refid="selectCompanyUserDelayTimeVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertCompanyUserDelayTime" parameterType="CompanyUserDelayTime">
+        insert into company_user_delay_time
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">company_id,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="sendDelayTime != null">send_delay_time,</if>
+            <if test="nickName != null">nick_name,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="remark != null">remark,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="sendDelayTime != null">#{sendDelayTime},</if>
+            <if test="nickName != null">#{nickName},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="remark != null">#{remark},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCompanyUserDelayTime" parameterType="CompanyUserDelayTime">
+        update company_user_delay_time
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="sendDelayTime != null">send_delay_time = #{sendDelayTime},</if>
+            <if test="nickName != null">nick_name = #{nickName},</if>
+            <if test="delFlag != null">del_flag = #{delFlag},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="remark != null">remark = #{remark},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCompanyUserDelayTimeById" parameterType="Long">
+        delete from company_user_delay_time where id = #{id}
+    </delete>
+
+    <delete id="deleteCompanyUserDelayTimeByIds" parameterType="String">
+        delete from company_user_delay_time where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="selectUserDelayTimeByCompanyUserId"  resultMap="CompanyUserDelayTimeResult">
+        <include refid="selectCompanyUserDelayTimeVo"/>
+        <where>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
+        </where>
+    </select>
+</mapper>

+ 26 - 2
fs-service/src/main/resources/mapper/course/FsUserCoursePeriodDaysMapper.xml

@@ -88,6 +88,10 @@
         <trim prefix="SET" suffixOverrides=",">
             <if test="periodId != null">period_id = #{periodId},</if>
             <if test="lesson != null">lesson = #{lesson},</if>
+            <if test="startDateTime != null">start_date_time = #{startDateTime},</if>
+            <if test="endDateTime != null">end_date_time = #{endDateTime},</if>
+            <if test="lastJoinTime != null">last_join_time = #{lastJoinTime},</if>
+            <if test="dayDate != null">day_date = #{dayDate},</if>
             <if test="startTime != null">start_time = #{startTime},</if>
             <if test="endTime != null">end_time = #{endTime},</if>
             <if test="courseId != null">course_id = #{courseId},</if>
@@ -142,16 +146,35 @@
         <trim prefix="set" suffixOverrides=",">
             day_date =
             <foreach collection="list" item="item" open="case" close=" end,">
-                <if test="item.dayDate !=null ">
+                <if test="item.dayDate != null">
                     when id=#{item.id} then #{item.dayDate}
                 </if>
             </foreach>
+            start_date_time =
+            <foreach collection="list" item="item" open="case" close=" end,">
+                <if test="item.startDateTime != null">
+                    when id=#{item.id} then #{item.startDateTime}
+                </if>
+            </foreach>
+            end_date_time =
+            <foreach collection="list" item="item" open="case" close=" end,">
+                <if test="item.endDateTime != null">
+                    when id=#{item.id} then #{item.endDateTime}
+                </if>
+            </foreach>
+            last_join_time =
+            <foreach collection="list" item="item" open="case" close=" end,">
+                <if test="item.lastJoinTime != null">
+                    when id=#{item.id} then #{item.lastJoinTime}
+                </if>
+            </foreach>
             lesson =
             <foreach collection="list" item="item" open="case" close=" end,">
-                <if test="item.lesson !=null ">
+                <if test="item.lesson != null">
                     when id=#{item.id} then #{item.lesson}
                 </if>
             </foreach>
+
         </trim>
         where id in
         <foreach collection="list" index="index" item="item" separator="," open="(" close=")">
@@ -159,6 +182,7 @@
         </foreach>
     </update>
 
+
     <update id="startPeriodCourse">
     <![CDATA[
         update fs_user_course_period_days ucpd

+ 8 - 7
fs-service/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml

@@ -29,6 +29,7 @@
     <select id="selectFsUserCoursePeriodList" parameterType="FsUserCoursePeriod" resultMap="FsUserCoursePeriodResult">
         <include refid="selectFsUserCoursePeriodVo"/>
         <where>
+            del_flag ='0'
             <if test="periodName != null  and periodName != ''"> and period_name like concat('%', #{periodName}, '%')</if>
             <if test="companyId != null "> and FIND_IN_SET (#{companyId}, company_id ) > 0</if>
             <if test="trainingCampId != null "> and training_camp_id = #{trainingCampId}</if>
@@ -64,6 +65,7 @@
         left join fs_user_course_training_camp fctc on fctc.training_camp_id = fs_user_course_period.training_camp_id
         left join company on FIND_IN_SET(company.company_id, fs_user_course_period.company_id) > 0
         <where>
+            fs_user_course_period.del_flag ='0'
             <if test="trainingCampId != null"> and fs_user_course_period.training_camp_id = #{trainingCampId}</if>
             <if test="periodName != null  and periodName != ''"> and period_name like concat('%', #{periodName}, '%')</if>
             <if test="companyIdList != null and companyIdList.size() > 0 ">
@@ -82,7 +84,7 @@
 
     <select id="selectFsUserCoursePeriodById" parameterType="Long" resultMap="FsUserCoursePeriodResult">
         <include refid="selectFsUserCoursePeriodVo"/>
-        where period_id = #{periodId}
+        where del_flag ='0' and  period_id = #{periodId}
     </select>
 
     <insert id="insertFsUserCoursePeriod" parameterType="FsUserCoursePeriod" useGeneratedKeys="true" keyProperty="periodId">
@@ -232,7 +234,7 @@
 
     <select id="selectFsUserCoursePeriodsByIds" resultType="FsUserCoursePeriod">
         select * from fs_user_course_period
-        where period_id in
+        where del_flag ='0' and  period_id in
         <foreach collection="periodIds" item="item" open="(" separator="," close=")">
             #{item}
         </foreach>
@@ -242,20 +244,19 @@
         select count(ucp.period_id)
         from fs_user_course_period ucp
         inner join fs_user_course_training_camp uctc on uctc.training_camp_id = ucp.training_camp_id
-        where uctc.training_camp_id in
+        where ucp.del_flag ='0' and uctc.training_camp_id in
         <foreach collection="params.ids" item="id" open="(" separator="," close=")">
             #{id}
         </foreach>
         <![CDATA[
-        and ((ucp.period_starting_time <= #{params.date}
-        and ucp.period_end_time >= #{params.date}
-        ) or ucp.period_end_time < #{params.date})
+       and (ucp.period_starting_time <= #{params.date}
+        and ucp.period_end_time > #{params.date})
         ]]>
     </select>
 
     <select id="selectPeriodListByTrainingCampIds" resultType="FsUserCoursePeriod">
         select * from fs_user_course_period
-        where training_camp_id in
+        where del_flag ='0' and training_camp_id in
         <foreach collection="trainingCampIds" item="item" open="(" separator="," close=")">
             #{item}
         </foreach>

+ 3 - 1
fs-service/src/main/resources/mapper/course/FsUserCourseTrainingCampMapper.xml

@@ -17,6 +17,7 @@
         left join fs_user_course_period ctp on ctc.training_camp_id = ctp.training_camp_id
         left join fs_course_watch_log cu on cu.period_id = ctp.period_id
         <where>
+            ctc.del_flag ='0'
             <if test="params.trainingCampName != null and params.trainingCampName != ''">
                 and ctc.training_camp_name like concat('%',#{params.trainingCampName},'%')
             </if>
@@ -41,8 +42,9 @@
         ctc.training_camp_name dictLabel
         from fs_user_course_training_camp ctc
         <where>
+            ctc.del_flag ='0'
             <if test="params.name != null and params.name != ''">
-                ctc.training_camp_name like concat('%', #{params.name}, '%')
+               and ctc.training_camp_name like concat('%', #{params.name}, '%')
             </if>
         </where>
     </select>

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

@@ -89,6 +89,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="customerId"    column="customer_id"    />
         <result property="source"    column="source"    />
         <result property="billPrice"    column="bill_price"    />
+        <result property="erpPhone"    column="erp_phone"    />
     </resultMap>
 
     <sql id="selectFsStoreOrderVo">
@@ -230,6 +231,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isFirst != null">is_first,</if>
             <if test="customerId != null">customer_id,</if>
             <if test="source != null">source,</if>
+            <if test="erpPhone != null">erp_phone,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="storeId != null">#{storeId},</if>
@@ -314,6 +316,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isFirst != null">#{isFirst},</if>
             <if test="customerId != null">#{customerId},</if>
             <if test="source != null">#{source},</if>
+            <if test="erpPhone != null">#{erpPhone},</if>
          </trim>
     </insert>
 
@@ -403,9 +406,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="customerId != null">customer_id = #{customerId},</if>
             <if test="source != null">source = #{source},</if>
             <if test="billPrice != null">bill_price = #{billPrice},</if>
+            <if test="erpPhone != null and erpPhone != ''">erp_phone = #{erpPhone},</if>
         </trim>
         where order_id = #{orderId}
     </update>
+    <update id="batchUpdateErpByOrderIds">
+        update fs_store_order set erp_phone = #{maps.erpPhone} where
+        order_id in
+        <foreach collection="maps.orderIds" separator="," open="(" close=")" index="index" item="item">
+            #{item}
+        </foreach>
+    </update>
 
     <delete id="deleteFsStoreOrderByOrderId" parameterType="String">
         delete from fs_store_order where order_id = #{orderId}
@@ -526,9 +537,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="maps.isFirst != null ">
             and so.is_first = #{maps.isFirst}
         </if>
-        <if test="maps.status != null">
+        <if test="maps.status != null  and maps.status != 5">
             and so.status = #{maps.status}
         </if>
+        <if test="maps.status == 5">
+            and so.`status`= 2
+            and (
+            so.store_id in (select store_id from fs_store where delivery_type=2 or delivery_type=1)
+            )
+            and  so.extend_order_id is null
+        </if>
         <if test="maps.deliverySn != null and maps.deliverySn != ''">
             and so.delivery_sn = #{maps.deliverySn}
         </if>
@@ -664,4 +682,176 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
         group by user_id
     </select>
+    <select id="selectFsStoreOrderListVO" resultType="com.fs.his.vo.FsStoreOrderListVO">
+        select so.*,st.store_name,us.nick_name,us.phone,d.doctor_name,p.patient_name,p.prescribe_code,c.company_name,
+               cu.nick_name company_user_name
+        FROM fs_store_order so LEFT JOIN fs_store st ON so.store_id =st.store_id
+        LEFT JOIN fs_user us ON us.user_id=so.user_id
+        LEFT JOIN fs_prescribe p ON p.prescribe_id =so.prescribe_id
+        LEFT JOIN fs_doctor d ON so.doctor_id= d.doctor_id
+        LEFT JOIN company c on c.company_id =so.company_id
+        LEFT JOIN company_user cu on cu.user_id=so.company_user_id
+        <where>
+            <if test="maps.packageSecondName != null and maps.packageSecondName != ''">
+                and so.package_second_name like concat('%', #{maps.packageSecondName}, '%')
+            </if>
+            <if test="maps.storeId != null">
+                and so.store_id = #{maps.storeId}
+            </if>
+            <if test="maps.orderCode != null  and maps.orderCode != ''">
+                and so.order_code = #{maps.orderCode}
+            </if>
+            <if test="maps.prescribeCode != null  and maps.prescribeCode != ''">
+                and p.prescribe_code = #{maps.prescribeCode}
+            </if>
+            <if test="maps.userName != null  and maps.userName != ''">
+                and so.user_name like concat('%', #{maps.userName}, '%')
+            </if>
+            <if test="maps.userPhone != null  and maps.userPhone != ''">
+                and so.user_phone = #{maps.userPhone}
+            </if>
+            <if test="maps.userId != null ">
+                and so.user_id = #{maps.userId}
+            </if>
+            <if test="maps.isFirst != null">
+                and so.is_first = #{maps.isFirst}
+            </if>
+            <if test="maps.status != null and maps.status != 5">
+                and so.status = #{maps.status}
+            </if>
+            <if test="maps.status == 5">
+                and so.`status`= 2
+                and (
+                so.store_id in (select store_id from fs_store where delivery_type=2 or delivery_type=1)
+                )
+                and  so.extend_order_id is null
+            </if>
+            <if test="maps.source != null">
+                and so.source = #{maps.source}
+            </if>
+            <if test="maps.deliverySn != null  and maps.deliverySn != ''">
+                and so.delivery_sn = #{maps.deliverySn}
+            </if>
+            <if test="maps.prescribeId != null">
+                and so.prescribe_id = #{maps.prescribeId}
+            </if>
+            <if test="maps.companyUserId != null">
+                and so.company_user_id = #{maps.companyUserId}
+            </if>
+            <if test="maps.sTime != null">
+                and DATE(so.create_time) &gt;= DATE(#{maps.sTime})
+            </if>
+            <if test="maps.eTime != null">
+                and DATE(so.create_time) &lt;= DATE(#{maps.eTime})
+            </if>
+            <if test="maps.paysTime != null">
+                and DATE(so.pay_time) &gt;= DATE(#{maps.paysTime})
+            </if>
+            <if test="maps.payeTime != null">
+                and DATE(so.pay_time) &lt;= DATE(#{maps.payeTime})
+            </if>
+            <if test="maps.orderCreateType != null">
+                and so.order_create_type = #{maps.orderCreateType}
+            </if>
+            <if test="maps.patientName != null">
+                and p.patient_name like concat('%', #{maps.patientName}, '%')
+            </if>
+            <if test="maps.doctorName != null">
+                and d.doctor_name like concat('%', #{maps.doctorName}, '%')
+            </if>
+            <if test="maps.orderType != null">
+                and so.order_type = #{maps.orderType}
+            </if>
+            <if test="maps.deliverySendsTime != null">
+                and DATE(so.delivery_send_time) &gt;= DATE(#{maps.deliverySendsTime})
+            </if>
+            <if test="maps.deliverySendeTime != null">
+                and DATE(so.delivery_send_time) &lt;= DATE(#{maps.deliverySendeTime})
+            </if>
+            <if test="maps.deliveryImportsTime != null">
+                and DATE(so.delivery_import_time) &gt;= DATE(#{maps.deliveryImportsTime})
+            </if>
+            <if test="maps.deliveryImporteTime != null">
+                and DATE(so.delivery_import_time) &lt;= DATE(#{maps.deliveryImporteTime})
+            </if>
+            <if test="maps.tuisTime != null">
+                and DATE(so.tui_money_time) &gt;= DATE(#{maps.tuisTime})
+            </if>
+            <if test="maps.tuieTime != null">
+                and DATE(so.tui_money_time) &lt;= DATE(#{maps.tuieTime})
+            </if>
+            <if test="maps.companyUserNickName != null and  maps.companyUserNickName !=''">
+                and cu.nick_name like concat( #{maps.companyUserNickName}, '%')
+            </if>
+            <if test="maps.companyId != null and  maps.companyId != -1">
+                " + "and so.company_id =#{maps.companyId} " + "
+            </if>
+            <if test="maps.companyId == -1">
+                and so.company_id is null
+            </if>
+            <if test="maps.deliveryStatus != null">
+                and so.delivery_status =#{maps.deliveryStatus}
+            </if>
+            <if test="maps.customerId != null">
+                and so.customer_id =#{maps.customerId}
+            </if>
+            <if test="maps.deliveryPayStatus != null">
+                and so.delivery_pay_status =#{maps.deliveryPayStatus}
+            </if>
+            <if test="maps.tuiMoneyStatus != null">
+                and so.tui_money_status =#{maps.tuiMoneyStatus}
+            </if>
+            <if test="maps.deptId != null">
+                AND (so.dept_id = #{maps.deptId} OR so.dept_id IN ( SELECT t.dept_id FROM company_dept t WHERE find_in_set(#{maps.deptId}, ancestors) ))
+            </if>
+            <if test="maps.packageName != null and maps.packageName != ''">
+                and so.package_name like concat('%', #{maps.packageName}, '%')
+            </if>
+            <if test="maps.payType != null">
+                and so.pay_type IN
+                <foreach collection="maps.payType.split(',')" item="item" index="index" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="maps.scheduleId != null  and  maps.scheduleId != -1">
+                and so.schedule_id IN
+                <foreach collection="maps.scheduleId.split(',')" item="item" index="index" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="maps.scheduleId == -1">
+                and so.schedule_id is null
+            </if>
+            <if test="maps.orderBuyType != null and maps.orderBuyType != -1">
+                and so.order_buy_type IN
+                <foreach collection="maps.orderBuyType.split(',')" item="item" index="index" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="maps.orderBuyType == -1">
+                and so.order_buy_type is null
+            </if>
+            <if test="maps.orderChannel == -1">
+                and so.order_channel is null
+            </if>
+            <if test="maps.orderChannel != null and maps.orderChannel != -1">
+                and so.order_channel IN
+                <foreach collection="maps.orderChannel.split(',')" item="item" index="index" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="maps.qwSubject == -1">
+                and so.qw_subject is null
+            </if>
+            <if test="maps.qwSubject != null and maps.qwSubject != -1">
+                and so.qw_subject IN
+                <foreach collection="maps.qwSubject.split(',')" item="item" index="index" open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </if>
+
+        </where>
+        ${maps.params.dataScope}
+        ORDER BY so.order_id desc
+    </select>
 </mapper>

+ 24 - 0
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -85,6 +85,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         group by a.id
     </select>
 
+    <select id="selectQwExternalContactByIds" resultType="com.fs.qw.domain.QwExternalContact">
+        select * from qw_external_contact
+        where id in
+        <foreach collection="ids" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
+
     <select id="selectQwExternalContactListVOByIds" resultType="com.fs.qw.param.QwExternalContactVOTime">
             select id,tag_ids,remark,create_time,fs_user_id,avatar from qw_external_contact
           where id in
@@ -165,6 +173,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
     </update>
 
+
+    <update id="batchUpdateQwExternalContactByTags" parameterType="map">
+        UPDATE qw_external_contact
+        SET
+        tag_ids = CASE id
+        <foreach collection="list" item="item">
+            WHEN #{item.id} THEN #{item.tagIds}
+        </foreach>
+        ELSE tag_ids
+        END
+        WHERE id IN
+        <foreach collection="list" item="item" open="(" separator="," close=")">
+            #{item.id}
+        </foreach>
+    </update>
+
     <update id="batchUpdateQwExternalContactStatus" parameterType="map">
         UPDATE qw_external_contact
         SET status = 4

+ 30 - 28
fs-service/src/main/resources/mapper/sop/QwSopMapper.xml

@@ -31,12 +31,15 @@
         <result property="stopTime"    column="stop_time"    />
         <result property="isRating"    column="is_rating"    />
         <result property="courseDay"    column="course_day"    />
+        <result property="isFixed"    column="is_fixed"    />
+        <result property="chatId"    column="chat_id"    />
         <result property="openCommentStatus"    column="open_comment_status"    />
         <result property="isSampSend"    column="is_samp_send"    />
     </resultMap>
 
     <sql id="selectQwSopVo">
-        select *  from qw_sop
+        select *
+        from qw_sop
     </sql>
 
     <select id="selectQwSopAutoByTagsByForeach"  resultType="com.fs.qw.vo.QwSopRuleTimeVO">
@@ -53,14 +56,13 @@
         FROM
             qw_sop qs
                 LEFT JOIN
-            qw_sop_temp qst ON qs.temp_id = qst.id
+            qw_sop_temp qst ON qs.temp_id = qst.id AND qst.status = '1'
         WHERE
-            FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
-          AND qs.corp_id = #{map.corpId}
-          AND qst.status = '1'
+          qs.corp_id = #{map.corpId}
           AND qs.status IN (2, 3, 4)
+          AND qs.send_type in (2,11)
           AND qs.is_auto_sop = 1
-          AND qs.send_type = #{map.sendType}
+          AND FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
           AND (
            <foreach collection='map.tagsIdsSelectList' item='item' index='index' separator=' or '>
                      find_in_set( #{item}, TRIM(REGEXP_REPLACE(qs.tags, '[\"\\\\[\\\\]]', '')))
@@ -83,13 +85,12 @@
         FROM
         qw_sop qs
         LEFT JOIN
-        qw_sop_temp qst ON qs.temp_id = qst.id
+        qw_sop_temp qst ON qs.temp_id = qst.id AND qst.status = '1'
         WHERE
-            FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
-        AND qs.corp_id = #{map.corpId}
-        AND qst.status = '1'
+        qs.corp_id = #{map.corpId}
         AND qs.status IN (2, 3, 4)
-        AND qs.send_type = #{map.sendType}
+        AND qs.send_type IN (2,11)
+        AND FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
         AND (
             <foreach collection='map.tagsIdsSelectList' item='item' index='index' separator=' or '>
                 find_in_set( #{item}, TRIM(REGEXP_REPLACE(qs.tags, '[\"\\\\[\\\\]]', '')) )
@@ -111,13 +112,12 @@
         FROM
         qw_sop qs
         LEFT JOIN
-        qw_sop_temp qst ON qs.temp_id = qst.id
+        qw_sop_temp qst ON qs.temp_id = qst.id  AND qst.status = '1'
         WHERE
-        FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
-        AND qs.corp_id = #{map.corpId}
-        AND qst.status = '1'
+         qs.corp_id = #{map.corpId}
         AND qs.status IN (2, 3, 4)
-        AND qs.send_type = #{map.sendType}
+        AND qs.send_type IN (2,11)
+        AND FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
         AND (
         <foreach collection='map.tagsIdsSelectList' item='item' index='index' separator=' and '>
           NOT find_in_set( #{item}, TRIM(REGEXP_REPLACE(qs.tags, '[\"\\\\[\\\\]]', '')) )
@@ -139,13 +139,12 @@
         FROM
         qw_sop qs
         LEFT JOIN
-        qw_sop_temp qst ON qs.temp_id = qst.id
+        qw_sop_temp qst ON qs.temp_id = qst.id  AND qst.status = '1'
         WHERE
-             FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
-        AND qs.corp_id = #{map.corpId}
-        AND qst.status = '1'
+         qs.corp_id = #{map.corpId}
         AND qs.status IN (2, 3, 4)
         AND qs.send_type = 4
+        AND FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
         AND (
         <foreach collection='map.tagsIdsSelectList' item='item' index='index' separator=' or '>
             find_in_set( #{item}, TRIM(REGEXP_REPLACE(qs.tags, '[\"\\\\[\\\\]]', '')) )
@@ -159,13 +158,12 @@
         FROM
             qw_sop qs
                 LEFT JOIN
-            qw_sop_temp qst ON qs.temp_id = qst.id
+            qw_sop_temp qst ON qs.temp_id = qst.id  AND qst.status = '1'
         WHERE
-            FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
-          AND qs.corp_id = #{map.corpId}
-          AND qst.status = '1'
+           qs.corp_id = #{map.corpId}
           AND qs.status IN (2, 3, 4)
-          AND qs.send_type = #{map.sendType}
+          AND qs.send_type IN (2,11)
+          AND FIND_IN_SET(#{map.qwUserId}, COALESCE(qs.qw_user_ids, '')) > 0
     </select>
 
 
@@ -213,13 +211,13 @@
             ]]>
     </select>
 
-    <select id="selectQwSopList" parameterType="QwSop" resultType="QwSop">
+    <select id="selectQwSopList" parameterType="QwSop" resultMap="QwSopResult">
         <include refid="selectQwSopVo"/>
         <where>
             <if test="name != null  and name != ''"> and name like concat('%', #{name}, '%')</if>
             <if test="status != null "> and status = #{status}</if>
             <if test="type != null "> and type = #{type}</if>
-            <if test="id != null and id != '' "> and id = #{id}</if>
+            <if test="id != null "> and id = #{id}</if>
             <if test="companyId != null "> and company_id = #{companyId}</if>
             <if test="qwUserIds != null  and qwUserIds != ''"> and FIND_IN_SET(#{qwUserIds}, qw_user_ids) > 0</if>
             <if test="createBy != null  and createBy != ''"> and create_by = #{createBy}</if>
@@ -244,12 +242,14 @@
             and status != 6
          order by create_time desc
     </select>
+
     <select id="selectQwSopByIsRating" parameterType="QwSop" resultMap="QwSopResult">
         <include refid="selectQwSopVo"/>
         where is_rating = 1
-        and send_type in(2,3)
+        and send_type in(2,3,11)
         order by create_time desc
     </select>
+
     <select id="selectQwSopById" parameterType="String" resultType="com.fs.sop.domain.QwSop">
         <include refid="selectQwSopVo"/>
         where id = #{id}
@@ -519,6 +519,8 @@
         SET max_send = 1
         WHERE id = #{id}
     </update>
+
+
     <update id="updateStatusQwSopById2" useGeneratedKeys="false" keyProperty="id" >
         UPDATE qw_sop
         SET status = 3

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

@@ -109,7 +109,7 @@ public class CourseWxH5Controller extends AppBaseController {
         if (param.getDuration()==null){
             logger.info("zyp \n【未识别到时长】:{}",param.getUserId());
         }
-        return questionBankService.courseAnswer(param, true);
+        return questionBankService.courseAnswerByFsUser(param);
     }
 
     @ApiOperation("发放奖励")

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

@@ -78,11 +78,14 @@ public class WxH5MpController {
             CompanyUser companyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
 
             FsUser user;
+        logger.info("当前微信信息---------------------------》{}",wxMpUser);
             if(StringUtils.isNotEmpty(wxMpUser.getUnionId())) {
                 user = userService.selectFsUserByUnionId(wxMpUser.getUnionId());
             } else {
-                user = userService.selectFsUserByMpOpenId(wxMpUser.getOpenid());
+                logger.info("当前查询参数-----------》{}",wxMpUser.getOpenid());
+                user = userService.selectFsUserByOpenId(wxMpUser.getOpenid());
             }
+        logger.info("当前用户信息-----------》{}",user);
             if (user != null) {
                 //修改
                 FsUser userMap = new FsUser();

+ 140 - 0
fs-user-course/pom.xml

@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>fs</artifactId>
+        <groupId>com.fs</groupId>
+        <version>1.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>fs-user-course</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+        <!-- spring-boot-devtools -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <optional>true</optional> <!-- 表示依赖不会传递 -->
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-cache</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <!-- swagger2-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+        </dependency>
+
+        <!-- swagger2-UI-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>swagger-bootstrap-ui</artifactId>
+            <version>1.9.3</version>
+        </dependency>
+
+
+        <!-- Mysql驱动包 -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <!-- SpringBoot Web容器 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!-- SpringBoot 拦截器 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <!-- 阿里数据库连接池 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+
+        <!-- 验证码 -->
+        <dependency>
+            <groupId>com.github.penggle</groupId>
+            <artifactId>kaptcha</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>javax.servlet-api</artifactId>
+                    <groupId>javax.servlet</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- 获取系统信息 -->
+        <dependency>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fs</groupId>
+            <artifactId>fs-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-spring-boot-starter</artifactId>
+            <version>2.2.3</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.1.1.RELEASE</version>
+                <configuration>
+                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>3.1.0</version>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                    <warName>${project.artifactId}</warName>
+                </configuration>
+            </plugin>
+        </plugins>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+
+</project>

+ 14 - 0
fs-user-course/src/main/java/com/fs/FSServletInitializer.java

@@ -0,0 +1,14 @@
+package com.fs;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+
+public class FSServletInitializer extends SpringBootServletInitializer
+{
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
+    {
+        return application.sources(FsUserCourseApplication.class);
+    }
+}

+ 26 - 0
fs-user-course/src/main/java/com/fs/FsUserCourseApplication.java

@@ -0,0 +1,26 @@
+package com.fs;
+
+import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * 启动程序
+ */
+@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
+@EnableTransactionManagement
+@EnableSwaggerBootstrapUI
+@EnableScheduling
+public class FsUserCourseApplication
+{
+    public static void main(String[] args)
+    {
+
+        // System.setProperty("spring.devtools.restart.enabled", "false");
+        SpringApplication.run(FsUserCourseApplication.class, args);
+        System.out.println("CourseAPI启动成功");
+    }
+}

+ 12 - 0
fs-user-course/src/main/java/com/fs/course/annotation/Login.java

@@ -0,0 +1,12 @@
+package com.fs.course.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * app登录效验
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Login {
+}

+ 15 - 0
fs-user-course/src/main/java/com/fs/course/annotation/LoginUser.java

@@ -0,0 +1,15 @@
+package com.fs.course.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 登录用户信息
+ */
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface LoginUser {
+
+}

部分文件因文件數量過多而無法顯示