Parcourir la source

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderMapper.java
#	fs-service/src/main/java/com/fs/his/service/IFsStoreOrderService.java
ct il y a 3 jours
Parent
commit
08194f8f8f
100 fichiers modifiés avec 7090 ajouts et 294 suppressions
  1. 24 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyMenuController.java
  2. 120 0
      fs-admin/src/main/java/com/fs/his/controller/HzOMSErpApiController.java
  3. 28 9
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  4. 211 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserDelayTimeController.java
  5. 7 3
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java
  6. 1 0
      fs-framework/src/main/java/com/fs/framework/config/SecurityConfig.java
  7. 40 0
      fs-service/src/main/java/com/fs/company/domain/CompanyUserDelayTime.java
  8. 72 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyUserDelayTimeMapper.java
  9. 10 0
      fs-service/src/main/java/com/fs/company/param/CompanyUserDelayTimeParam.java
  10. 63 0
      fs-service/src/main/java/com/fs/company/service/ICompanyUserDelayTimeService.java
  11. 29 9
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  12. 106 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserDelayTimeServiceImpl.java
  13. 5 0
      fs-service/src/main/java/com/fs/company/vo/CompanyUserQwListVO.java
  14. 1 0
      fs-service/src/main/java/com/fs/course/config/CourseConfig.java
  15. 1 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java
  16. 1 1
      fs-service/src/main/java/com/fs/course/param/FsCourseRedPacketLogParam.java
  17. 1 1
      fs-service/src/main/java/com/fs/course/service/IFsCourseRedPacketLogService.java
  18. 1 1
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  19. 20 9
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java
  20. 26 29
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  21. 2 1
      fs-service/src/main/java/com/fs/erp/domain/ErpGoods.java
  22. 2 1
      fs-service/src/main/java/com/fs/erp/domain/ErpRefundOrder.java
  23. 1 0
      fs-service/src/main/java/com/fs/erp/dto/ErpRefundUpdateRequest.java
  24. 179 0
      fs-service/src/main/java/com/fs/erp/dto/sdk/HzOMS/api/HzOMSClient.java
  25. 91 0
      fs-service/src/main/java/com/fs/erp/dto/sdk/HzOMS/utils/HzOMSUtils.java
  26. 122 0
      fs-service/src/main/java/com/fs/erp/service/impl/HzOMSErpGoodsServiceImpl.java
  27. 383 0
      fs-service/src/main/java/com/fs/erp/service/impl/HzOMSErpOrderServiceImpl.java
  28. 131 4
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  29. 6 1
      fs-service/src/main/java/com/fs/his/config/FsSysConfig.java
  30. 6 0
      fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderMapper.java
  31. 9 0
      fs-service/src/main/java/com/fs/his/mapper/FsStoreProductAttrValueMapper.java
  32. 29 17
      fs-service/src/main/java/com/fs/his/mapper/FsStoreProductMapper.java
  33. 16 0
      fs-service/src/main/java/com/fs/his/param/HzOMSErpApiParam.java
  34. 37 0
      fs-service/src/main/java/com/fs/his/service/ErpApiService.java
  35. 4 0
      fs-service/src/main/java/com/fs/his/service/IFsStoreOrderService.java
  36. 6 0
      fs-service/src/main/java/com/fs/his/service/IFsStoreProductAttrValueService.java
  37. 3 0
      fs-service/src/main/java/com/fs/his/service/IFsStoreProductService.java
  38. 131 110
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java
  39. 33 6
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java
  40. 15 0
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreProductAttrValueServiceImpl.java
  41. 128 86
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreProductServiceImpl.java
  42. 238 0
      fs-service/src/main/java/com/fs/his/service/impl/HzOMSErpApiServiceImpl.java
  43. 11 0
      fs-service/src/main/java/com/fs/his/vo/HzOMSErpResponseDetailVO.java
  44. 19 0
      fs-service/src/main/java/com/fs/his/vo/HzOMSErpResponseErrorItemVO.java
  45. 20 0
      fs-service/src/main/java/com/fs/his/vo/HzOMSErpResponseVO.java
  46. 2 2
      fs-service/src/main/java/com/fs/qw/domain/QwAutoTags.java
  47. 14 0
      fs-service/src/main/java/com/fs/qw/mapper/QwAutoTagsMapper.java
  48. 7 2
      fs-service/src/main/java/com/fs/qw/param/QwAutoTagsParam.java
  49. 13 0
      fs-service/src/main/java/com/fs/qw/vo/QwSopTempSetting2.java
  50. 4 0
      fs-service/src/main/java/com/fs/sop/params/SendSopParamDetailsC.java
  51. 24 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java
  52. 5 2
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java
  53. 13 0
      fs-service/src/main/java/com/fs/store/config/CompanyMenuConfig.java
  54. 103 0
      fs-service/src/main/resources/mapper/company/CompanyUserDelayTimeMapper.xml
  55. 140 0
      fs-user-course/pom.xml
  56. 14 0
      fs-user-course/src/main/java/com/fs/FSServletInitializer.java
  57. 26 0
      fs-user-course/src/main/java/com/fs/FsUserCourseApplication.java
  58. 12 0
      fs-user-course/src/main/java/com/fs/course/annotation/Login.java
  59. 15 0
      fs-user-course/src/main/java/com/fs/course/annotation/LoginUser.java
  60. 29 0
      fs-user-course/src/main/java/com/fs/course/config/WebMvcConfig.java
  61. 40 0
      fs-user-course/src/main/java/com/fs/course/config/WebSocketConfig.java
  62. 121 0
      fs-user-course/src/main/java/com/fs/course/controller/AppBaseController.java
  63. 165 0
      fs-user-course/src/main/java/com/fs/course/controller/CompanyUserController.java
  64. 322 0
      fs-user-course/src/main/java/com/fs/course/controller/CourseController.java
  65. 119 0
      fs-user-course/src/main/java/com/fs/course/controller/CourseH5Controller.java
  66. 129 0
      fs-user-course/src/main/java/com/fs/course/controller/CourseWxH5Controller.java
  67. 67 0
      fs-user-course/src/main/java/com/fs/course/controller/H5Controller.java
  68. 245 0
      fs-user-course/src/main/java/com/fs/course/controller/UserController.java
  69. 202 0
      fs-user-course/src/main/java/com/fs/course/controller/WxCompanyUserController.java
  70. 267 0
      fs-user-course/src/main/java/com/fs/course/controller/WxMpController.java
  71. 371 0
      fs-user-course/src/main/java/com/fs/course/controller/WxUserController.java
  72. 51 0
      fs-user-course/src/main/java/com/fs/course/exception/FSException.java
  73. 68 0
      fs-user-course/src/main/java/com/fs/course/interceptor/AuthorizationInterceptor.java
  74. 14 0
      fs-user-course/src/main/java/com/fs/course/param/FsBindCompanyUserParam.java
  75. 95 0
      fs-user-course/src/main/java/com/fs/course/param/FsDoctorRegisterParam.java
  76. 24 0
      fs-user-course/src/main/java/com/fs/course/param/FsUserEditParam.java
  77. 13 0
      fs-user-course/src/main/java/com/fs/course/param/FsUserLoginByMpParam.java
  78. 88 0
      fs-user-course/src/main/java/com/fs/course/utils/JwtUtils.java
  79. 23 0
      fs-user-course/src/main/java/com/fs/course/vo/CityVO.java
  80. 182 0
      fs-user-course/src/main/java/com/fs/framework/aspectj/DataScopeAspect.java
  81. 73 0
      fs-user-course/src/main/java/com/fs/framework/aspectj/DataSourceAspect.java
  82. 244 0
      fs-user-course/src/main/java/com/fs/framework/aspectj/LogAspect.java
  83. 117 0
      fs-user-course/src/main/java/com/fs/framework/aspectj/RateLimiterAspect.java
  84. 31 0
      fs-user-course/src/main/java/com/fs/framework/config/ApplicationConfig.java
  85. 85 0
      fs-user-course/src/main/java/com/fs/framework/config/CaptchaConfig.java
  86. 92 0
      fs-user-course/src/main/java/com/fs/framework/config/DataSourceConfig.java
  87. 72 0
      fs-user-course/src/main/java/com/fs/framework/config/FastJson2JsonRedisSerializer.java
  88. 59 0
      fs-user-course/src/main/java/com/fs/framework/config/FilterConfig.java
  89. 76 0
      fs-user-course/src/main/java/com/fs/framework/config/KaptchaTextCreator.java
  90. 149 0
      fs-user-course/src/main/java/com/fs/framework/config/MyBatisConfig.java
  91. 137 0
      fs-user-course/src/main/java/com/fs/framework/config/RedisConfig.java
  92. 65 0
      fs-user-course/src/main/java/com/fs/framework/config/ResourcesConfig.java
  93. 50 0
      fs-user-course/src/main/java/com/fs/framework/config/SecurityConfig.java
  94. 33 0
      fs-user-course/src/main/java/com/fs/framework/config/ServerConfig.java
  95. 124 0
      fs-user-course/src/main/java/com/fs/framework/config/SwaggerConfig.java
  96. 63 0
      fs-user-course/src/main/java/com/fs/framework/config/ThreadPoolConfig.java
  97. 77 0
      fs-user-course/src/main/java/com/fs/framework/config/properties/DruidProperties.java
  98. 27 0
      fs-user-course/src/main/java/com/fs/framework/datasource/DynamicDataSource.java
  99. 44 0
      fs-user-course/src/main/java/com/fs/framework/datasource/DynamicDataSourceContextHolder.java
  100. 56 0
      fs-user-course/src/main/java/com/fs/framework/interceptor/RepeatSubmitInterceptor.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));
+    }
+
 }

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

+ 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")

+ 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

+ 1 - 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>" +

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

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

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

+ 26 - 29
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);
             }
 
@@ -1499,7 +1496,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());

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

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

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

@@ -1086,4 +1086,10 @@ public interface FsStoreOrderMapper
     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);
+
+
 }

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

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

@@ -213,4 +213,8 @@ public interface IFsStoreOrderService
     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());

+ 33 - 6
fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java

@@ -189,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
@@ -1620,14 +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);
+                        }
                     }
                 }
             }
@@ -1665,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);
@@ -3202,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;
+    }
+}

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

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

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

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

+ 24 - 0
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) {

+ 5 - 2
fs-service/src/main/java/com/fs/sop/service/impl/QwSopTempServiceImpl.java

@@ -388,6 +388,7 @@ 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);
@@ -407,11 +408,13 @@ public class QwSopTempServiceImpl implements IQwSopTempService
                 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));

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

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

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

+ 29 - 0
fs-user-course/src/main/java/com/fs/course/config/WebMvcConfig.java

@@ -0,0 +1,29 @@
+package com.fs.course.config;
+
+
+import com.fs.course.interceptor.AuthorizationInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * MVC配置
+ */
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+    @Autowired
+    private AuthorizationInterceptor authorizationInterceptor;
+//    @Autowired
+//    private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(authorizationInterceptor).addPathPatterns("/app/**");
+    }
+//
+//    @Override
+//    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
+//        argumentResolvers.add(loginUserHandlerMethodArgumentResolver);
+//    }
+}

+ 40 - 0
fs-user-course/src/main/java/com/fs/course/config/WebSocketConfig.java

@@ -0,0 +1,40 @@
+package com.fs.course.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
+
+@Configuration
+public class WebSocketConfig {
+    /**
+     * 自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
+     *
+     * @return
+     */
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+
+    /**
+     * 通信文本消息和二进制缓存区大小
+     * 避免对接 第三方 报文过大时,Websocket 1009 错误
+     *
+     * @return
+     */
+    @Bean
+    public ServletServerContainerFactoryBean createWebSocketContainer() {
+        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
+        // 设置文本消息缓冲区大小
+        container.setMaxTextMessageBufferSize(10240000);
+        // 设置二进制消息缓冲区大小
+        container.setMaxBinaryMessageBufferSize(10240000);
+        // 设置最大会话空闲超时时间(单位:毫秒)
+        container.setMaxSessionIdleTimeout(20 * 60000L); // 15分钟
+        // 设置异步发送超时时间(单位:毫秒)
+        container.setAsyncSendTimeout(300 * 1000L);
+        return container;
+    }
+
+}

+ 121 - 0
fs-user-course/src/main/java/com/fs/course/controller/AppBaseController.java

@@ -0,0 +1,121 @@
+package com.fs.course.controller;
+
+
+import com.fs.course.utils.JwtUtils;
+import com.fs.common.constant.HttpStatus;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.PageDomain;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.TableSupport;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.sql.SqlUtil;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.jsonwebtoken.Claims;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.List;
+
+
+public class AppBaseController {
+	@Autowired
+	JwtUtils jwtUtils;
+	@Autowired
+	RedisCache redisCache;
+
+	public String getUserId()
+	{
+		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
+		if (StringUtils.isNotEmpty(headValue)){
+			Claims claims=jwtUtils.getClaimByToken(headValue);
+			if(claims!=null){
+				String userId = claims.getSubject().toString();
+				return userId;
+			}
+			else{
+				return "";
+			}
+		}
+		return "";
+	}
+	public Long getCompanyUserId( )
+	{
+		Long companyUesrId=redisCache.getCacheObject("company-user-token:"+ ServletUtils.getRequest().getHeader("companyUserToken"));
+		if(companyUesrId== null){
+			throw  new CustomException("未登录",403);
+		}
+		return companyUesrId;
+	}
+
+	/**
+	 * 设置请求分页数据
+	 */
+	protected void startPage()
+	{
+		PageDomain pageDomain = TableSupport.buildPageRequest();
+		Integer pageNum = pageDomain.getPageNum();
+		Integer pageSize = pageDomain.getPageSize();
+		if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
+		{
+			String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
+			Boolean reasonable = pageDomain.getReasonable();
+			PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
+		}
+	}
+
+	/**
+	 * 响应请求分页数据
+	 */
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	protected TableDataInfo getDataTable(List<?> list)
+	{
+		TableDataInfo rspData = new TableDataInfo();
+		rspData.setCode(HttpStatus.SUCCESS);
+		rspData.setMsg("查询成功");
+		rspData.setRows(list);
+		rspData.setTotal(new PageInfo(list).getTotal());
+		return rspData;
+	}
+
+	/**
+	 * 响应返回结果
+	 *
+	 * @param rows 影响行数
+	 * @return 操作结果
+	 */
+	protected AjaxResult toAjax(int rows)
+	{
+		return rows > 0 ? AjaxResult.success() : AjaxResult.error();
+	}
+
+	/**
+	 * 响应返回结果
+	 *
+	 * @param result 结果
+	 * @return 操作结果
+	 */
+	protected AjaxResult toAjax(boolean result)
+	{
+		return result ? success() : error();
+	}
+
+	/**
+	 * 返回成功
+	 */
+	public AjaxResult success()
+	{
+		return AjaxResult.success();
+	}
+
+	/**
+	 * 返回失败消息
+	 */
+	public AjaxResult error()
+	{
+		return AjaxResult.error();
+	}
+
+}

+ 165 - 0
fs-user-course/src/main/java/com/fs/course/controller/CompanyUserController.java

@@ -0,0 +1,165 @@
+package com.fs.course.controller;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.course.annotation.Login;
+import com.fs.course.param.FsBindCompanyUserParam;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.SecurityUtils;
+import com.fs.common.utils.sign.Md5Utils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.domain.CompanyUserUser;
+import com.fs.company.mapper.CompanyUserMapper;
+import com.fs.company.param.CompanyUserLoginParam;
+import com.fs.company.param.companyUserAddPrintParam;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.company.service.ICompanyUserUserService;
+import com.fs.fastgptApi.util.AudioUtils;
+import com.fs.system.oss.CloudStorageService;
+import com.fs.system.oss.OSSFactory;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+
+@Api("销售接口")
+@RestController
+@RequestMapping(value="/app/companyUser")
+public class CompanyUserController extends  AppBaseController {
+
+
+    @Autowired
+    RedisCache redisCache;
+    @Autowired
+    private ICompanyUserService companyUserService;
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
+    @Autowired
+    private ICompanyUserUserService companyUserUserService;
+    @PostMapping("/login")
+    public R Login(@RequestBody CompanyUserLoginParam param, HttpServletRequest request){
+        try {
+            CompanyUser companyUser=companyUserService.selectUserByUserName(param.getUserName());
+            if(companyUser==null||companyUser.getDelFlag().equals("1")){
+                return R.error("用户不存在");
+            }
+            if(!companyUser.getStatus().equals("0")){
+                return R.error("用户已禁用");
+
+            }
+            String pwd= SecurityUtils.encryptPassword(param.getPassword());
+            if(!SecurityUtils.matchesPassword(param.getPassword(),companyUser.getPassword())){
+                return R.error("密码不正确");
+            }
+            redisCache.setCacheObject("company-user-token:"+Md5Utils.hash(companyUser.getUserId().toString()),companyUser.getUserId(),100, TimeUnit.DAYS);
+            return R.ok().put("companyUserToken", Md5Utils.hash(companyUser.getUserId().toString())).put("user",companyUser);
+        } catch (Exception e){
+
+            return R.error("操作异常");
+        }
+    }
+
+    @ApiOperation("获取销售信息")
+    @GetMapping("/getUserInfo")
+    public R getUserInfo(HttpServletRequest request){
+
+        Long userId=getCompanyUserId();
+        if(userId==null){
+            return R.error(403,"用户失效");
+        }
+        CompanyUser companyUser=companyUserService.selectCompanyUserById(userId);
+        if(companyUser==null||companyUser.getDelFlag().equals("1")){
+            return R.error("用户不存在");
+        }
+        if(!companyUser.getStatus().equals("0")){
+            return R.error("用户已禁用");
+        }
+        return R.ok().put("data",companyUser);
+    }
+
+    @Login
+    @ApiOperation("绑定销售")
+    @PostMapping("/bindCompanyUser")
+    public R bindCompanyUser(@Validated @RequestBody FsBindCompanyUserParam param, HttpServletRequest request){
+        CompanyUserUser map=new CompanyUserUser();
+        map.setCompanyUserId(param.getCompanyUserId());
+        map.setUserId(Long.parseLong(getUserId()));
+        List<CompanyUserUser> list= companyUserUserService.selectCompanyUserUserList(map);
+        if(list==null||list.size()==0){
+            CompanyUser companyUser=companyUserService.selectCompanyUserById(param.getCompanyUserId());
+            if(companyUser!=null&&companyUser.getStatus().equals("0")){
+                map.setCompanyId(companyUser.getCompanyId());
+                companyUserUserService.insertCompanyUserUser(map);
+            }
+        }
+        return R.ok();
+    }
+
+
+    @Login
+    @ApiOperation("上传声纹")
+    @PostMapping("/addVoicePrintUrl")
+    public R addVoicePrintUrl(@RequestBody companyUserAddPrintParam param) throws Exception {
+        Long userId=getCompanyUserId();
+        if(userId==null){
+            return R.error(403,"用户失效");
+        }
+        CompanyUser companyUser = new CompanyUser();
+        companyUser.setUserId(userId);
+        companyUser.setVoicePrintUrl(param.getVoicePrintUrl());
+
+        String s = AudioUtils.audioWAVFromUrl(param.getVoicePrintUrl());
+
+        System.out.println(s);
+        File file = new File(s);
+        FileInputStream fileInputStream = new FileInputStream(file);
+        CloudStorageService storage = OSSFactory.build();
+        String wavUrl = storage.uploadSuffix(fileInputStream, ".wav");
+        companyUser.setVoicePrintUrl(wavUrl);
+        companyUserMapper.updateCompanyUser(companyUser);
+        try {
+            CloseableHttpClient httpClient = HttpClients.createDefault();
+            HttpPost httpPost = new HttpPost("http://118.24.209.192:7771/app/common/addCompanyAudio");
+            String json = "{\"url\":\""+wavUrl+"\",\"id\":\""+userId+"\"}";
+            StringEntity entity = new StringEntity(json);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-type", "application/json");
+            HttpResponse response = httpClient.execute(httpPost);
+
+            if (response.getStatusLine().getStatusCode() == 200) {
+                String responseBody = EntityUtils.toString(response.getEntity());
+                JSONObject jsonObject = JSON.parseObject(responseBody);
+                Integer code = (Integer)jsonObject.get("code");
+                if (code==200){
+                    return R.ok();
+                }
+            } else {
+                return R.error();
+            }
+
+            httpClient.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return R.error();
+
+    }
+
+}

+ 322 - 0
fs-user-course/src/main/java/com/fs/course/controller/CourseController.java

@@ -0,0 +1,322 @@
+package com.fs.course.controller;
+
+
+import cn.hutool.json.JSONUtil;
+import com.fs.course.annotation.Login;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.R;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.core.utils.OrderCodeUtils;
+import com.fs.course.config.CourseConfig;
+import com.fs.course.domain.*;
+import com.fs.course.param.*;
+import com.fs.course.service.*;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.service.impl.TencentCloudCosService;
+import com.fs.course.vo.*;
+import com.fs.his.service.IFsIntegralGoodsService;
+import com.fs.his.vo.OptionsVO;
+import com.fs.sop.domain.QwSop;
+import com.fs.sop.service.IQwSopService;
+import com.fs.system.service.ISysConfigService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.jsonwebtoken.Claims;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.Synchronized;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+
+@Api("课堂接口")
+@RestController
+@RequestMapping(value="/app/course")
+public class CourseController extends  AppBaseController{
+    Logger logger= LoggerFactory.getLogger(getClass());
+    @Autowired
+    private IFsUserCourseService courseService;
+    @Autowired
+    private IFsUserCourseCategoryService courseCategoryService;
+
+    @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+
+
+    @Autowired
+    private ISysConfigService configService;
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
+
+    @Autowired
+    private IFsCourseQuestionBankService questionBankService;
+
+    @Autowired
+    private IFsCourseWatchLogService courseWatchLogService;
+
+    @Autowired
+    private IFsCourseWatchCommentService courseWatchCommentService;
+
+    @Autowired
+    private IFsIntegralGoodsService goodsService;
+
+    @Autowired
+    private IQwSopService qwSopService;
+
+
+    @ApiOperation("h5课程简介")
+    @GetMapping("/getH5CourseByVideoId")
+    public R getCourseByVideoId(@RequestParam("videoId") Long videoId,HttpServletRequest request)
+    {
+        FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
+        return R.ok().put("data",course);
+    }
+
+    @Login
+    @ApiOperation("h5课程详情加问答")
+    @GetMapping("/getH5CourseVideoDetails")
+    public R getCourseVideoDetails(FsUserCourseVideoFinishUParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        FsUserCourseVideoH5DVO course = courseService.selectFsUserCourseVideoH5DVOByVideoId(param.getVideoId());
+        List<FsUserCourseVideoQuestionVO> questionVOList = new ArrayList<>();
+        if (StringUtils.isNotEmpty(course.getQuestionBankId())){
+            String[] questionIds = course.getQuestionBankId().split(",");
+            for (String questionId : questionIds){
+                FsUserCourseVideoQuestionVO vo = new FsUserCourseVideoQuestionVO();
+                FsCourseQuestionBank questionBank = questionBankService.selectFsCourseQuestionBankById(Long.parseLong(questionId));
+                if (questionBank!=null&&questionBank.getStatus()!=0){
+                    BeanUtils.copyProperties(questionBank,vo);
+                    questionVOList.add(vo);
+                }
+            }
+        }
+        Long duration = 0L;
+        long tipsTime = 0L;
+        int isFinish = 0;
+        if (param.getLinkType()!=null&&param.getLinkType()==1){
+            return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime);
+        }
+
+        if (param.getIsRoom()!=null&&param.getIsRoom()==1&&param.getQwExternalId()==null){
+            return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime);
+        }
+        // 从Redis中获取观看时长
+        String redisKey = "h5user:watch:duration:" + param.getQwUserId()+ ":" + param.getQwExternalId() + ":" + param.getVideoId();
+        String durationStr = redisCache.getCacheObject(redisKey);
+        FsCourseWatchLog log = courseWatchLogService.getWatchCourseVideo(param.getUserId(),param.getVideoId(),param.getQwUserId(),param.getQwExternalId());
+        if (log==null){
+            return R.error("记录不存在,请联系客服!");
+        }
+        //redis取不到查库
+        if (durationStr != null) {
+            duration = Long.parseLong(durationStr);
+        }else {
+            duration = log.getDuration();
+        }
+
+        if (course.getDuration()!=null){
+            tipsTime = course.getDuration()/2;
+        }
+        //判断是否完课
+        if (log.getLogType()==2){
+            isFinish=1;
+        }
+
+        //将视频时长也存到redis
+        String videoRedisKey = "h5user:video:duration:" + param.getVideoId();
+        Long videoDuration = redisCache.getCacheObject(videoRedisKey);
+        if (videoDuration==null){
+
+            redisCache.setCacheObject(videoRedisKey,course.getDuration());
+        }
+        // 返回是否开启评论/弹幕。优先获取sop任务配置的是否开启评论/弹幕,如果没有配置就取总后台的配置;
+        QwSop qwSop = qwSopService.selectQwSopById(log.getSopId());
+        if(qwSop != null && qwSop.getOpenCommentStatus() != null) {
+            config.setOpenCommentStatus(qwSop.getOpenCommentStatus());
+        }
+
+        return R.ok().put("course",course).put("questions",questionVOList).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime).put("isFinish",isFinish);
+    }
+
+    @ApiOperation("答题")
+    @PostMapping("/courseAnswer")
+    @Login
+    public R courseAnswer(@RequestBody FsCourseQuestionAnswerUParam param)
+    {
+        logger.info("zyp \n【答题】:{}",param.getQuestions());
+        if (param.getDuration()==null){
+            logger.info("zyp \n【未识别到时长】:{}",param.getUserId());
+        }
+        param.setUserId(Long.parseLong(getUserId()));
+        return questionBankService.courseAnswer(param,false);
+    }
+
+
+    @Login
+    @ApiOperation("发放奖励")
+    @PostMapping("/sendReward")
+    @RepeatSubmit
+    public R sendReward(@RequestBody FsCourseSendRewardUParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        logger.info("zyp \n【发放奖励】:{}",param);
+        return courseVideoService.sendReward(param);
+    }
+
+    @ApiOperation("获取真实链接")
+    @GetMapping("/getRealLink")
+    public R getRealLink(@RequestParam("sortLink")String link)
+    {
+        return courseLinkService.getRealLink(link);
+    }
+
+    @Login
+    @ApiOperation("更新看课时长")
+    @PostMapping("/updateWatchDuration")
+    public R updateWatchDuration(@RequestBody FsUserCourseVideoFinishUParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        return courseVideoService.updateWatchDuration(param);
+    }
+
+    @Login
+    @ApiOperation("每十分钟获取积分")
+    @PostMapping("/getIntegralByH5Video")
+    public R getIntegralByH5Video(@RequestBody FsUserCourseVideoFinishUParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        return courseVideoService.getIntegralByH5Video(param);
+    }
+
+    /**
+     * 是否添加客服
+     */
+    @Login
+    @ApiOperation("是否添加客服")
+    @PostMapping("/isAddKf")
+    public R isAddKf(@RequestBody FsUserCourseVideoAddKfUParam param) {
+        Long userId = Long.parseLong(getUserId());
+        param.setUserId(userId);
+        return courseVideoService.isAddKf(param);
+    }
+
+    @Login
+    @ApiOperation("获取缓冲流量")
+    @PostMapping("/getInternetTraffic")
+    public R getInternetTraffic(@RequestBody FsUserCourseVideoFinishUParam param) {
+        Long userId = Long.parseLong(getUserId());
+        param.setUserId(userId);
+        return courseVideoService.getInternetTraffic(param);
+    }
+
+
+//    @Login
+//    @ApiOperation("H5看课绑定手机号")
+//    @PostMapping("/bindUserPhone")
+//    public R bindUserPhone(@RequestBody FsCourseH5PhoneParam param) {
+//        Long userId = Long.parseLong(getUserId());
+//        param.setUserId(userId);
+//        return courseVideoService.bindUserPhone(param);
+//    }
+
+    @GetMapping("/genOrderSn")
+    public R genOrderSn() {
+        String orderCode = OrderCodeUtils.getOrderSn();
+        System.out.println(orderCode);
+        redisCache.setCacheObject("orderCode"+orderCode,orderCode);
+        return R.ok(orderCode);
+    }
+
+
+    @Login
+    @PostMapping("/getErrMsg")
+    public void getErrMsg(@RequestParam("msg") String msg) {
+        Long userId = Long.parseLong(getUserId());
+        logger.error("zyp \n【看课中途报错】:{}","userId:"+userId+msg);
+    }
+
+
+    @Login
+    @ApiOperation("获取积分奖励信息")
+    @GetMapping("/getCourseIntegralGoods")
+    public R getCourseIntegralGoods() {
+        return goodsService.getCourseIntegralGoods(Long.parseLong(getUserId()));
+    }
+
+
+    @Autowired
+    TencentCloudCosService tencentCloudCosService;
+    @Autowired
+    IFsCourseLinkService linkService;
+
+    @GetMapping("/test1")
+    public void test1() {
+        linkService.updateLinks();
+    }
+
+
+    @GetMapping("/test2")
+    public void test2() {
+
+//        courseVideoService.updateVideoUrl();
+    }
+
+    @Login
+    @ApiOperation("保存评论数据")
+    @PostMapping("/saveMsg")
+    public R saveMsg(@RequestBody FsCourseWatchCommentSaveParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        return courseWatchCommentService.saveH5CourseWatchComment(param);
+    }
+
+    @Login
+    @ApiOperation("撤销评论")
+    @PutMapping("/revokeMsg")
+    public R revokeMsg(@ApiParam(value = "评论id", required = true) @RequestParam Long commentId)
+    {
+        return courseWatchCommentService.revokeH5CourseWatchComment(commentId);
+    }
+
+    @ApiOperation("获取历史评论数据")
+    @GetMapping("/getComments")
+    public R getCourseWatchComments(FsCourseWatchCommentListParam param)
+    {
+        //获取配置信息中需要查询的数据条数
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        param.setListNum(config.getViewCommentNum() != null &&  config.getViewCommentNum() != 0 ? config.getViewCommentNum() : 200);
+
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsCourseWatchCommentVO> list = courseWatchCommentService.selectH5CourseWatchComments(param);
+        PageInfo<FsCourseWatchCommentVO> pageInfo = new PageInfo<>(list);
+        return R.ok().put("data", pageInfo);
+    }
+
+
+    /**
+     * 校验是否可以答题
+     *
+     * @param param
+     * @return
+     */
+    @PostMapping("/isAllowAnswer")
+    public R isAllowAnswer(@RequestBody FsUserCourseVideoFinishUParam param) {
+        return courseVideoService.isAllowAnswer(param);
+    }
+
+}

+ 119 - 0
fs-user-course/src/main/java/com/fs/course/controller/CourseH5Controller.java

@@ -0,0 +1,119 @@
+package com.fs.course.controller;
+
+
+import cn.hutool.json.JSONUtil;
+import com.fs.common.core.domain.R;
+import com.fs.course.config.CourseConfig;
+import com.fs.course.domain.*;
+import com.fs.course.param.*;
+import com.fs.course.service.*;
+import com.fs.course.vo.*;
+import com.fs.system.service.ISysConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Api("h5课堂接口")
+@RestController
+@RequestMapping(value="/app/course/h5")
+public class CourseH5Controller extends  AppBaseController{
+    Logger logger= LoggerFactory.getLogger(getClass());
+    @Autowired
+    private IFsUserCourseService courseService;
+
+    @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+
+    @Autowired
+    private ISysConfigService configService;
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
+
+    @Autowired
+    private IFsCourseWatchLogService courseWatchLogService;
+
+    @ApiOperation("h5课程简介")
+    @GetMapping("/getH5CourseByVideoId")
+    public R getCourseByVideoId(@RequestParam("videoId") Long videoId,HttpServletRequest request)
+    {
+        FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
+        return R.ok().put("data",course);
+    }
+
+    @ApiOperation("h5课程详情")
+    @GetMapping("/getH5CourseVideoDetails")
+    public R getCourseVideoDetails(FsUserCourseVideoFinishUParam param)
+    {
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        FsUserCourseVideoH5DVO course = courseService.selectFsUserCourseVideoH5DVOByVideoId(param.getVideoId());
+
+        Long duration = 0L;
+        long tipsTime = 0L;
+        int isFinish = 0;
+        if (param.getLinkType()!=null&&param.getLinkType()==1){
+            return R.ok().put("course",course).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime);
+        }
+        // 从Redis中获取观看时长
+        String redisKey = "h5user:watch:duration:" + param.getQwUserId()+ ":" + param.getQwExternalId() + ":" + param.getVideoId();
+        String durationStr = redisCache.getCacheObject(redisKey);
+        FsCourseWatchLog log = courseWatchLogService.getWatchCourseVideoH5(param.getVideoId(),param.getQwUserId(),param.getQwExternalId());
+        //redis取不到查库
+        if (durationStr != null) {
+            duration = Long.parseLong(durationStr);
+        }else {
+            duration = log.getDuration();
+        }
+
+        if (course.getDuration()!=null){
+            tipsTime = course.getDuration()/2;
+        }
+        //判断是否完课
+        if (log.getLogType()==2){
+           isFinish=1;
+        }
+
+        //将视频时长也存到redis
+        String videoRedisKey = "h5user:video:duration:" + param.getVideoId();
+        Long videoDuration = redisCache.getCacheObject(videoRedisKey);
+        if (videoDuration==null){
+            redisCache.setCacheObject(videoRedisKey,course.getDuration());
+        }
+
+        return R.ok().put("course",course).put("config",config).put("playDuration",duration).put("tipsTime",tipsTime).put("isFinish",isFinish);
+    }
+
+
+
+    @ApiOperation("获取真实链接")
+    @GetMapping("/getRealLink")
+    public R getRealLink(@RequestParam("sortLink")String link)
+    {
+        return courseLinkService.getRealLink(link);
+    }
+
+
+    @ApiOperation("更新看课时长")
+    @PostMapping("/updateWatchDuration")
+    public R updateWatchDuration(@RequestBody FsUserCourseVideoFinishUParam param)
+    {
+        return courseVideoService.updateWatchDuration(param);
+    }
+
+
+    @ApiOperation("获取缓冲流量")
+    @PostMapping("/getInternetTraffic")
+    public R getInternetTraffic(@RequestBody FsUserCourseVideoFinishUParam param) {
+        return courseVideoService.getInternetTraffic(param);
+    }
+
+    @PostMapping("/getErrMsg")
+    public void getErrMsg(@RequestParam("msg") String msg) {
+        logger.error("zyp \n【h5看课中途报错】:{}",msg);
+    }
+}

+ 129 - 0
fs-user-course/src/main/java/com/fs/course/controller/CourseWxH5Controller.java

@@ -0,0 +1,129 @@
+package com.fs.course.controller;
+
+
+import com.fs.course.annotation.Login;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.course.param.FsCourseQuestionAnswerUParam;
+import com.fs.course.param.FsCourseSendRewardUParam;
+import com.fs.course.param.FsUserCourseVideoFinishUParam;
+import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
+import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
+import com.fs.course.param.newfs.FsUserCourseVideoUParam;
+import com.fs.course.service.*;
+import com.fs.course.vo.FsUserCourseVideoH5VO;
+import com.fs.course.vo.newfs.FsUserCourseVideoLinkDetailsVO;
+import com.fs.his.service.IFsUserService;
+import com.fs.system.service.ISysConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+@Api("会员-h5看课接口")
+@RestController
+@RequestMapping(value = "/app/course/wx/h5")
+public class CourseWxH5Controller extends AppBaseController {
+    Logger logger = LoggerFactory.getLogger(getClass());
+    @Autowired
+    private IFsUserCourseService courseService;
+
+    @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+
+    @Autowired
+    private IFsCourseLinkService courseLinkService;
+
+    @Autowired
+    private IFsCourseWatchLogService courseWatchLogService;
+
+    @Autowired
+    private IFsCourseQuestionBankService questionBankService;
+
+    @Autowired
+    private IFsUserService fsUserService;
+
+
+    @Login
+    @ApiOperation("判断是否添加客服(是否关联销售)")
+    @PostMapping("/isAddKf")
+    public ResponseResult<Boolean> isAddCompanyUser(@Valid @RequestBody FsUserCourseAddCompanyUserParam param) {
+        Long userId = Long.parseLong(getUserId());
+        param.setUserId(userId);
+        return courseVideoService.isAddCompanyUser(param);
+    }
+
+    @ApiOperation("h5课程简介")
+    @GetMapping("/getH5CourseByVideoId")
+    public R getCourseByVideoId(@RequestParam("videoId") Long videoId)
+    {
+        FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
+        return R.ok().put("data",course);
+    }
+
+    @Login
+    @ApiOperation("H5课程详情")
+    @GetMapping("/videoDetails")
+    public ResponseResult<FsUserCourseVideoLinkDetailsVO> getCourseVideoDetails(FsUserCourseVideoLinkParam param) {
+        param.setFsUserId(Long.parseLong(getUserId()));
+        return courseVideoService.getLinkCourseVideoDetails(param);
+    }
+
+    @ApiOperation("获取真实链接")
+    @GetMapping("/getRealLink")
+    public R getRealLink(@RequestParam("sortLink")String link)
+    {
+        return courseLinkService.getRealLinkH5(link);
+    }
+
+    @ApiOperation("更新看课时长")
+    @PostMapping("/updateWatchDuration")
+    public R updateWatchDuration(@RequestBody FsUserCourseVideoUParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        return courseVideoService.updateWatchDurationWx(param);
+    }
+
+
+    @ApiOperation("获取缓冲流量")
+    @PostMapping("/getInternetTraffic")
+    public R getInternetTraffic(@RequestBody FsUserCourseVideoFinishUParam param) {
+        param.setUserId(Long.parseLong(getUserId()));
+        return courseVideoService.getInternetTraffic(param);
+    }
+
+
+    @ApiOperation("答题")
+    @PostMapping("/courseAnswer")
+    public R courseAnswer(@RequestBody FsCourseQuestionAnswerUParam param){
+        param.setUserId(Long.parseLong(getUserId()));
+        logger.info("zyp \n【答题】:{}",param.getQuestions());
+        if (param.getDuration()==null){
+            logger.info("zyp \n【未识别到时长】:{}",param.getUserId());
+        }
+        return questionBankService.courseAnswer(param, true);
+    }
+
+    @ApiOperation("发放奖励")
+    @PostMapping("/sendReward")
+    @RepeatSubmit
+    public R sendReward(@RequestBody FsCourseSendRewardUParam param)
+    {
+        param.setUserId(Long.parseLong(getUserId()));
+        logger.info("zyp \n【发放奖励】:{}",param);
+        return courseVideoService.sendRewardByFsUser(param);
+    }
+
+
+    @PostMapping("/getErrMsg")
+    public void getErrMsg(@RequestParam("msg") String msg) {
+        logger.error("zyp \n【h5看课中途报错】:{}",msg);
+    }
+
+
+}

+ 67 - 0
fs-user-course/src/main/java/com/fs/course/controller/H5Controller.java

@@ -0,0 +1,67 @@
+package com.fs.course.controller;
+
+import cn.hutool.json.JSONUtil;
+import com.fs.his.config.AgreementConfig;
+import com.fs.system.service.ISysConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+@Controller
+@RequestMapping(value="/web")
+public class H5Controller
+{
+
+    @Autowired
+    private ISysConfigService configService;
+
+
+    @GetMapping("/userAgreement")
+    public ModelAndView userAgreement( )
+    {
+
+        String json=configService.selectConfigByKey("his.agreementConfig");
+        AgreementConfig config= JSONUtil.toBean(json, AgreementConfig.class);
+
+        ModelAndView mv=new ModelAndView();
+        mv.addObject("userAgreement", config.getUserRegister());
+        mv.setViewName("userAgreement");
+        return mv;
+    }
+    @GetMapping("/privacyPolicy")
+    public ModelAndView privacyPolicy( )
+    {
+        String json=configService.selectConfigByKey("his.agreementConfig");
+        AgreementConfig config= JSONUtil.toBean(json, AgreementConfig.class);
+        ModelAndView mv=new ModelAndView();
+        mv.addObject("privacyPolicy", config.getUserPrivacy());
+        mv.setViewName("privacyPolicy");
+        return mv;
+    }
+
+    @GetMapping("/healthCustomerService")
+    public ModelAndView healthCustomerService( )
+    {
+        String json=configService.selectConfigByKey("his.agreementConfig");
+        AgreementConfig config= JSONUtil.toBean(json, AgreementConfig.class);
+        ModelAndView mv=new ModelAndView();
+        mv.addObject("healthCustomerService", config.getUserHealth());
+        mv.setViewName("healthCustomerService");
+        return mv;
+    }
+
+    @GetMapping("/vipService")
+    public ModelAndView vipService( )
+    {
+        String json=configService.selectConfigByKey("his.agreementConfig");
+        AgreementConfig config= JSONUtil.toBean(json, AgreementConfig.class);
+        ModelAndView mv=new ModelAndView();
+        mv.addObject("vipService", config.getVipService());
+        mv.setViewName("vipService");
+        return mv;
+    }
+
+
+}

+ 245 - 0
fs-user-course/src/main/java/com/fs/course/controller/UserController.java

@@ -0,0 +1,245 @@
+package com.fs.course.controller;
+
+
+import com.fs.course.annotation.Login;
+import com.fs.course.param.FsDoctorRegisterParam;
+import com.fs.course.param.FsUserEditParam;
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.sign.Md5Utils;
+import com.fs.his.domain.FsDoctor;
+import com.fs.his.domain.FsUser;
+import com.fs.his.param.FsUserCouponUParam;
+import com.fs.his.param.FsUserEditPushParam;
+import com.fs.his.service.IFsDoctorService;
+import com.fs.his.service.IFsPackageService;
+import com.fs.his.service.IFsUserCouponService;
+import com.fs.his.service.IFsUserService;
+import com.fs.his.vo.FsUserCouponCountUVO;
+import com.fs.his.vo.FsUserCouponListUVO;
+import com.fs.qw.service.IQwAppContactWayService;
+import com.fs.system.oss.CloudStorageService;
+import com.fs.system.oss.OSSFactory;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.imageio.ImageIO;
+import javax.servlet.http.HttpServletRequest;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.fs.common.utils.SecurityUtils.getUserId;
+import static com.fs.his.utils.PhoneUtil.decryptPhoneMk;
+
+
+@Api("个人中心")
+@RestController
+@RequestMapping(value="/app/user")
+public class UserController extends  AppBaseController {
+
+
+    @Autowired
+    private IFsUserService userService;
+    @Autowired
+    private IFsDoctorService doctorService;
+    @Autowired
+    private IFsPackageService packageService;
+    @Autowired
+    private IFsUserCouponService couponService;
+    @Autowired
+    private IQwAppContactWayService qwAppContactWayService;
+
+    /**
+     * 获取用户信息
+     * @param request
+     * @return     */
+    @Login
+    @ApiOperation("获取用户信息")
+    @GetMapping("/getUserInfo")
+    public R getUserInfo(HttpServletRequest request){
+        try {
+            FsUser user=userService.selectFsUserByUserId(Long.parseLong(getUserId()));
+            if (user.getPhone()!=null&&user.getPhone().length()>11&&!user.getPhone().matches("\\d+")){
+                user.setPhone(decryptPhoneMk(user.getPhone()));
+            }
+            Map<String,Object> map=new HashMap<>();
+            map.put("user",user);
+            return R.ok(map);
+        } catch (Exception e){
+            return R.error("操作异常");
+        }
+    }
+
+    @Login
+    @ApiOperation("查看推送设置")
+    @GetMapping("/getUserPushSetting")
+    public R getUserPushSetting(HttpServletRequest request){
+        FsUser user=userService.selectFsUserByUserId(Long.parseLong(getUserId()));
+        Map<String,Object> map=new HashMap<>();
+        map.put("isPush",user.getIsPush());
+        map.put("isIndividuationPush",user.getIsIndividuationPush());
+        return R.ok().put("data",map);
+    }
+
+    @Login
+    @ApiOperation("保存推送设置")
+    @PostMapping("/editUserPushSetting")
+    public R editUserPushSetting(@RequestBody FsUserEditPushParam param, HttpServletRequest request){
+        FsUser map=new FsUser();
+        BeanUtils.copyProperties(param,map);
+        map.setUserId(Long.parseLong(getUserId()));
+        if(userService.updateFsUser(map)>0){
+            return R.ok("保存成功");
+        }
+        else{
+            return R.error("保存失败");
+        }
+    }
+
+    @ApiOperation("检测是否登录")
+    @GetMapping("/checkLogin")
+    public R checkLogin(HttpServletRequest request){
+        if(StringUtils.isEmpty(getUserId())){
+            //未登录
+            return R.error("未登录");
+        }
+        else{
+            //登录
+            String token = jwtUtils.generateToken(Long.parseLong(getUserId()));
+            Map<String,Object> map=new HashMap<>();
+            map.put("token",token);
+            return R.ok("认证成功").put("userId",getUserId()).put("token",token);
+        }
+    }
+
+
+
+
+    @Login
+    @ApiOperation("修改用户信息")
+    @PostMapping("/editUser")
+    public R editUser(@RequestBody FsUserEditParam param, HttpServletRequest request){
+        FsUser map=new FsUser();
+        BeanUtils.copyProperties(param,map);
+        map.setUserId(Long.parseLong(getUserId()));
+        if(userService.updateFsUser(map)>0){
+            return R.ok("修改成功");
+        }
+        else{
+            return R.error("修改失败");
+        }
+    }
+
+
+    @Login
+    @ApiOperation("注册医生")
+    @PostMapping("/registerDoctor")
+    public R registerDoctor(@Validated  @RequestBody FsDoctorRegisterParam param, HttpServletRequest request){
+        FsDoctor doctor= doctorService.selectFsDoctorByUserId(Long.parseLong(getUserId()));
+        if(doctor!=null){
+            if(doctor.getIsAudit().equals(0)){
+                return R.error("您已提交申请,等待审核...");
+            }
+            else if(doctor.getIsAudit().equals(1)){
+                return R.error("您已提交申请");
+            }
+        }
+        doctor=new FsDoctor();
+        BeanUtils.copyProperties(param,doctor);
+        doctor.setUserId(Long.parseLong(getUserId()));
+        doctor.setIsAudit(0);
+        doctor.setPassword(Md5Utils.hash(param.getPassword()));
+        doctor.setStatus(0);
+        doctor.setBalance(new BigDecimal(0));
+        doctor.setPriceJson("[{\"price\":1.00,\"type\":1},{\"price\":1.00,\"type\":2}]");
+        if(doctorService.insertFsDoctor(doctor)>0){
+            return R.ok("注册成功");
+        }
+        else{
+            return R.error("注册失败");
+        }
+    }
+    @Login
+    @ApiOperation("获取用户优惠券")
+    @GetMapping("/getMyCouponList")
+    public R getMyCouponList(FsUserCouponUParam param, HttpServletRequest request){
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        param.setUserId(Long.parseLong(getUserId()));
+        List<FsUserCouponListUVO> list=couponService.selectFsUserCouponListUVO(param);
+        PageInfo<FsUserCouponListUVO> listPageInfo=new PageInfo<>(list);
+        return R.ok().put("data",listPageInfo);
+    }
+
+    @Login
+    @ApiOperation("获取用户可用优惠券")
+    @GetMapping("/getMyEnableCouponList")
+    public R getMyEnableCouponList(FsUserCouponUParam param, HttpServletRequest request){
+        param.setUserId(Long.parseLong(getUserId()));
+        List<FsUserCouponListUVO> list=couponService.getMyEnableCouponList(param);
+        return R.ok().put("data",list);
+    }
+    @Login
+    @ApiOperation("获取用户优惠券数量")
+    @GetMapping("/getMyCouponCount")
+    public R getMyCouponCount(FsUserCouponUParam param, HttpServletRequest request){
+        if (StringUtils.isNotEmpty(getUserId())) {
+            param.setUserId(Long.parseLong(getUserId()));
+        }
+        FsUserCouponCountUVO count =couponService.selectFsUserCouponCountUVO(param);
+        return R.ok().put("data",count);
+    }
+
+    @ApiOperation("获取APP客服活码")
+    @GetMapping("/getAppContactWay/{userId}")
+    public R getAppContactWay(@PathVariable("userId") Long userId)
+    {
+        if (userId==null||userId==0){
+            return R.error("UserId不能为空");
+        }
+        String appContactWayImgCode = qwAppContactWayService.getAppContactWayImgCode(userId);
+       // String appContactWayImgCode ="https://wework.qpic.cn/wwpic3az/769765_uPSW3IV5QP6-bsM_1733303905/0";
+        File mb = new File("C:\\fs\\qwCode.png");
+        if (!mb.exists()) {
+            // 创建目录
+            throw  new CustomException("模板文件不存在");
+        }
+        BufferedImage image = null;
+        try {
+            image = ImageIO.read(mb);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        Graphics2D pen = image.createGraphics();
+        try {
+            URL imageUrl = new URL(appContactWayImgCode);
+            BufferedImage img = ImageIO.read(imageUrl);
+            pen.drawImage(img, 1550, 3270, 500, 500, null);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        try {
+            ImageIO.write(image, "png", os);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        InputStream inputStream = new ByteArrayInputStream(os.toByteArray());
+        CloudStorageService storage = OSSFactory.build();
+        String url = storage.uploadSuffix(inputStream, ".jpg");
+        return R.ok().put("data",url);
+    }
+
+}

+ 202 - 0
fs-user-course/src/main/java/com/fs/course/controller/WxCompanyUserController.java

@@ -0,0 +1,202 @@
+package com.fs.course.controller;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
+import cn.hutool.core.date.DateTime;
+import com.alibaba.fastjson.JSON;
+import com.fs.course.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.param.LoginMaWxParam;
+import com.fs.common.utils.IpUtil;
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyDeptService;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyUserService;
+import com.fs.core.config.WxMaConfiguration;
+import com.fs.course.config.CourseMaConfig;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
+
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import com.fs.wx.miniapp.config.WxMaProperties;
+import io.jsonwebtoken.Claims;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Date;
+import java.util.List;
+
+@Api("微信小程序相关接口")
+@RestController
+@RequestMapping(value = "/app/wx/miniapp")
+@Slf4j
+public class WxCompanyUserController extends AppBaseController {
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private WxMaProperties maProperties;
+
+    @Autowired
+    JwtUtils jwtUtils;
+
+    @Autowired
+    RedisCache redisCache;
+
+    @Autowired
+    private ICompanyUserService companyUserService;
+
+    @Autowired
+    private ICompanyDeptService companyDeptService;
+
+    @Autowired
+    private IFsUserService userService;
+
+    @Autowired
+    ICompanyService companyService;
+
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+
+    @ApiOperation("小程序-授权登录")
+    @PostMapping("/loginByMa")
+    public R login(@RequestBody LoginMaWxParam param) {
+        log.info("=====================进入小程序授权登录, 入参: {}", param);
+        if (StringUtils.isBlank(param.getCode())) {
+            return R.error("code不存在");
+        }
+        SysConfig sysConfig3 = sysConfigMapper.selectConfigByConfigKey("courseMa.config");
+        List<CourseMaConfig> courseMaConfigs = JSON.parseArray(sysConfig3.getConfigValue(), CourseMaConfig.class);
+        if (courseMaConfigs.isEmpty()){
+            return R.error("小程序配置为空");
+        }
+        CourseMaConfig courseMaConfig = courseMaConfigs.get(0);
+        //获取第二个小程序配置,序号从0开始
+        final WxMaService wxService = WxMaConfiguration.getMaService(courseMaConfig.getAppid());
+        try {
+            WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
+            this.logger.info(session.getSessionKey());
+            this.logger.info(session.getOpenid());
+            this.logger.info(session.getUnionid());
+            // 解密
+            WxMaPhoneNumberInfo phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+            FsUser user = userService.selectFsUserByPhone(phoneNoInfo.getPhoneNumber());
+
+            // 特殊(需求设计:需要根据公司是否开启黑名单来设置会员初始化的状态)
+            Company company = null;
+            if(param.getCompanyId() != null){
+                company = companyService.selectCompanyById(param.getCompanyId());
+            }
+
+            // 根据销售后台设置的  是否需要单独注册会员 来判断是否需要设置销售的值
+            CompanyUser companyUser = null;
+            if(param.getCompanyUserId() != null){
+                companyUser = companyUserService.selectCompanyUserById(param.getCompanyUserId());
+            }
+
+            String ip = IpUtil.getRequestIp();
+            if (user == null) {
+                if(StringUtils.isNotEmpty(session.getUnionid())){
+                    user = userService.selectFsUserByUnionId(session.getUnionid());
+                } else {
+                    user = userService.selectFsUserByCourseMaOpenId(session.getOpenid());
+                }
+                if (user != null) {
+                    //修改
+                    FsUser userMap = new FsUser();
+                    userMap.setUserId(user.getUserId());
+                    userMap.setCourseMaOpenId(session.getOpenid());
+                    userMap.setUnionId(session.getUnionid());
+                    userMap.setUpdateTime(new DateTime());
+                    userMap.setNickName(param.getNickname() != null ? param.getNickname() : "微信用户");
+                    userMap.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
+                    userMap.setPhone(phoneNoInfo.getPhoneNumber());
+                    // 逻辑调整:如果会员已经绑定了销售,直接提示,不让注册-2025年6月16日14点53分
+                    if (user.getCompanyUserId() != null && !param.getCompanyUserId().equals(user.getCompanyUserId())){
+                        return R.error(406, "该用户已成为其他销售会员");
+                    }
+                    if(companyUser != null && companyUser.getIsNeedRegisterMember() != null && companyUser.getIsNeedRegisterMember() != 1){
+                        user.setCompanyId(param.getCompanyId());
+                        user.setCompanyUserId(param.getCompanyUserId());
+                    }
+                    userService.updateFsUser(userMap);
+                } else {
+                    //新增
+                    user = new FsUser();
+                    user.setNickName(param.getNickname() != null ? param.getNickname() : "微信用户");
+                    user.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
+                    user.setStatus((company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1);
+                    user.setCourseMaOpenId(session.getOpenid());
+                    user.setUnionId(session.getUnionid());
+                    user.setCreateTime(new Date());
+                    user.setPhone(phoneNoInfo.getPhoneNumber());
+                    if(companyUser != null && companyUser.getIsNeedRegisterMember() != null && companyUser.getIsNeedRegisterMember() != 1){
+                        user.setCompanyId(param.getCompanyId());
+                        user.setCompanyUserId(param.getCompanyUserId());
+                    }
+                    userService.insertFsUser(user);
+                }
+            } else {
+                FsUser userMap = new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setUpdateTime(new DateTime());
+                userMap.setPhone(phoneNoInfo.getPhoneNumber());
+                userMap.setLastIp(ip);
+                if (StringUtils.isNotEmpty(session.getUnionid())) {
+                    userMap.setUnionId(session.getUnionid());
+                }
+                userService.updateFsUser(userMap);
+            }
+            log.info("保存成功的用户信息user: {}, 用户id: {}", user, user.getUserId());
+            String token = jwtUtils.generateToken(user.getUserId());
+            // 返回一个写死的数据到前端
+            return R.ok("登录成功").put("token", token).put("phoneNumber", phoneNoInfo.getPhoneNumber()).put("nickName", "微信用户").put("user", user);
+        } catch (WxErrorException e) {
+            this.logger.error(e.getMessage(), e);
+            return R.error("授权失败," + e.getMessage());
+        }
+    }
+
+//    @Login(isMiniLogin = true)
+//    @ApiOperation("获取销售通过小程序登录后的用户信息")
+//    @GetMapping("/getMaUser")
+//    public R getUserInfo() {
+//        try {
+//            CompanyUser companyUser = companyUserService.selectCompanyUserById(Long.parseLong(getUserId()));
+//            if (companyUser == null) {
+//                return R.error(401, "用户信息不存在");
+//            }
+//            return R.ok().put("user", companyUser);
+//        } catch (Exception e) {
+//            return R.error("操作异常");
+//        }
+//    }
+
+    /**
+     * 特殊要求:销售小程序临时登录,登录后页面中还有一个之前常用的登录,所以为了区分,token名称不能跟之前的一样
+     *
+     * @return 用户id
+     */
+    public String getUserId() {
+        String headValue = ServletUtils.getRequest().getHeader("UserToken");
+        Claims claims = jwtUtils.getClaimByToken(headValue);
+        String userId = claims.getSubject().toString();
+        return userId;
+    }
+
+
+}

+ 267 - 0
fs-user-course/src/main/java/com/fs/course/controller/WxMpController.java

@@ -0,0 +1,267 @@
+package com.fs.course.controller;
+
+import cn.hutool.core.date.DateTime;
+import com.fs.course.param.FsUserLoginByMpParam;
+import com.fs.course.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.course.mapper.FsCourseSopLogsMapper;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
+import com.fs.his.utils.ConfigUtil;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.sop.params.QwSopSettingTimeParam;
+import com.fs.system.mapper.SysConfigMapper;
+import com.fs.wx.mp.WxEeventType;
+import com.fs.wx.mp.WxMessageType;
+import com.fs.wx.mp.WxServiceMsgDto;
+import io.swagger.annotations.ApiOperation;
+import lombok.Synchronized;
+import me.chanjar.weixin.common.bean.WxJsapiSignature;
+import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
+import me.chanjar.weixin.common.bean.menu.WxMenu;
+import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpMenuService;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.WxMpUserService;
+import me.chanjar.weixin.mp.bean.result.WxMpUser;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import static com.fs.course.controller.WxUserController.genCode;
+
+
+@RestController
+@RequestMapping("/app/wx/mp")
+public class WxMpController {
+  Logger logger= LoggerFactory.getLogger(getClass());
+  @Autowired
+  private WxMpService wxMpService;
+  private static final String MP_TOKEN = "U2qmxEbsp0PJFoLRvUDvIjVi9XPzuVc2";
+
+  private static final String EncodingAESKey = "P3HE7Gd1PJVQqCLoOMop5uYfjx9LwfY53rnC3VUuLZS";
+
+  @Autowired
+  private IFsUserService userService;
+
+  @Autowired
+  JwtUtils jwtUtils;
+  @Autowired
+  RedisCache redisCache;
+
+  @Autowired
+  private ConfigUtil configUtil;
+
+  @Autowired
+  private SysConfigMapper sysConfigMapper;
+
+  @Autowired
+  FsCourseWatchLogMapper fsCourseWatchLogMapper;
+  @Autowired
+  QwExternalContactMapper qwExternalContactMapper;
+  @Autowired
+  private FsCourseSopLogsMapper courseSopLogsMapper;
+
+
+  //公众号事件回调
+  @PostMapping
+  public String checkWxToken(@RequestParam(value = "signature", required = false) String signature,
+                             @RequestParam(value = "timestamp", required = false) String timestamp,
+                             @RequestParam(name = "nonce", required = false) String nonce,
+                             @RequestParam(value = "echostr", required = false) String echostr,
+                             @RequestBody(required = false) WxServiceMsgDto wxServiceMsgDto) throws WxErrorException{
+    // 只处理订阅与取消订阅消息
+    if (null != wxServiceMsgDto && StringUtils.isNotEmpty(wxServiceMsgDto.getMsgType()) &&
+            StringUtils.isNotEmpty(wxServiceMsgDto.getEvent())) {
+      String msgType = wxServiceMsgDto.getMsgType();
+      String event = wxServiceMsgDto.getEvent();
+      // 判断是否是订阅或取消订阅事件
+      boolean isSubscribeEvent = WxMessageType.EVENT.getCode().equals(msgType) &&
+              (WxEeventType.SUBSCRIBE.getCode().equals(event) || WxEeventType.UNSUBSCRIBE.getCode().equals(event));
+      if (isSubscribeEvent) {
+        WxEeventType wxEeventType = WxEeventType.SUBSCRIBE.getCode().equals(event) ? WxEeventType.SUBSCRIBE : WxEeventType.UNSUBSCRIBE;
+        logger.info("公众号关注回调:{}",wxEeventType);
+        logger.info("公众号关注回调详情:{}",wxServiceMsgDto);
+        WxMpUserService wxMpUserService = wxMpService.getUserService();
+        WxMpUser wxMpUser = wxMpUserService.userInfo(wxServiceMsgDto.getFromUserName());
+        logger.info("获取用户信息:{}",wxMpUser);
+        if (wxMpUser != null && wxMpUser.getUnionId()!=null) {
+          FsUser user = userService.selectFsUserByUnionid(wxMpUser.getUnionId());
+          if (user != null) {
+            // 根据事件类型更新 isOfficialAccountAuth 字段
+            if (WxEeventType.SUBSCRIBE.equals(wxEeventType)) {
+              user.setIsOfficialAccountAuth(1); // 设置为已关注
+            } else if (WxEeventType.UNSUBSCRIBE.equals(wxEeventType)) {
+              user.setIsOfficialAccountAuth(0); // 设置为取消关注
+            }
+            userService.updateFsUser(user); // 保存更新后的用户信息
+          }else {
+            //写入
+            String code=genCode();
+            user=new FsUser();
+//            user.setPhone(code);
+            user.setNickName("匿名用户**");
+            user.setAvatar("https://hos-1309931967.cos.ap-chongqing.myqcloud.com/fs/20230725/a848605591384ec29d49773dd58d9345.jpg");
+            user.setStatus(1);
+            user.setMpOpenId(wxMpUser.getOpenId());
+            user.setUnionId(wxMpUser.getUnionId());
+            user.setIsOfficialAccountAuth(wxMpUser.getSubscribe()? 1 : 0);
+            user.setCreateTime(new Date());
+            userService.insertFsUser(user);
+          }
+        }
+      }
+    }
+    return "success";
+  }
+
+  @GetMapping
+  public String checkWxToken(
+          @RequestParam("signature") String signature,
+          @RequestParam("timestamp") String timestamp,
+          @RequestParam("nonce") String nonce,
+          @RequestParam("echostr") String echostr) {
+    logger.info("数据回调URLServer-微信调用dataGet请求");
+    boolean check = wxMpService.checkSignature(timestamp,nonce,signature);
+    logger.info("验证结果:{}",check);
+    if (check){
+      return echostr;
+    }
+    return "error";
+  }
+
+  @PostMapping("/createMenu")
+  public R createMenu(@RequestBody WxMenu wxMenu) throws WxErrorException{
+    WxMpMenuService wxMpMenuService = wxMpService.getMenuService();
+    try{
+      wxMpMenuService.menuCreate(wxMenu);
+      return R.ok("创建成功");
+    }catch (WxErrorException e){
+      logger.error(e.getMessage());
+      return R.error("创建失败");
+    }
+  }
+
+  @ApiOperation("课程短链公众号登录")
+  @PostMapping("/loginByMp")
+  @Transactional
+  public R loginByMp( @RequestBody FsUserLoginByMpParam param) {
+
+    if (StringUtils.isBlank(param.getCode())) {
+      return R.error("code不存在");
+    }
+    try{
+      WxOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(param.getCode());
+      WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(wxMpOAuth2AccessToken, null);
+      WxMpUserService wxMpUserService = wxMpService.getUserService();
+      WxMpUser userInfo = wxMpUserService.userInfo(wxMpUser.getOpenid());
+//      if (!userInfo.getSubscribe()){
+//        return R.error("请关注公众号进行登录");
+//      }
+      FsUser user=userService.selectFsUserByUnionid(wxMpUser.getUnionId());
+      if(user!=null){
+        FsUser userMap=new FsUser();
+        userMap.setUserId(user.getUserId());
+        userMap.setNickName(wxMpUser.getNickname());
+        userMap.setAvatar(wxMpUser.getHeadImgUrl());
+        userMap.setMpOpenId(wxMpUser.getOpenid());
+        userMap.setUpdateTime(new DateTime());
+        userService.updateFsUser(userMap);
+      }
+      else{
+        //写入
+        String code=genCode();
+        user=new FsUser();
+//        user.setPhone(code);
+        user.setNickName(wxMpUser.getNickname());
+        user.setAvatar(wxMpUser.getHeadImgUrl());
+        user.setStatus(1);
+        user.setSex(wxMpUser.getSex());
+        user.setMpOpenId(wxMpUser.getOpenid());
+        user.setUnionId(wxMpUser.getUnionId());
+        user.setCreateTime(new Date());
+        userService.insertFsUser(user);
+      }
+      System.out.println("user:id:"+user.getUserId());
+      String token = jwtUtils.generateToken(user.getUserId());
+      redisCache.setCacheObject("token:"+user.getUserId(),token,604800, TimeUnit.SECONDS);
+      Map<String,Object> map=new HashMap<>();
+      map.put("token",token);
+      map.put("user",user);
+      logger.info("zyp \n 【点播公众号登录】:{}",user.getUserId());
+      return R.ok(map);
+
+    }
+    catch (WxErrorException e){
+      if(e.getError().getErrorCode()==40163){
+        return R.error(40163,e.getError().getErrorMsg());
+      }
+      else{
+        return R.error("授权失败,"+e.getMessage());
+      }
+    }
+
+  }
+
+
+
+  private Date setSendTime(QwSopSettingTimeParam params) {
+    Date currentTime = new Date();
+    Calendar calendar = Calendar.getInstance();
+    calendar.setTime(currentTime);
+
+    try {
+      if (params.getType() == 1) {
+        int hourToAdd = Integer.parseInt(params.getHour());
+        int minuteToAdd = Integer.parseInt(params.getMinute());
+        calendar.add(Calendar.HOUR, hourToAdd);
+        calendar.add(Calendar.MINUTE, minuteToAdd);
+      } else if (params.getType() == 2) {
+        calendar.add(Calendar.DAY_OF_MONTH, Integer.parseInt(params.getDay()));
+        SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm");
+        Date parsedTime = timeFormatter.parse(params.getTime());
+        Calendar timeCalendar = Calendar.getInstance();
+        timeCalendar.setTime(parsedTime);
+        calendar.set(Calendar.HOUR_OF_DAY, timeCalendar.get(Calendar.HOUR_OF_DAY));
+        calendar.set(Calendar.MINUTE, timeCalendar.get(Calendar.MINUTE));
+        calendar.set(Calendar.SECOND, 0);
+      }
+    } catch (Exception e) {
+      e.printStackTrace(); // 或者更好的异常处理
+    }
+
+    return calendar.getTime();
+  }
+    @GetMapping("/getWxConfig")
+    @Synchronized
+    public R getWxConfig(@RequestParam String url) throws WxErrorException {
+      try {
+        String sLink = URLDecoder.decode(url, "UTF-8");
+        final WxJsapiSignature jsapiSignature = wxMpService.createJsapiSignature(sLink);
+        return R.ok().put("data", jsapiSignature);
+      } catch (UnsupportedEncodingException e) {
+        // URL解码异常
+        return R.error(e.getMessage());
+      }
+
+    }
+
+
+
+
+}

+ 371 - 0
fs-user-course/src/main/java/com/fs/course/controller/WxUserController.java

@@ -0,0 +1,371 @@
+package com.fs.course.controller;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
+import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
+import cn.hutool.core.date.DateTime;
+import com.alibaba.fastjson.JSON;
+import com.fs.course.annotation.Login;
+import com.fs.course.utils.JwtUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.param.LoginParam;
+import com.fs.common.utils.DateUtils;
+import com.fs.core.config.WxMaConfiguration;
+import com.fs.course.config.CourseMaConfig;
+import com.fs.his.config.FsSysConfig;
+import com.fs.his.domain.FsUser;
+import com.fs.his.domain.FsUserLoginLog;
+import com.fs.his.mapper.FsUserLoginLogMapper;
+import com.fs.his.service.IFsUserService;
+import com.fs.his.utils.ConfigUtil;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.Synchronized;
+import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
+import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+import static com.fs.his.utils.PhoneUtil.encryptPhone;
+
+/**
+ * 微信小程序用户接口
+ *
+ */
+@Api("微信接口")
+@RestController
+@RequestMapping(value="/app/wx")
+public class WxUserController extends AppBaseController{
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+    @Autowired
+    private ConfigUtil configUtil;
+    @Autowired
+    private IFsUserService userService;
+    @Autowired
+    FsUserLoginLogMapper fsUserLoginLogMapper;
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+    @Autowired
+    JwtUtils jwtUtils;
+    @Autowired
+    RedisCache redisCache;
+
+    @Autowired
+    private WxMpService wxMpService;
+
+
+
+
+    /**
+     * 登陆接口
+     */
+    /**
+     * 登陆接口
+     */
+    @ApiOperation("登录")
+    @PostMapping("/login")
+    @Transactional
+    public R login( @RequestBody LoginParam param) {
+        if (StringUtils.isBlank(param.getCode())) {
+            return R.error("code不存在");
+        }
+        FsSysConfig con = configUtil.getSysConfig();
+        final WxMaService wxService = WxMaConfiguration.getMaService(con.getAppid());
+        try {
+            WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
+            this.logger.info(session.getSessionKey());
+            this.logger.info(session.getOpenid());
+            // 解密
+            WxMaPhoneNumberInfo phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+            //三端用户同步,先用unionid查询
+            FsUser user = new FsUser();
+            if (session.getUnionid()!=null){
+                user = userService.selectFsUserByUnionid(session.getUnionid());
+                if (user==null){
+                    user = userService.selectFsUserByOpenId(session.getOpenid());
+                }
+            }else {
+                user = userService.selectFsUserByOpenId(session.getOpenid());
+            }
+
+            if(user==null){
+                //新用户
+                String phoneNumber = phoneNoInfo.getPhoneNumber();
+
+
+                //查询手机号是否存在,如果存在,更新
+                FsUser checkPhone=userService.selectFsUserByPhone(encryptPhone(phoneNumber));
+                if (checkPhone==null){
+                    checkPhone=userService.selectFsUserByPhone(phoneNumber);
+                }
+                if(checkPhone!=null){
+                    user=checkPhone;
+                    FsUser userMap=new FsUser();
+                    userMap.setMaOpenId(session.getOpenid());
+                    userMap.setUserId(checkPhone.getUserId());
+                    userMap.setUpdateTime(new DateTime());
+                    if(session.getUnionid()!=null){
+                        userMap.setUnionId(session.getUnionid());
+                    }
+                    userService.updateFsUser(userMap);
+                }
+                else{
+                    //写入
+                    user=new FsUser();
+                    user.setPhone(phoneNoInfo.getPhoneNumber());
+                    user.setNickName("微信用户"+phoneNoInfo.getPhoneNumber().substring(phoneNoInfo.getPhoneNumber().length()-4));
+                    user.setAvatar("https://hos-1309931967.cos.ap-chongqing.myqcloud.com/fs/20230725/a848605591384ec29d49773dd58d9345.jpg");
+                    user.setStatus(1);
+                    user.setMaOpenId(session.getOpenid());
+                    user.setCreateTime(new Date());
+                    if(session.getUnionid()!=null){
+                        user.setUnionId(session.getUnionid());
+                    }
+                    userService.insertFsUser(user);
+                }
+            }
+            else{
+                if(user.getStatus().equals(0)){
+
+                    return R.error("此会员已禁用");
+                }
+                FsUser userMap=new FsUser();
+                userMap.setUserId(user.getUserId());
+                if(session.getUnionid()!=null){
+                    userMap.setUnionId(session.getUnionid());
+                }
+                userMap.setMaOpenId(session.getOpenid());
+                userMap.setPhone(phoneNoInfo.getPhoneNumber());
+                userMap.setUpdateTime(new DateTime());
+                userService.updateFsUser(userMap);
+            }
+            String token = jwtUtils.generateToken(user.getUserId());
+//            redisCache.setCacheObject("token:"+user.getUserId(),token,365, TimeUnit.DAYS);
+            Map<String,Object> map=new HashMap<>();
+            map.put("token",token);
+            user.setPhone(encryptPhone(phoneNoInfo.getPhoneNumber()));
+            map.put("user",user);
+            FsUserLoginLog log = new FsUserLoginLog();
+            log.setCode(param.getCode());
+            log.setLoginJson(JSON.toJSONString(param));
+//            log.setUserRegisterJson(JSON.toJSONString(jsonMap));
+            log.setStatus(1);
+            log.setUserId(user.getUserId());
+            log.setPhone(user.getPhone());
+            log.setMaOpenId(user.getMaOpenId());
+            log.setCreateTime(DateUtils.getNowDate());
+            fsUserLoginLogMapper.insertFsUserLoginLog(log);
+            return R.ok(map);
+        } catch (WxErrorException e) {
+            //this.logger.error(e.getMessage(), e);
+            return R.error("授权失败,"+e.getMessage());
+        }
+    }
+
+    @ApiOperation("小程序看课登录")
+    @PostMapping("/courseLogin")
+    @Transactional
+    public R courseLogin(@RequestBody LoginParam param) {
+        SysConfig sysConfig3 = sysConfigMapper.selectConfigByConfigKey("courseMa.config");
+        List<CourseMaConfig> courseMaConfigs = JSON.parseArray(sysConfig3.getConfigValue(), CourseMaConfig.class);
+        if (courseMaConfigs.isEmpty()){
+            return R.error("小程序配置为空");
+        }
+        CourseMaConfig courseMaConfig = courseMaConfigs.get(0);
+        return handleCourseLogin(param,
+                () -> WxMaConfiguration.getMaService(courseMaConfig.getAppid()),
+                courseMaConfig.getName());
+    }
+
+    /**
+     * 公共登录处理方法
+     * @param param 登录参数
+     * @param wxServiceSupplier 微信服务提供函数(差异化点1:不同方式获取WxMaService)
+     * @param logName 日志名称(差异化点2:不同场景标识)
+     */
+    private R handleCourseLogin(LoginParam param, Supplier<WxMaService> wxServiceSupplier, String logName) {
+        if (StringUtils.isBlank(param.getCode())) {
+            return R.error("code不存在");
+        }
+
+        try {
+            // 通过函数式接口获取不同的微信服务实例
+            final WxMaService wxService = wxServiceSupplier.get();
+            WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
+            this.logger.info("获取{} Session:{}", logName, session);
+
+            FsUser user = userService.selectFsUserByUnionid(session.getUnionid());
+            boolean isNewUser = false;
+
+            // 用户存在时的更新逻辑
+            if(user != null){
+                FsUser userMap = new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setCourseMaOpenId(session.getOpenid());
+                userMap.setUpdateTime(new DateTime());
+                userService.updateFsUser(userMap);
+            }
+            // 用户不存在时的创建逻辑
+            else {
+                user = new FsUser();
+                user.setNickName("微信用户");
+                user.setAvatar("https://hos-1309931967.cos.ap-chongqing.myqcloud.com/fs/20230725/a848605591384ec29d49773dd58d9345.jpg");
+                user.setStatus(1);
+                user.setCourseMaOpenId(session.getOpenid());
+                user.setUnionId(session.getUnionid());
+                user.setCreateTime(new Date());
+                userService.insertFsUser(user);
+                isNewUser = true;
+            }
+
+            // 生成Token
+            String token = jwtUtils.generateToken(user.getUserId());
+            Map<String,Object> map = new HashMap<>();
+            map.put("token", token);
+            map.put("user", user);
+            map.put("isNew", isNewUser);
+
+            logger.info("zyp \n 【点播{}登录】:{}", logName, user.getUserId());
+            return R.ok(map);
+        } catch (WxErrorException e){
+            logger.error("{}授权失败:{}", logName, e.getMessage(), e);
+            return R.error("授权失败," + e.getMessage());
+        }
+    }
+
+
+    public static String genCode() {
+        String year = new SimpleDateFormat("yy").format(new Date());
+        String day = String.format("%tj", new Date());
+        double random = Math.random() * 10000000;
+        while (random < 1000000) {
+            random = Math.random() * 10000000;
+        }
+        int intRandom = Double.valueOf(random).intValue();
+        String verifyCode = year + day + intRandom;
+        return verifyCode;
+    }
+
+
+
+    @ApiOperation("公众号登录")
+    @PostMapping("/loginByMp")
+    public R loginByMp( @RequestBody LoginParam param) {
+        if (StringUtils.isBlank(param.getCode())) {
+            return R.error("code不存在");
+        }
+        try{
+            WxOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(param.getCode());
+            WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(wxMpOAuth2AccessToken, null);
+            FsUser user=userService.selectFsUserByUnionid(wxMpUser.getUnionId());
+            if(user!=null){
+                FsUser userMap=new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setNickName(wxMpUser.getNickname());
+                userMap.setAvatar(wxMpUser.getHeadImgUrl());
+                userMap.setMpOpenId(wxMpUser.getOpenid());
+                userMap.setUpdateTime(new DateTime());
+                userService.updateFsUser(userMap);
+            }
+            else{
+                //写入
+                String code=genCode();
+                user=new FsUser();
+                user.setNickName(wxMpUser.getNickname());
+                user.setAvatar(wxMpUser.getHeadImgUrl());
+                user.setStatus(1);
+                user.setSex(wxMpUser.getSex());
+                user.setMpOpenId(wxMpUser.getOpenid());
+                user.setUnionId(wxMpUser.getUnionId());
+                user.setCreateTime(new Date());
+                userService.insertFsUser(user);
+            }
+            String token = jwtUtils.generateToken(user.getUserId());
+            redisCache.setCacheObject("token:"+user.getUserId(),token,604800, TimeUnit.SECONDS);
+            Map<String,Object> map=new HashMap<>();
+            map.put("token",token);
+            map.put("user",user);
+
+            FsUserLoginLog log = new FsUserLoginLog();
+            log.setCode(param.getCode());
+            log.setLoginJson(JSON.toJSONString(param));
+//            log.setUserRegisterJson(JSON.toJSONString(jsonMap));
+            log.setStatus(1);
+            log.setUserId(user.getUserId());
+            log.setPhone(user.getPhone());
+            log.setMaOpenId(user.getMaOpenId());
+            log.setCreateTime(DateUtils.getNowDate());
+            fsUserLoginLogMapper.insertFsUserLoginLog(log);
+            return R.ok(map);
+        }
+        catch (WxErrorException e){
+            if(e.getError().getErrorCode()==40163){
+                return R.error(40163,e.getError().getErrorMsg());
+            }
+            else{
+                return R.error("授权失败,"+e.getMessage());
+            }
+        }
+
+    }
+
+    /**
+     * <pre>
+     * 获取微信用户信息
+     * </pre>
+     */
+    @Login
+    @ApiOperation("获取微信小程序用户信息")
+    @PostMapping("/getWeixinInfo")
+    public R getWeixinInfo(@RequestBody LoginParam param) {
+        FsSysConfig con = configUtil.getSysConfig();
+        final WxMaService wxService = WxMaConfiguration.getMaService(con.getAppid());
+        try {
+            WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
+            // 用户信息校验
+            if (!wxService.getUserService().checkUserInfo(session.getSessionKey(), param.getRawData(), param.getSignature())) {
+                return R.error("user check failed");
+            }
+            // 解密用户信息
+            WxMaUserInfo userInfo = wxService.getUserService().getUserInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+            FsUser user=userService.selectFsUserByUserId(Long.parseLong(getUserId()));
+            user.setNickName(userInfo.getNickName());
+            user.setAvatar(userInfo.getAvatarUrl());
+            user.setIsWeixinAuth(1);
+            userService.updateFsUser(user);
+            return R.ok();
+        } catch (WxErrorException e) {
+            e.printStackTrace();
+        }
+        return R.ok("授权成功");
+    }
+
+
+    @GetMapping("/loginTest")
+    @Synchronized
+    public R loginByMp(String phone) {
+        //如果开启了UnionId
+        FsUser user=userService.selectFsUserByMpOpenId(phone);
+        String token = jwtUtils.generateToken(user.getUserId());
+        return R.ok("登录成功").put("token",token).put("user", user);
+    }
+
+}

+ 51 - 0
fs-user-course/src/main/java/com/fs/course/exception/FSException.java

@@ -0,0 +1,51 @@
+package com.fs.course.exception;
+
+/**
+ * 自定义异常
+ */
+public class FSException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+    private String msg;
+    private int code = 500;
+
+    public FSException(String msg) {
+		super(msg);
+		this.msg = msg;
+	}
+
+	public FSException(String msg, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+	}
+
+	public FSException(String msg, int code) {
+		super(msg);
+		this.msg = msg;
+		this.code = code;
+	}
+
+	public FSException(String msg, int code, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+		this.code = code;
+	}
+
+	public String getMsg() {
+		return msg;
+	}
+
+	public void setMsg(String msg) {
+		this.msg = msg;
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	public void setCode(int code) {
+		this.code = code;
+	}
+
+
+}

+ 68 - 0
fs-user-course/src/main/java/com/fs/course/interceptor/AuthorizationInterceptor.java

@@ -0,0 +1,68 @@
+package com.fs.course.interceptor;
+
+
+import com.fs.course.annotation.Login;
+import com.fs.course.exception.FSException;
+import com.fs.course.utils.JwtUtils;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
+import io.jsonwebtoken.Claims;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 权限(Token)验证
+ */
+@Component
+public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
+    @Autowired
+    private JwtUtils jwtUtils;
+    @Autowired
+    RedisCache redisCache;
+    public static final String USER_KEY = "userId";
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        Login annotation;
+        if(handler instanceof HandlerMethod) {
+            annotation = ((HandlerMethod) handler).getMethodAnnotation(Login.class);
+        }else{
+            return true;
+        }
+
+        if(annotation == null){
+            return true;
+        }
+
+        //获取用户凭证
+        String token = request.getHeader(jwtUtils.getHeader());
+        if(StringUtils.isBlank(token)){
+            token = request.getParameter(jwtUtils.getHeader());
+        }
+
+        //凭证为空
+        if(StringUtils.isBlank(token)){
+            throw new FSException(jwtUtils.getHeader() + "不能为空", HttpStatus.UNAUTHORIZED.value());
+        }
+
+        Claims claims = jwtUtils.getClaimByToken(token);
+        if(claims == null || jwtUtils.isTokenExpired(claims.getExpiration())){
+            throw new FSException(jwtUtils.getHeader() + "失效,请重新登录", HttpStatus.UNAUTHORIZED.value());
+        }
+        //查询用户的TOKEN是否和REDIS中的一样
+//        String redisToken=redisCache.getCacheObject("token:"+ Long.parseLong(claims.getSubject()));
+//        if(redisToken==null||!redisToken.equals(token)){
+//            throw new FSException(jwtUtils.getHeader() + "失效,请重新登录", HttpStatus.UNAUTHORIZED.value());
+//        }
+        //设置userId到request里,后续根据userId,获取用户信息
+        request.setAttribute(USER_KEY, Long.parseLong(claims.getSubject()));
+
+        return true;
+    }
+}

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

@@ -0,0 +1,14 @@
+package com.fs.course.param;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+@Getter
+@Setter
+public class FsBindCompanyUserParam implements Serializable {
+
+    private Long companyUserId;
+
+}

+ 95 - 0
fs-user-course/src/main/java/com/fs/course/param/FsDoctorRegisterParam.java

@@ -0,0 +1,95 @@
+package com.fs.course.param;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.Date;
+
+@Getter
+@Setter
+public class FsDoctorRegisterParam implements Serializable {
+
+
+
+    @NotBlank(message = "医生姓名不能为空")
+    private String doctorName;
+    /** 简介 */
+    private String introduction;
+
+    /** 擅长领域 */
+    @NotBlank(message = "擅长领域不能为空")
+    private String speciality;
+
+    /** 资格证编号 */
+    private String certificateCode;
+
+    /** 证书照片 */
+    private String certificateImages;
+
+    /** 工作照 */
+    @NotBlank(message = "工作照不能为空")
+    private String avatar;
+
+
+
+    /** 所属医院 */
+    @NotNull(message = "所属医院不能为空")
+    private Long hospitalId;
+
+    /** 科室ID */
+    @NotNull(message = "所属科室不能为空")
+    private Long deptId;
+
+
+
+    /** 职务 */
+    @NotBlank(message = "职务不能为空")
+    private String position;
+
+
+    @NotBlank(message = "手机号不能为空")
+    private String mobile;
+
+    /** 医生类型 1医生 2药师 */
+    @NotNull(message = "医生类型不能为空")
+    private Integer doctorType;
+
+    /** 性别 */
+    @NotNull(message = "性别不能为空")
+    private Integer sex;
+
+
+    private Date bitthday;
+
+    /** 身份证号 */
+    @NotBlank(message = "身份证号不能为空")
+    private String idCard;
+
+    /** 身份证正面 */
+    @NotBlank(message = "身份证正面照片不能为空")
+    private String idCardFrontUrl;
+
+    /** 身份证反面 */
+    @NotBlank(message = "身份证反面照片不能为空")
+    private String idCardBackUrl;
+
+    /** 所属省市区 */
+    @NotBlank(message = "所属省市区不能为空")
+    private String cityIds;
+
+    private String province;
+
+    private String city;
+
+    private String district;
+
+    @NotBlank(message = "帐户不能为空")
+    private String account;
+    @NotBlank(message = "密码不能为空")
+//    @Pattern(regexp="^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_]+$)(?![a-z0-9]+$)(?![a-z\\W_]+$)(?![0-9\\W_]+$)[a-zA-Z0-9\\W_]{8,}$",message="密码长度最少8位,由数字、大写字母、小写字母、特殊字符中的至少三种组成")
+    private String password;
+
+}

+ 24 - 0
fs-user-course/src/main/java/com/fs/course/param/FsUserEditParam.java

@@ -0,0 +1,24 @@
+package com.fs.course.param;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@Getter
+@Setter
+public class FsUserEditParam implements Serializable {
+
+    @NotBlank(message = "头像不能为空")
+    private String avatar;
+    @NotBlank(message = "昵称不能为空")
+    private String nickName;
+    @NotBlank(message = "手机号不能为空")
+    private String phone;
+    @NotNull(message = "性别不能为空")
+    private Integer sex;
+    private Integer isWeixinAuth;
+
+}

+ 13 - 0
fs-user-course/src/main/java/com/fs/course/param/FsUserLoginByMpParam.java

@@ -0,0 +1,13 @@
+package com.fs.course.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+@Data
+public class FsUserLoginByMpParam implements Serializable {
+    @NotBlank(message = "code参数缺失")
+    private String code;
+    private Long videoId;
+}

+ 88 - 0
fs-user-course/src/main/java/com/fs/course/utils/JwtUtils.java

@@ -0,0 +1,88 @@
+package com.fs.course.utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * jwt工具类
+
+ */
+@ConfigurationProperties(prefix = "fs.jwt")
+@Component
+public class JwtUtils {
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+
+    private String secret;
+    private long expire;
+    private String header;
+
+    /**
+     * 生成jwt token
+     */
+    public String generateToken(long userId) {
+        Date nowDate = new Date();
+        //过期时间
+        Date expireDate = new Date(nowDate.getTime() + expire * 1000);
+//        System.out.println("==============================="+secret);
+//        logger.info("secret:{}",secret);
+        return Jwts.builder()
+                .setHeaderParam("typ", "JWT")
+                .setSubject(userId+"")
+                .setIssuedAt(nowDate)
+                .setExpiration(expireDate)
+                .signWith(SignatureAlgorithm.HS512, secret)
+                .compact();
+    }
+
+    public Claims getClaimByToken(String token) {
+        try {
+            return Jwts.parser()
+                    .setSigningKey(secret)
+                    .parseClaimsJws(token)
+                    .getBody();
+        }catch (Exception e){
+            logger.debug("validate is token error ", e);
+            return null;
+        }
+    }
+
+    /**
+     * token是否过期
+     * @return  true:过期
+     */
+    public boolean isTokenExpired(Date expiration) {
+        return expiration.before(new Date());
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    public void setSecret(String secret) {
+        this.secret = secret;
+    }
+
+    public long getExpire() {
+        return expire;
+    }
+
+    public void setExpire(long expire) {
+        this.expire = expire;
+    }
+
+    public String getHeader() {
+        return header;
+    }
+
+    public void setHeader(String header) {
+        this.header = header;
+    }
+}

+ 23 - 0
fs-user-course/src/main/java/com/fs/course/vo/CityVO.java

@@ -0,0 +1,23 @@
+package com.fs.course.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Getter
+@Setter
+@ToString
+public class CityVO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String v; //id
+
+    private String n; //名称
+
+    private String pid;
+
+    private List<CityVO> c; //子集
+}

+ 182 - 0
fs-user-course/src/main/java/com/fs/framework/aspectj/DataScopeAspect.java

@@ -0,0 +1,182 @@
+package com.fs.framework.aspectj;
+
+import com.fs.common.annotation.DataScope;
+import com.fs.common.core.domain.BaseEntity;
+import com.fs.common.core.domain.entity.SysRole;
+import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.utils.SecurityUtils;
+import com.fs.common.utils.StringUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+/**
+ * 数据过滤处理
+ *
+
+ */
+@Aspect
+@Component
+public class DataScopeAspect
+{
+    /**
+     * 全部数据权限
+     */
+    public static final String DATA_SCOPE_ALL = "1";
+
+    /**
+     * 自定数据权限
+     */
+    public static final String DATA_SCOPE_CUSTOM = "2";
+
+    /**
+     * 部门数据权限
+     */
+    public static final String DATA_SCOPE_DEPT = "3";
+
+    /**
+     * 部门及以下数据权限
+     */
+    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
+
+    /**
+     * 仅本人数据权限
+     */
+    public static final String DATA_SCOPE_SELF = "5";
+
+    /**
+     * 数据权限过滤关键字
+     */
+    public static final String DATA_SCOPE = "dataScope";
+
+    // 配置织入点
+    @Pointcut("@annotation(com.fs.common.annotation.DataScope)")
+    public void dataScopePointCut()
+    {
+    }
+
+    @Before("dataScopePointCut()")
+    public void doBefore(JoinPoint point) throws Throwable
+    {
+        clearDataScope(point);
+        handleDataScope(point);
+    }
+
+    protected void handleDataScope(final JoinPoint joinPoint)
+    {
+        // 获得注解
+        DataScope controllerDataScope = getAnnotationLog(joinPoint);
+        if (controllerDataScope == null)
+        {
+            return;
+        }
+        // 获取当前的用户
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if (StringUtils.isNotNull(loginUser))
+        {
+            SysUser currentUser = loginUser.getUser();
+            // 如果是超级管理员,则不过滤数据
+            if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
+            {
+                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
+                        controllerDataScope.userAlias());
+            }
+        }
+    }
+
+    /**
+     * 数据范围过滤
+     *
+     * @param joinPoint 切点
+     * @param user 用户
+     * @param userAlias 别名
+     */
+    public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias)
+    {
+        StringBuilder sqlString = new StringBuilder();
+
+        for (SysRole role : user.getRoles())
+        {
+            String dataScope = role.getDataScope();
+            if (DATA_SCOPE_ALL.equals(dataScope))
+            {
+                sqlString = new StringBuilder();
+                break;
+            }
+            else if (DATA_SCOPE_CUSTOM.equals(dataScope))
+            {
+                sqlString.append(StringUtils.format(
+                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
+                        role.getRoleId()));
+            }
+            else if (DATA_SCOPE_DEPT.equals(dataScope))
+            {
+                sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
+            }
+            else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
+            {
+                sqlString.append(StringUtils.format(
+                        " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
+                        deptAlias, user.getDeptId(), user.getDeptId()));
+            }
+            else if (DATA_SCOPE_SELF.equals(dataScope))
+            {
+                if (StringUtils.isNotBlank(userAlias))
+                {
+                    sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
+                }
+                else
+                {
+                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
+                    sqlString.append(" OR 1=0 ");
+                }
+            }
+        }
+
+        if (StringUtils.isNotBlank(sqlString.toString()))
+        {
+            Object params = joinPoint.getArgs()[0];
+            if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
+            {
+                BaseEntity baseEntity = (BaseEntity) params;
+                baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
+            }
+        }
+    }
+
+    /**
+     * 是否存在注解,如果存在就获取
+     */
+    private DataScope getAnnotationLog(JoinPoint joinPoint)
+    {
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+
+        if (method != null)
+        {
+            return method.getAnnotation(DataScope.class);
+        }
+        return null;
+    }
+
+    /**
+     * 拼接权限sql前先清空params.dataScope参数防止注入
+     */
+    private void clearDataScope(final JoinPoint joinPoint)
+    {
+        Object params = joinPoint.getArgs()[0];
+        if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
+        {
+            BaseEntity baseEntity = (BaseEntity) params;
+            baseEntity.getParams().put(DATA_SCOPE, "");
+        }
+    }
+}

+ 73 - 0
fs-user-course/src/main/java/com/fs/framework/aspectj/DataSourceAspect.java

@@ -0,0 +1,73 @@
+package com.fs.framework.aspectj;
+
+import com.fs.common.annotation.DataSource;
+import com.fs.common.utils.StringUtils;
+import com.fs.framework.datasource.DynamicDataSourceContextHolder;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * 多数据源处理
+ *
+
+ */
+@Aspect
+@Order(1)
+@Component
+public class DataSourceAspect
+{
+    protected Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Pointcut("@annotation(com.fs.common.annotation.DataSource)"
+            + "|| @within(com.fs.common.annotation.DataSource)")
+    public void dsPointCut()
+    {
+
+    }
+
+    @Around("dsPointCut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable
+    {
+        DataSource dataSource = getDataSource(point);
+
+        if (StringUtils.isNotNull(dataSource))
+        {
+            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
+        }
+
+        try
+        {
+            return point.proceed();
+        }
+        finally
+        {
+            // 销毁数据源 在执行方法之后
+            DynamicDataSourceContextHolder.clearDataSourceType();
+        }
+    }
+
+    /**
+     * 获取需要切换的数据源
+     */
+    public DataSource getDataSource(ProceedingJoinPoint point)
+    {
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
+        if (Objects.nonNull(dataSource))
+        {
+            return dataSource;
+        }
+
+        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
+    }
+}

+ 244 - 0
fs-user-course/src/main/java/com/fs/framework/aspectj/LogAspect.java

@@ -0,0 +1,244 @@
+package com.fs.framework.aspectj;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.enums.BusinessStatus;
+import com.fs.common.enums.HttpMethod;
+import com.fs.common.utils.SecurityUtils;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.ip.IpUtils;
+import com.fs.framework.manager.AsyncManager;
+import com.fs.framework.manager.factory.AsyncFactory;
+import com.fs.system.domain.SysOperLog;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.HandlerMapping;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * 操作日志记录处理
+ *
+
+ */
+@Aspect
+@Component
+public class LogAspect
+{
+    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
+
+    // 配置织入点
+    @Pointcut("@annotation(com.fs.common.annotation.Log)")
+    public void logPointCut()
+    {
+    }
+
+    /**
+     * 处理完请求后执行
+     *
+     * @param joinPoint 切点
+     */
+    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
+    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
+    {
+        handleLog(joinPoint, null, jsonResult);
+    }
+
+    /**
+     * 拦截异常操作
+     *
+     * @param joinPoint 切点
+     * @param e 异常
+     */
+    @AfterThrowing(value = "logPointCut()", throwing = "e")
+    public void doAfterThrowing(JoinPoint joinPoint, Exception e)
+    {
+        handleLog(joinPoint, e, null);
+    }
+
+    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult)
+    {
+        try
+        {
+            // 获得注解
+            Log controllerLog = getAnnotationLog(joinPoint);
+            if (controllerLog == null)
+            {
+                return;
+            }
+
+            // 获取当前的用户
+            LoginUser loginUser = SecurityUtils.getLoginUser();
+
+            // *========数据库日志=========*//
+            SysOperLog operLog = new SysOperLog();
+            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
+            // 请求的地址
+            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
+            operLog.setOperIp(ip);
+            // 返回参数
+            operLog.setJsonResult(JSON.toJSONString(jsonResult));
+
+            operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
+            if (loginUser != null)
+            {
+                operLog.setOperName(loginUser.getUsername());
+            }
+
+            if (e != null)
+            {
+                operLog.setStatus(BusinessStatus.FAIL.ordinal());
+                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
+            }
+            // 设置方法名称
+            String className = joinPoint.getTarget().getClass().getName();
+            String methodName = joinPoint.getSignature().getName();
+            operLog.setMethod(className + "." + methodName + "()");
+            // 设置请求方式
+            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
+            // 处理设置注解上的参数
+            getControllerMethodDescription(joinPoint, controllerLog, operLog);
+            // 保存数据库
+            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
+        }
+        catch (Exception exp)
+        {
+            // 记录本地异常日志
+            log.error("==前置通知异常==");
+            log.error("异常信息:{}", exp.getMessage());
+            exp.printStackTrace();
+        }
+    }
+
+    /**
+     * 获取注解中对方法的描述信息 用于Controller层注解
+     *
+     * @param log 日志
+     * @param operLog 操作日志
+     * @throws Exception
+     */
+    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception
+    {
+        // 设置action动作
+        operLog.setBusinessType(log.businessType().ordinal());
+        // 设置标题
+        operLog.setTitle(log.title());
+        // 设置操作人类别
+        operLog.setOperatorType(log.operatorType().ordinal());
+        // 是否需要保存request,参数和值
+        if (log.isSaveRequestData())
+        {
+            // 获取参数的信息,传入到数据库中。
+            setRequestValue(joinPoint, operLog);
+        }
+    }
+
+    /**
+     * 获取请求的参数,放到log中
+     *
+     * @param operLog 操作日志
+     * @throws Exception 异常
+     */
+    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception
+    {
+        String requestMethod = operLog.getRequestMethod();
+        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
+        {
+            String params = argsArrayToString(joinPoint.getArgs());
+            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
+        }
+        else
+        {
+            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+            operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
+        }
+    }
+
+    /**
+     * 是否存在注解,如果存在就获取
+     */
+    private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
+    {
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+
+        if (method != null)
+        {
+            return method.getAnnotation(Log.class);
+        }
+        return null;
+    }
+
+    /**
+     * 参数拼装
+     */
+    private String argsArrayToString(Object[] paramsArray)
+    {
+        String params = "";
+        if (paramsArray != null && paramsArray.length > 0)
+        {
+            for (int i = 0; i < paramsArray.length; i++)
+            {
+                if (StringUtils.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i]))
+                {
+                    Object jsonObj = JSON.toJSON(paramsArray[i]);
+                    params += jsonObj.toString() + " ";
+                }
+            }
+        }
+        return params.trim();
+    }
+
+    /**
+     * 判断是否需要过滤的对象。
+     *
+     * @param o 对象信息。
+     * @return 如果是需要过滤的对象,则返回true;否则返回false。
+     */
+    @SuppressWarnings("rawtypes")
+    public boolean isFilterObject(final Object o)
+    {
+        Class<?> clazz = o.getClass();
+        if (clazz.isArray())
+        {
+            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+        }
+        else if (Collection.class.isAssignableFrom(clazz))
+        {
+            Collection collection = (Collection) o;
+            for (Iterator iter = collection.iterator(); iter.hasNext();)
+            {
+                return iter.next() instanceof MultipartFile;
+            }
+        }
+        else if (Map.class.isAssignableFrom(clazz))
+        {
+            Map map = (Map) o;
+            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
+            {
+                Map.Entry entry = (Map.Entry) iter.next();
+                return entry.getValue() instanceof MultipartFile;
+            }
+        }
+        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
+                || o instanceof BindingResult;
+    }
+}

+ 117 - 0
fs-user-course/src/main/java/com/fs/framework/aspectj/RateLimiterAspect.java

@@ -0,0 +1,117 @@
+package com.fs.framework.aspectj;
+
+import com.fs.common.annotation.RateLimiter;
+import com.fs.common.enums.LimitType;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.ip.IpUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 限流处理
+ *
+
+ */
+@Aspect
+@Component
+public class RateLimiterAspect
+{
+    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
+
+    private RedisTemplate<Object, Object> redisTemplate;
+
+    private RedisScript<Long> limitScript;
+
+    @Autowired
+    public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
+    {
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Autowired
+    public void setLimitScript(RedisScript<Long> limitScript)
+    {
+        this.limitScript = limitScript;
+    }
+
+    // 配置织入点
+    @Pointcut("@annotation(com.fs.common.annotation.RateLimiter)")
+    public void rateLimiterPointCut()
+    {
+    }
+
+    @Before("rateLimiterPointCut()")
+    public void doBefore(JoinPoint point) throws Throwable
+    {
+        RateLimiter rateLimiter = getAnnotationRateLimiter(point);
+        String key = rateLimiter.key();
+        int time = rateLimiter.time();
+        int count = rateLimiter.count();
+
+        String combineKey = getCombineKey(rateLimiter, point);
+        List<Object> keys = Collections.singletonList(combineKey);
+        try
+        {
+            Long number = redisTemplate.execute(limitScript, keys, count, time);
+            if (StringUtils.isNull(number) || number.intValue() > count)
+            {
+                throw new ServiceException("访问过于频繁,请稍后再试");
+            }
+            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key);
+        }
+        catch (ServiceException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException("服务器限流异常,请稍后再试");
+        }
+    }
+
+    /**
+     * 是否存在注解,如果存在就获取
+     */
+    private RateLimiter getAnnotationRateLimiter(JoinPoint joinPoint)
+    {
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+
+        if (method != null)
+        {
+            return method.getAnnotation(RateLimiter.class);
+        }
+        return null;
+    }
+
+    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
+    {
+        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
+        if (rateLimiter.limitType() == LimitType.IP)
+        {
+            stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest()));
+        }
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Method method = signature.getMethod();
+        Class<?> targetClass = method.getDeclaringClass();
+        stringBuffer.append("-").append(targetClass.getName()).append("- ").append(method.getName());
+        return stringBuffer.toString();
+    }
+}

+ 31 - 0
fs-user-course/src/main/java/com/fs/framework/config/ApplicationConfig.java

@@ -0,0 +1,31 @@
+package com.fs.framework.config;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+
+import java.util.TimeZone;
+
+/**
+ * 程序注解配置
+ *
+
+ */
+@Configuration
+// 表示通过aop框架暴露该代理对象,AopContext能够访问
+@EnableAspectJAutoProxy(exposeProxy = true)
+// 指定要扫描的Mapper类的包的路径
+@MapperScan("com.fs.**.mapper")
+public class ApplicationConfig
+{
+    /**
+     * 时区配置
+     */
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
+    {
+        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
+    }
+}

+ 85 - 0
fs-user-course/src/main/java/com/fs/framework/config/CaptchaConfig.java

@@ -0,0 +1,85 @@
+package com.fs.framework.config;
+
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+import static com.google.code.kaptcha.Constants.*;
+
+/**
+ * 验证码配置
+ *
+
+ */
+@Configuration
+public class CaptchaConfig
+{
+    @Bean(name = "captchaProducer")
+    public DefaultKaptcha getKaptchaBean()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+
+    @Bean(name = "captchaProducerMath")
+    public DefaultKaptcha getKaptchaBeanMath()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 边框颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
+        // 验证码文本生成器
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.fs.framework.config.KaptchaTextCreator");
+        // 验证码文本字符间距 默认为2
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 验证码噪点颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
+        // 干扰实现类
+        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+}

+ 92 - 0
fs-user-course/src/main/java/com/fs/framework/config/DataSourceConfig.java

@@ -0,0 +1,92 @@
+package com.fs.framework.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
+import com.alibaba.druid.util.Utils;
+import com.fs.common.enums.DataSourceType;
+import com.fs.framework.datasource.DynamicDataSource;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import javax.servlet.*;
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Configuration
+public class DataSourceConfig {
+
+    @Bean
+    @ConfigurationProperties(prefix = "spring.datasource.sop.druid.master")
+    public DataSource sopDataSource() {
+        return new DruidDataSource();
+    }
+
+    @Bean
+    @ConfigurationProperties(prefix = "spring.datasource.mysql.druid.master")
+    public DataSource masterDataSource() {
+        return new DruidDataSource();
+    }
+
+
+
+    @Bean
+    @Primary
+    public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("sopDataSource") DataSource sopDataSource) {
+        Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.SOP.name(), sopDataSource);
+        return new DynamicDataSource(masterDataSource, targetDataSources);
+    }
+
+    /**
+     * 去除监控页面底部的广告
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    @ConditionalOnProperty(name = "spring.datasource.mysql.druid.statViewServlet.enabled", havingValue = "true")
+    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
+    {
+        // 获取web监控页面的参数
+        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
+        // 提取common.js的配置路径
+        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
+        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
+        final String filePath = "support/http/resources/js/common.js";
+        // 创建filter进行过滤
+        Filter filter = new Filter()
+        {
+            @Override
+            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
+            {
+            }
+            @Override
+            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+                    throws IOException, ServletException
+            {
+                chain.doFilter(request, response);
+                // 重置缓冲区,响应头不会被重置
+                response.resetBuffer();
+                // 获取common.js
+                String text = Utils.readFromResource(filePath);
+                // 正则替换banner, 除去底部的广告信息
+                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
+                text = text.replaceAll("powered.*?shrek.wang</a>", "");
+                response.getWriter().write(text);
+            }
+            @Override
+            public void destroy()
+            {
+            }
+        };
+        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+        registrationBean.setFilter(filter);
+        registrationBean.addUrlPatterns(commonJsPattern);
+        return registrationBean;
+    }
+}

+ 72 - 0
fs-user-course/src/main/java/com/fs/framework/config/FastJson2JsonRedisSerializer.java

@@ -0,0 +1,72 @@
+package com.fs.framework.config;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.parser.ParserConfig;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.SerializationException;
+import org.springframework.util.Assert;
+
+import java.nio.charset.Charset;
+
+/**
+ * Redis使用FastJson序列化
+ *
+
+ */
+public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
+{
+    @SuppressWarnings("unused")
+    private ObjectMapper objectMapper = new ObjectMapper();
+
+    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
+
+    private Class<T> clazz;
+
+    static
+    {
+        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
+    }
+
+    public FastJson2JsonRedisSerializer(Class<T> clazz)
+    {
+        super();
+        this.clazz = clazz;
+    }
+
+    @Override
+    public byte[] serialize(T t) throws SerializationException
+    {
+        if (t == null)
+        {
+            return new byte[0];
+        }
+        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
+    }
+
+    @Override
+    public T deserialize(byte[] bytes) throws SerializationException
+    {
+        if (bytes == null || bytes.length <= 0)
+        {
+            return null;
+        }
+        String str = new String(bytes, DEFAULT_CHARSET);
+
+        return JSON.parseObject(str, clazz);
+    }
+
+    public void setObjectMapper(ObjectMapper objectMapper)
+    {
+        Assert.notNull(objectMapper, "'objectMapper' must not be null");
+        this.objectMapper = objectMapper;
+    }
+
+    protected JavaType getJavaType(Class<?> clazz)
+    {
+        return TypeFactory.defaultInstance().constructType(clazz);
+    }
+}

+ 59 - 0
fs-user-course/src/main/java/com/fs/framework/config/FilterConfig.java

@@ -0,0 +1,59 @@
+package com.fs.framework.config;
+
+import com.fs.common.filter.RepeatableFilter;
+import com.fs.common.filter.XssFilter;
+import com.fs.common.utils.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.servlet.DispatcherType;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Filter配置
+ *
+
+ */
+@Configuration
+@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
+public class FilterConfig
+{
+    @Value("${xss.excludes}")
+    private String excludes;
+
+    @Value("${xss.urlPatterns}")
+    private String urlPatterns;
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    public FilterRegistrationBean xssFilterRegistration()
+    {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setDispatcherTypes(DispatcherType.REQUEST);
+        registration.setFilter(new XssFilter());
+        registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
+        registration.setName("xssFilter");
+        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
+        Map<String, String> initParameters = new HashMap<String, String>();
+        initParameters.put("excludes", excludes);
+        registration.setInitParameters(initParameters);
+        return registration;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    public FilterRegistrationBean someFilterRegistration()
+    {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setFilter(new RepeatableFilter());
+        registration.addUrlPatterns("/*");
+        registration.setName("repeatableFilter");
+        registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
+        return registration;
+    }
+
+}

+ 76 - 0
fs-user-course/src/main/java/com/fs/framework/config/KaptchaTextCreator.java

@@ -0,0 +1,76 @@
+package com.fs.framework.config;
+
+import com.google.code.kaptcha.text.impl.DefaultTextCreator;
+
+import java.util.Random;
+
+/**
+ * 验证码文本生成器
+ *
+
+ */
+public class KaptchaTextCreator extends DefaultTextCreator
+{
+    private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
+
+    @Override
+    public String getText()
+    {
+        Integer result = 0;
+        Random random = new Random();
+        int x = random.nextInt(10);
+        int y = random.nextInt(10);
+        StringBuilder suChinese = new StringBuilder();
+        int randomoperands = (int) Math.round(Math.random() * 2);
+        if (randomoperands == 0)
+        {
+            result = x * y;
+            suChinese.append(CNUMBERS[x]);
+            suChinese.append("*");
+            suChinese.append(CNUMBERS[y]);
+        }
+        else if (randomoperands == 1)
+        {
+            if (!(x == 0) && y % x == 0)
+            {
+                result = y / x;
+                suChinese.append(CNUMBERS[y]);
+                suChinese.append("/");
+                suChinese.append(CNUMBERS[x]);
+            }
+            else
+            {
+                result = x + y;
+                suChinese.append(CNUMBERS[x]);
+                suChinese.append("+");
+                suChinese.append(CNUMBERS[y]);
+            }
+        }
+        else if (randomoperands == 2)
+        {
+            if (x >= y)
+            {
+                result = x - y;
+                suChinese.append(CNUMBERS[x]);
+                suChinese.append("-");
+                suChinese.append(CNUMBERS[y]);
+            }
+            else
+            {
+                result = y - x;
+                suChinese.append(CNUMBERS[y]);
+                suChinese.append("-");
+                suChinese.append(CNUMBERS[x]);
+            }
+        }
+        else
+        {
+            result = x + y;
+            suChinese.append(CNUMBERS[x]);
+            suChinese.append("+");
+            suChinese.append(CNUMBERS[y]);
+        }
+        suChinese.append("=?@" + result);
+        return suChinese.toString();
+    }
+}

+ 149 - 0
fs-user-course/src/main/java/com/fs/framework/config/MyBatisConfig.java

@@ -0,0 +1,149 @@
+package com.fs.framework.config;
+
+import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
+import org.apache.ibatis.io.VFS;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.util.ClassUtils;
+
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Mybatis支持*匹配扫描包
+ *
+
+ */
+@Configuration
+public class MyBatisConfig
+{
+    @Autowired
+    private Environment env;
+
+    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
+
+    public static String setTypeAliasesPackage(String typeAliasesPackage)
+    {
+        ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
+        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
+        List<String> allResult = new ArrayList<String>();
+        try
+        {
+            for (String aliasesPackage : typeAliasesPackage.split(","))
+            {
+                List<String> result = new ArrayList<String>();
+                aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+                        + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
+                Resource[] resources = resolver.getResources(aliasesPackage);
+                if (resources != null && resources.length > 0)
+                {
+                    MetadataReader metadataReader = null;
+                    for (Resource resource : resources)
+                    {
+                        if (resource.isReadable())
+                        {
+                            metadataReader = metadataReaderFactory.getMetadataReader(resource);
+                            try
+                            {
+                                result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
+                            }
+                            catch (ClassNotFoundException e)
+                            {
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+                }
+                if (result.size() > 0)
+                {
+                    HashSet<String> hashResult = new HashSet<String>(result);
+                    allResult.addAll(hashResult);
+                }
+            }
+            if (allResult.size() > 0)
+            {
+                typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
+            }
+            else
+            {
+                throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
+            }
+        }
+        catch (IOException e)
+        {
+            e.printStackTrace();
+        }
+        return typeAliasesPackage;
+    }
+
+    public Resource[] resolveMapperLocations(String[] mapperLocations)
+    {
+        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
+        List<Resource> resources = new ArrayList<Resource>();
+        if (mapperLocations != null)
+        {
+            for (String mapperLocation : mapperLocations)
+            {
+                try
+                {
+                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
+                    resources.addAll(Arrays.asList(mappers));
+                }
+                catch (IOException e)
+                {
+                    // ignore
+                }
+            }
+        }
+        return resources.toArray(new Resource[resources.size()]);
+    }
+
+//    @Bean
+//    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
+//    {
+//        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
+//        String mapperLocations = env.getProperty("mybatis.mapperLocations");
+//        String configLocation = env.getProperty("mybatis.configLocation");
+//        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
+//        VFS.addImplClass(SpringBootVFS.class);
+//
+//        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
+//        sessionFactory.setDataSource(dataSource);
+//        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
+//        sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
+//        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
+//        return sessionFactory.getObject();
+//    }
+
+    @Bean
+    public SqlSessionFactory sqlSessionFactorys(DataSource dataSource) throws Exception
+    {
+        String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage");
+        String mapperLocations = env.getProperty("mybatis-plus.mapperLocations");
+        String configLocation = env.getProperty("mybatis-plus.configLocation");
+        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
+        VFS.addImplClass(SpringBootVFS.class);
+
+        final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
+        sessionFactory.setDataSource(dataSource);
+        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
+        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
+        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
+        return sessionFactory.getObject();
+    }
+}

+ 137 - 0
fs-user-course/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -0,0 +1,137 @@
+package com.fs.framework.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.serializer.GenericToStringSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * redis配置
+ *
+
+ */
+@Configuration
+@EnableCaching
+public class RedisConfig extends CachingConfigurerSupport
+{
+//    @Bean
+//    public CacheManager cacheManager() {
+//        SimpleCacheManager cacheManager = new SimpleCacheManager();
+//        cacheManager.setCaches(Arrays.asList(
+//                new ConcurrentMapCache("getCourseCate"),
+//                new ConcurrentMapCache("getProductCateByPid"),
+//                new ConcurrentMapCache("getCourseList"),
+//                new ConcurrentMapCache("getAppAdvList"),
+//                new ConcurrentMapCache("getAdvList")
+//        ));
+//        return cacheManager;
+//    }
+
+
+
+    @Bean
+    @SuppressWarnings(value = { "unchecked", "rawtypes" })
+    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
+    {
+        RedisTemplate<Object, Object> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+        serializer.setObjectMapper(mapper);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+        template.setValueSerializer(serializer);
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(serializer);
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    @Bean
+    public RedisTemplate<String, Boolean> redisTemplateForBoolean(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Boolean> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+        template.setValueSerializer(new GenericToStringSerializer<>(Boolean.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Boolean.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    @Bean
+    @SuppressWarnings(value = { "unchecked", "rawtypes" })
+    public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Object> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+        serializer.setObjectMapper(mapper);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+        template.setValueSerializer(serializer);
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(serializer);
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    @Bean
+    public DefaultRedisScript<Long> limitScript()
+    {
+        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
+        redisScript.setScriptText(limitScriptText());
+        redisScript.setResultType(Long.class);
+        return redisScript;
+    }
+
+    /**
+     * 限流脚本
+     */
+    private String limitScriptText()
+    {
+        return "local key = KEYS[1]\n" +
+                "local count = tonumber(ARGV[1])\n" +
+                "local time = tonumber(ARGV[2])\n" +
+                "local current = redis.call('get', key);\n" +
+                "if current and tonumber(current) > count then\n" +
+                "    return current;\n" +
+                "end\n" +
+                "current = redis.call('incr', key)\n" +
+                "if tonumber(current) == 1 then\n" +
+                "    redis.call('expire', key, time)\n" +
+                "end\n" +
+                "return current;";
+    }
+}

+ 65 - 0
fs-user-course/src/main/java/com/fs/framework/config/ResourcesConfig.java

@@ -0,0 +1,65 @@
+package com.fs.framework.config;
+
+import com.fs.common.config.FSConfig;
+import com.fs.common.constant.Constants;
+import com.fs.framework.interceptor.RepeatSubmitInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 通用配置
+ *
+
+ */
+@Configuration
+public class ResourcesConfig implements WebMvcConfigurer
+{
+    @Autowired
+    private RepeatSubmitInterceptor repeatSubmitInterceptor;
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry)
+    {
+        /** 本地文件上传路径 */
+        registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + FSConfig.getProfile() + "/");
+
+        /** swagger配置 */
+        registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
+    }
+
+    /**
+     * 自定义拦截规则
+     */
+    @Override
+    public void addInterceptors(InterceptorRegistry registry)
+    {
+        registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
+    }
+
+    /**
+     * 跨域配置
+     */
+    @Bean
+    public CorsFilter corsFilter()
+    {
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        CorsConfiguration config = new CorsConfiguration();
+        config.setAllowCredentials(true);
+        // 设置访问源地址
+        config.addAllowedOrigin("*");
+        // 设置访问源请求头
+        config.addAllowedHeader("*");
+        // 设置访问源请求方法
+        config.addAllowedMethod("*");
+        // 对接口配置跨域设置
+        source.registerCorsConfiguration("/**", config);
+        return new CorsFilter(source);
+    }
+}

+ 50 - 0
fs-user-course/src/main/java/com/fs/framework/config/SecurityConfig.java

@@ -0,0 +1,50 @@
+package com.fs.framework.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.BeanIds;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+/**
+ * spring security配置
+ *
+
+ */
+@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
+public class SecurityConfig extends WebSecurityConfigurerAdapter
+{
+
+    /**
+     * anyRequest          |   匹配所有请求路径
+     * access              |   SpringEl表达式结果为true时可以访问
+     * anonymous           |   匿名可以访问
+     * denyAll             |   用户不能访问
+     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
+     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
+     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
+     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
+     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
+     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
+     * permitAll           |   用户可以任意访问
+     * rememberMe          |   允许通过remember-me登录的用户访问
+     * authenticated       |   用户登录后可访问
+     */
+    @Override
+    protected void configure(HttpSecurity http) throws Exception
+    {
+        http.authorizeRequests()
+                .antMatchers("/**").permitAll()
+                .anyRequest().authenticated()
+                .and().csrf().disable();
+    }
+
+    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+
+}

+ 33 - 0
fs-user-course/src/main/java/com/fs/framework/config/ServerConfig.java

@@ -0,0 +1,33 @@
+package com.fs.framework.config;
+
+import com.fs.common.utils.ServletUtils;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 服务相关配置
+ *
+
+ */
+@Component
+public class ServerConfig
+{
+    /**
+     * 获取完整的请求路径,包括:域名,端口,上下文访问路径
+     *
+     * @return 服务地址
+     */
+    public String getUrl()
+    {
+        HttpServletRequest request = ServletUtils.getRequest();
+        return getDomain(request);
+    }
+
+    public static String getDomain(HttpServletRequest request)
+    {
+        StringBuffer url = request.getRequestURL();
+        String contextPath = request.getServletContext().getContextPath();
+        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
+    }
+}

+ 124 - 0
fs-user-course/src/main/java/com/fs/framework/config/SwaggerConfig.java

@@ -0,0 +1,124 @@
+package com.fs.framework.config;
+
+import com.fs.common.config.FSConfig;
+import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Swagger2的接口配置
+ *
+
+ */
+@Configuration
+@EnableSwagger2
+@EnableSwaggerBootstrapUI
+public class SwaggerConfig
+{
+    /** 系统基础配置 */
+    @Autowired
+    private FSConfig fsConfig;
+
+    /** 是否开启swagger */
+    @Value("${swagger.enabled}")
+    private boolean enabled;
+
+    /** 设置请求的统一前缀 */
+    @Value("${swagger.pathMapping}")
+    private String pathMapping;
+
+    /**
+     * 创建API
+     */
+    @Bean
+    public Docket createRestApi()
+    {
+        return new Docket(DocumentationType.SWAGGER_2)
+                // 是否启用Swagger
+                .enable(enabled)
+                // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
+                .apiInfo(apiInfo())
+                // 设置哪些接口暴露给Swagger展示
+                .select()
+                // 扫描所有有注解的api,用这种方式更灵活
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                // 扫描指定包中的swagger注解
+                // .apis(RequestHandlerSelectors.basePackage("com.fs.project.tool.swagger"))
+                // 扫描所有 .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build()
+                /* 设置安全模式,swagger可以设置访问token */
+                .securitySchemes(securitySchemes())
+                .securityContexts(securityContexts())
+                .pathMapping(pathMapping);
+    }
+
+    /**
+     * 安全模式,这里指定token通过Authorization头请求头传递
+     */
+    private List<ApiKey> securitySchemes()
+    {
+        List<ApiKey> apiKeyList = new ArrayList<ApiKey>();
+        apiKeyList.add(new ApiKey("Authorization", "AppToken", "header"));
+        return apiKeyList;
+    }
+
+    /**
+     * 安全上下文
+     */
+    private List<SecurityContext> securityContexts()
+    {
+        List<SecurityContext> securityContexts = new ArrayList<>();
+        securityContexts.add(
+                SecurityContext.builder()
+                        .securityReferences(defaultAuth())
+                        .forPaths(PathSelectors.regex("^(?!auth).*$"))
+                        .build());
+        return securityContexts;
+    }
+
+    /**
+     * 默认的安全上引用
+     */
+    private List<SecurityReference> defaultAuth()
+    {
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        List<SecurityReference> securityReferences = new ArrayList<>();
+        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
+        return securityReferences;
+    }
+
+    /**
+     * 添加摘要信息
+     */
+    private ApiInfo apiInfo()
+    {
+        // 用ApiInfoBuilder进行定制
+        return new ApiInfoBuilder()
+                // 设置标题
+                .title("标题:FS管理系统_接口文档")
+                // 描述
+                .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
+                // 作者信息
+                .contact(new Contact(fsConfig.getName(), null, null))
+                // 版本
+                .version("版本号:" + fsConfig.getVersion())
+                .build();
+    }
+}

+ 63 - 0
fs-user-course/src/main/java/com/fs/framework/config/ThreadPoolConfig.java

@@ -0,0 +1,63 @@
+package com.fs.framework.config;
+
+import com.fs.common.utils.Threads;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 线程池配置
+ *
+
+ **/
+@Configuration
+public class ThreadPoolConfig
+{
+    // 核心线程池大小
+    private int corePoolSize = 50;
+
+    // 最大可创建的线程数
+    private int maxPoolSize = 200;
+
+    // 队列最大长度
+    private int queueCapacity = 1000;
+
+    // 线程池维护线程所允许的空闲时间
+    private int keepAliveSeconds = 300;
+
+    @Bean(name = "threadPoolTaskExecutor")
+    public ThreadPoolTaskExecutor threadPoolTaskExecutor()
+    {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setMaxPoolSize(maxPoolSize);
+        executor.setCorePoolSize(corePoolSize);
+        executor.setQueueCapacity(queueCapacity);
+        executor.setKeepAliveSeconds(keepAliveSeconds);
+        // 线程池对拒绝任务(无线程可用)的处理策略
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        return executor;
+    }
+
+    /**
+     * 执行周期性或定时任务
+     */
+    @Bean(name = "scheduledExecutorService")
+    protected ScheduledExecutorService scheduledExecutorService()
+    {
+        return new ScheduledThreadPoolExecutor(corePoolSize,
+                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build())
+        {
+            @Override
+            protected void afterExecute(Runnable r, Throwable t)
+            {
+                super.afterExecute(r, t);
+                Threads.printException(r, t);
+            }
+        };
+    }
+}

+ 77 - 0
fs-user-course/src/main/java/com/fs/framework/config/properties/DruidProperties.java

@@ -0,0 +1,77 @@
+package com.fs.framework.config.properties;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * druid 配置属性
+ *
+
+ */
+@Configuration
+public class DruidProperties
+{
+    @Value("${spring.datasource.mysql.druid.initialSize}")
+    private int initialSize;
+
+    @Value("${spring.datasource.mysql.druid.minIdle}")
+    private int minIdle;
+
+    @Value("${spring.datasource.mysql.druid.maxActive}")
+    private int maxActive;
+
+    @Value("${spring.datasource.mysql.druid.maxWait}")
+    private int maxWait;
+
+    @Value("${spring.datasource.mysql.druid.timeBetweenEvictionRunsMillis}")
+    private int timeBetweenEvictionRunsMillis;
+
+    @Value("${spring.datasource.mysql.druid.minEvictableIdleTimeMillis}")
+    private int minEvictableIdleTimeMillis;
+
+    @Value("${spring.datasource.mysql.druid.maxEvictableIdleTimeMillis}")
+    private int maxEvictableIdleTimeMillis;
+
+    @Value("${spring.datasource.mysql.druid.validationQuery}")
+    private String validationQuery;
+
+    @Value("${spring.datasource.mysql.druid.testWhileIdle}")
+    private boolean testWhileIdle;
+
+    @Value("${spring.datasource.mysql.druid.testOnBorrow}")
+    private boolean testOnBorrow;
+
+    @Value("${spring.datasource.mysql.druid.testOnReturn}")
+    private boolean testOnReturn;
+
+    public DruidDataSource dataSource(DruidDataSource datasource)
+    {
+        /** 配置初始化大小、最小、最大 */
+        datasource.setInitialSize(initialSize);
+        datasource.setMaxActive(maxActive);
+        datasource.setMinIdle(minIdle);
+
+        /** 配置获取连接等待超时的时间 */
+        datasource.setMaxWait(maxWait);
+
+        /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
+        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+
+        /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */
+        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
+        datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
+
+        /**
+         * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
+         */
+        datasource.setValidationQuery(validationQuery);
+        /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
+        datasource.setTestWhileIdle(testWhileIdle);
+        /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
+        datasource.setTestOnBorrow(testOnBorrow);
+        /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
+        datasource.setTestOnReturn(testOnReturn);
+        return datasource;
+    }
+}

+ 27 - 0
fs-user-course/src/main/java/com/fs/framework/datasource/DynamicDataSource.java

@@ -0,0 +1,27 @@
+package com.fs.framework.datasource;
+
+import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
+
+import javax.sql.DataSource;
+import java.util.Map;
+
+/**
+ * 动态数据源
+ *
+
+ */
+public class DynamicDataSource extends AbstractRoutingDataSource
+{
+    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
+    {
+        super.setDefaultTargetDataSource(defaultTargetDataSource);
+        super.setTargetDataSources(targetDataSources);
+        super.afterPropertiesSet();
+    }
+
+    @Override
+    protected Object determineCurrentLookupKey()
+    {
+        return DynamicDataSourceContextHolder.getDataSourceType();
+    }
+}

+ 44 - 0
fs-user-course/src/main/java/com/fs/framework/datasource/DynamicDataSourceContextHolder.java

@@ -0,0 +1,44 @@
+package com.fs.framework.datasource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 数据源切换处理
+ *
+
+ */
+public class DynamicDataSourceContextHolder {
+    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
+
+    /**
+     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
+     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
+     */
+    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
+
+    /**
+     * 设置数据源的变量
+     */
+    public static void setDataSourceType(String dsType)
+    {
+//        log.info("切换到{}数据源", dsType);
+        CONTEXT_HOLDER.set(dsType);
+    }
+
+    /**
+     * 获得数据源的变量
+     */
+    public static String getDataSourceType()
+    {
+        return CONTEXT_HOLDER.get();
+    }
+
+    /**
+     * 清空数据源变量
+     */
+    public static void clearDataSourceType()
+    {
+        CONTEXT_HOLDER.remove();
+    }
+}

+ 56 - 0
fs-user-course/src/main/java/com/fs/framework/interceptor/RepeatSubmitInterceptor.java

@@ -0,0 +1,56 @@
+package com.fs.framework.interceptor;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.utils.ServletUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+
+/**
+ * 防止重复提交拦截器
+ *
+
+ */
+@Component
+public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
+{
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
+    {
+        if (handler instanceof HandlerMethod)
+        {
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+            Method method = handlerMethod.getMethod();
+            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
+            if (annotation != null)
+            {
+                if (this.isRepeatSubmit(request))
+                {
+                    AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
+                    ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
+                    return false;
+                }
+            }
+            return true;
+        }
+        else
+        {
+            return super.preHandle(request, response, handler);
+        }
+    }
+
+    /**
+     * 验证是否重复提交由子类实现具体的防重复提交的规则
+     *
+     * @param request
+     * @return
+     * @throws Exception
+     */
+    public abstract boolean isRepeatSubmit(HttpServletRequest request);
+}

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff