Przeglądaj źródła

Merge remote-tracking branch 'origin/ScrmStores' into ScrmStores

ct 1 dzień temu
rodzic
commit
f44e22877d
100 zmienionych plików z 4336 dodań i 227 usunięć
  1. 75 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyTrafficController.java
  2. 49 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyTrafficLogController.java
  3. 20 0
      fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchCommentController.java
  4. 1 0
      fs-admin/src/main/java/com/fs/course/controller/qw/QwFsCourseWatchLogController.java
  5. 97 0
      fs-admin/src/main/java/com/fs/fastGpt/FastGptChatReplaceTextController.java
  6. 112 0
      fs-admin/src/main/java/com/fs/fastGpt/FastGptKeywordSendController.java
  7. 139 0
      fs-admin/src/main/java/com/fs/fastGpt/FastgptEventLogTotalController.java
  8. 8 5
      fs-admin/src/main/java/com/fs/his/controller/FsFirstDiagnosisController.java
  9. 224 4
      fs-admin/src/main/java/com/fs/his/task/Task.java
  10. 12 4
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java
  11. 9 5
      fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java
  12. 24 1
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  13. 1 0
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseWatchLogController.java
  14. 1 0
      fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.java
  15. 134 0
      fs-company/src/main/java/com/fs/company/controller/fastGpt/FastGptKeywordSendController.java
  16. 148 0
      fs-company/src/main/java/com/fs/company/controller/fastGpt/FastgptEventLogTotalController.java
  17. 1 0
      fs-company/src/main/java/com/fs/framework/config/SecurityConfig.java
  18. 36 0
      fs-doctor-app/src/main/java/com/fs/app/controller/DiagnosisController.java
  19. 1 0
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  20. 1 1
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  21. 118 41
      fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java
  22. 52 16
      fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java
  23. 5 0
      fs-service/src/main/java/com/fs/company/constant/CompanyTrafficConstants.java
  24. 8 0
      fs-service/src/main/java/com/fs/company/domain/Company.java
  25. 41 0
      fs-service/src/main/java/com/fs/company/domain/CompanyTrafficRecord.java
  26. 45 0
      fs-service/src/main/java/com/fs/company/domain/CompanyTrafficRecordLog.java
  27. 6 2
      fs-service/src/main/java/com/fs/company/mapper/CompanyMapper.java
  28. 10 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyTrafficRecordLogMapper.java
  29. 14 0
      fs-service/src/main/java/com/fs/company/mapper/CompanyTrafficRecordMapper.java
  30. 23 0
      fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordChargeParam.java
  31. 20 0
      fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordLogQueryParam.java
  32. 20 0
      fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordQueryParam.java
  33. 7 0
      fs-service/src/main/java/com/fs/company/service/ICompanyService.java
  34. 16 0
      fs-service/src/main/java/com/fs/company/service/ICompanyTrafficRecordLogService.java
  35. 20 0
      fs-service/src/main/java/com/fs/company/service/ICompanyTrafficRecordService.java
  36. 10 5
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  37. 39 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyTrafficRecordLogServiceImpl.java
  38. 220 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyTrafficRecordServiceImpl.java
  39. 4 0
      fs-service/src/main/java/com/fs/company/vo/CompanyVO.java
  40. 36 6
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  41. 2 0
      fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogStatisticsListParam.java
  42. 13 0
      fs-service/src/main/java/com/fs/course/param/FsFirstDiagnosisListUParam.java
  43. 11 0
      fs-service/src/main/java/com/fs/course/param/FsInquiryPatientInfoUParam.java
  44. 11 2
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  45. 69 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  46. 2 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListVO.java
  47. 77 0
      fs-service/src/main/java/com/fs/course/vo/FsFirstDiagnosisListUVO.java
  48. 113 0
      fs-service/src/main/java/com/fs/course/vo/FsInquiryPatientInfoListUVO.java
  49. 2 2
      fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java
  50. 96 0
      fs-service/src/main/java/com/fs/fastGpt/domain/FastGptChatReplaceText.java
  51. 2 0
      fs-service/src/main/java/com/fs/fastGpt/domain/FastGptRole.java
  52. 68 0
      fs-service/src/main/java/com/fs/fastGpt/mapper/FastGptChatReplaceTextMapper.java
  53. 10 0
      fs-service/src/main/java/com/fs/fastGpt/mapper/FastGptRoleMapper.java
  54. 93 0
      fs-service/src/main/java/com/fs/fastGpt/mapper/FastgptEventLogTotalMapper.java
  55. 76 0
      fs-service/src/main/java/com/fs/fastGpt/param/FastgptEventLogTotalParam.java
  56. 62 0
      fs-service/src/main/java/com/fs/fastGpt/service/IFastGptChatReplaceTextService.java
  57. 8 0
      fs-service/src/main/java/com/fs/fastGpt/service/IFastGptRoleService.java
  58. 94 0
      fs-service/src/main/java/com/fs/fastGpt/service/IFastgptEventLogTotalService.java
  59. 101 30
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  60. 0 2
      fs-service/src/main/java/com/fs/fastGpt/service/impl/FastGptChatMsgServiceImpl.java
  61. 93 0
      fs-service/src/main/java/com/fs/fastGpt/service/impl/FastGptChatReplaceTextServiceImpl.java
  62. 77 0
      fs-service/src/main/java/com/fs/fastGpt/service/impl/FastGptRoleServiceImpl.java
  63. 290 0
      fs-service/src/main/java/com/fs/fastGpt/service/impl/FastgptEventLogTotalServiceImpl.java
  64. 19 0
      fs-service/src/main/java/com/fs/fastGpt/vo/FastGptRoleDataVO.java
  65. 70 0
      fs-service/src/main/java/com/fs/fastGpt/vo/FastgptEventLogTotalVo.java
  66. 110 8
      fs-service/src/main/java/com/fs/fastgptApi/util/AiImgUtil.java
  67. 2 2
      fs-service/src/main/java/com/fs/fastgptApi/util/EventLogUtils.java
  68. 20 1
      fs-service/src/main/java/com/fs/gtPush/domain/PushReqBean.java
  69. 93 0
      fs-service/src/main/java/com/fs/gtPush/service/impl/uniPush2ServiceImpl.java
  70. 4 0
      fs-service/src/main/java/com/fs/gtPush/service/uniPush2Service.java
  71. 120 0
      fs-service/src/main/java/com/fs/gtPush/utils/PushUtils.java
  72. 6 0
      fs-service/src/main/java/com/fs/his/domain/FsFirstDiagnosis.java
  73. 12 0
      fs-service/src/main/java/com/fs/his/dto/FindUsersByDTO.java
  74. 35 0
      fs-service/src/main/java/com/fs/his/enums/PushLogDesTypeEnum.java
  75. 34 0
      fs-service/src/main/java/com/fs/his/enums/PushLogTypeEnum.java
  76. 53 0
      fs-service/src/main/java/com/fs/his/mapper/FsFirstDiagnosisMapper.java
  77. 25 0
      fs-service/src/main/java/com/fs/his/mapper/FsInquiryPatientInfoMapper.java
  78. 4 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  79. 12 0
      fs-service/src/main/java/com/fs/his/param/FindUserByParam.java
  80. 17 0
      fs-service/src/main/java/com/fs/his/param/FsDiagnosisFillDParam.java
  81. 13 0
      fs-service/src/main/java/com/fs/his/param/FsDiagnosisListDParam.java
  82. 25 0
      fs-service/src/main/java/com/fs/his/param/FsFirstDiagnosisParam.java
  83. 2 0
      fs-service/src/main/java/com/fs/his/param/FsInquiryOrderDoPayParam.java
  84. 2 0
      fs-service/src/main/java/com/fs/his/param/FsPackageOrderDoPayParam.java
  85. 2 0
      fs-service/src/main/java/com/fs/his/param/FsStoreOrderDoPayParam.java
  86. 26 5
      fs-service/src/main/java/com/fs/his/service/IFsFirstDiagnosisService.java
  87. 8 0
      fs-service/src/main/java/com/fs/his/service/IFsInquiryPatientInfoService.java
  88. 8 0
      fs-service/src/main/java/com/fs/his/service/IFsUserService.java
  89. 110 1
      fs-service/src/main/java/com/fs/his/service/impl/FsFirstDiagnosisServiceImpl.java
  90. 17 0
      fs-service/src/main/java/com/fs/his/service/impl/FsInquiryPatientInfoServiceImpl.java
  91. 52 32
      fs-service/src/main/java/com/fs/his/service/impl/FsPackageOrderServiceImpl.java
  92. 123 49
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java
  93. 7 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  94. 33 0
      fs-service/src/main/java/com/fs/his/vo/FsDiagnosisListDVO.java
  95. 2 0
      fs-service/src/main/java/com/fs/his/vo/FsDoctorArticleUVO.java
  96. 81 0
      fs-service/src/main/java/com/fs/his/vo/FsFirstDiagnosisVO.java
  97. 1 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  98. 4 2
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  99. 3 0
      fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java
  100. 4 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

+ 75 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyTrafficController.java

@@ -0,0 +1,75 @@
+package com.fs.company.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.company.domain.CompanyTrafficRecord;
+import com.fs.company.param.CompanyTrafficRecordChargeParam;
+import com.fs.company.param.CompanyTrafficRecordQueryParam;
+import com.fs.company.service.ICompanyTrafficRecordService;
+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;
+
+@RestController
+@RequestMapping("/company/traffic")
+public class CompanyTrafficController extends BaseController {
+
+    @Autowired
+    private ICompanyTrafficRecordService companyTrafficRecordService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    /** 充值流量 */
+    @PreAuthorize("@ss.hasPermi('company:traffic:charge')")
+    @Log(title = "公司流量充值", businessType = BusinessType.INSERT)
+    @PostMapping(value = "/rechargeTraffic")
+    public R rechargeTraffic(@RequestBody CompanyTrafficRecordChargeParam companyTrafficRecord) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        companyTrafficRecord.setUserId(loginUser.getUser().getUserId());
+        companyTrafficRecord.setUserName(loginUser.getUser().getUserName());
+        companyTrafficRecordService.recharge(companyTrafficRecord);
+        return R.ok();
+    }
+
+    /** 流量充值记录查询 */
+    @PreAuthorize("@ss.hasPermi('company:traffic:list')")
+    @GetMapping(value = "/list")
+    public TableDataInfo list(CompanyTrafficRecordQueryParam param) {
+        startPage();
+        List<CompanyTrafficRecord> list = companyTrafficRecordService.selectList(param);
+        return getDataTable(list);
+    }
+
+    /** 流量充值详情 */
+    @PreAuthorize("@ss.hasPermi('company:traffic:detail')")
+    @GetMapping(value = "/detail/{logId}")
+    public R detail(@PathVariable("logId") Long logId) {
+        CompanyTrafficRecord record = companyTrafficRecordService.selectById(logId);
+        return R.ok().put("data", record);
+    }
+
+    /** 流量换算*/
+    @GetMapping(value = "/trafficConversion")
+    public R trafficConversion(@RequestParam("traffic") Long amount) {
+        Long trafficKB = companyTrafficRecordService.trafficConversion(amount);
+        return R.ok().put("data", trafficKB);
+    }
+
+    /** 刷新单价*/
+    @GetMapping(value = "/refreshPrice")
+    public R refreshPrice() {
+        companyTrafficRecordService.refreshPrice();
+        return R.ok();
+    }
+
+    /** 流量统计 TODO 待完善*/
+}

+ 49 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyTrafficLogController.java

@@ -0,0 +1,49 @@
+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.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import com.fs.company.param.CompanyTrafficRecordLogQueryParam;
+import com.fs.company.service.ICompanyTrafficRecordLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/company/trafficLog")
+public class CompanyTrafficLogController extends BaseController {
+
+    @Autowired
+    private ICompanyTrafficRecordLogService companyTrafficRecordLogService;
+
+
+    /** 流量充值记录查询 */
+    @PreAuthorize("@ss.hasPermi('company:trafficLog:list')")
+    @GetMapping(value = "/list")
+    public TableDataInfo list(CompanyTrafficRecordLogQueryParam param) {
+        startPage();
+        List<CompanyTrafficRecordLog> list = companyTrafficRecordLogService.selectList(param);
+        return getDataTable(list);
+    }
+
+
+    /** 导出 */
+    @PreAuthorize("@ss.hasPermi('company:trafficLog:export')")
+    @Log(title = "流量充值记录导出", businessType = BusinessType.EXPORT)
+    @GetMapping(value = "/export")
+    public AjaxResult export(CompanyTrafficRecordLogQueryParam param) {
+        List<CompanyTrafficRecordLog> list = companyTrafficRecordLogService.selectList(param);
+        ExcelUtil<CompanyTrafficRecordLog> util = new ExcelUtil<>(CompanyTrafficRecordLog.class);
+        return util.exportExcel(list, "流量充值记录");
+    }
+
+
+
+}

+ 20 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCourseWatchCommentController.java

@@ -70,6 +70,14 @@ public class FsCourseWatchCommentController extends BaseController
         return util.exportExcel(list, "看课评论数据");
     }
 
+    @PreAuthorize("@ss.hasPermi('course:courseWatchComment:edit')")
+    @Log(title = "看课评论", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateBarrageStatus")
+    public AjaxResult updateBarrageStatus(@RequestBody FsCourseWatchComment fsCourseWatchComment)
+    {
+        return toAjax(fsCourseWatchCommentService.updateFsCourseWatchComment(fsCourseWatchComment));
+    }
+
     /**
      * 获取看课评论详细信息
      */
@@ -125,4 +133,16 @@ public class FsCourseWatchCommentController extends BaseController
         }
     }
 
+    @Log(title = "手动解除外部联系人拉黑用户", businessType = BusinessType.UPDATE)
+    @PutMapping("/clearBlack")
+    public R clearBlack(Integer commentStatus, Long fsUserId)
+    {
+        int i = qwExternalContactService.updateQwExternalContactByFsUserId(commentStatus, fsUserId);
+        if (i > 0){
+            return R.ok();
+        } else {
+            return R.error();
+        }
+    }
+
 }

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

@@ -71,6 +71,7 @@ public class QwFsCourseWatchLogController extends BaseController
         if (param.getSTime()==null||param.getETime()==null){
             return getDataTable(new ArrayList<>());
         }
+        param.setSendType(2); //企微
         List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
         return getDataTable(list);
     }

+ 97 - 0
fs-admin/src/main/java/com/fs/fastGpt/FastGptChatReplaceTextController.java

@@ -0,0 +1,97 @@
+package com.fs.fastGpt;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.fastGpt.domain.FastGptChatReplaceText;
+import com.fs.fastGpt.service.IFastGptChatReplaceTextService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 易错词语Controller
+ *
+ * @author fs
+ * @date 2025-01-18
+ */
+@RestController
+@RequestMapping("/fastGpt/fastGptChatReplaceText")
+public class FastGptChatReplaceTextController extends BaseController
+{
+    @Autowired
+    private IFastGptChatReplaceTextService fastGptChatReplaceTextService;
+
+    /**
+     * 查询易错词语列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatReplaceText:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FastGptChatReplaceText fastGptChatReplaceText)
+    {
+        startPage();
+        List<FastGptChatReplaceText> list = fastGptChatReplaceTextService.selectFastGptChatReplaceTextList(fastGptChatReplaceText);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出易错词语列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatReplaceText:export')")
+    @Log(title = "易错词语", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FastGptChatReplaceText fastGptChatReplaceText)
+    {
+        List<FastGptChatReplaceText> list = fastGptChatReplaceTextService.selectFastGptChatReplaceTextList(fastGptChatReplaceText);
+        ExcelUtil<FastGptChatReplaceText> util = new ExcelUtil<FastGptChatReplaceText>(FastGptChatReplaceText.class);
+        return util.exportExcel(list, "易错词语数据");
+    }
+
+    /**
+     * 获取易错词语详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatReplaceText:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fastGptChatReplaceTextService.selectFastGptChatReplaceTextById(id));
+    }
+
+    /**
+     * 新增易错词语
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatReplaceText:add')")
+    @Log(title = "易错词语", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FastGptChatReplaceText fastGptChatReplaceText)
+    {
+        return toAjax(fastGptChatReplaceTextService.insertFastGptChatReplaceText(fastGptChatReplaceText));
+    }
+
+    /**
+     * 修改易错词语
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatReplaceText:edit')")
+    @Log(title = "易错词语", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FastGptChatReplaceText fastGptChatReplaceText)
+    {
+        return toAjax(fastGptChatReplaceTextService.updateFastGptChatReplaceText(fastGptChatReplaceText));
+    }
+
+    /**
+     * 删除易错词语
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptChatReplaceText:remove')")
+    @Log(title = "易错词语", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fastGptChatReplaceTextService.deleteFastGptChatReplaceTextByIds(ids));
+    }
+}

+ 112 - 0
fs-admin/src/main/java/com/fs/fastGpt/FastGptKeywordSendController.java

@@ -0,0 +1,112 @@
+package com.fs.fastGpt;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.fastGpt.domain.FastGptKeyword;
+import com.fs.fastGpt.domain.FastGptKeywordSend;
+import com.fs.fastGpt.service.IFastGptKeywordSendService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * Ai事件表(根据关键字发送文本和图片)Controller
+ * 
+ * @author fs
+ * @date 2025-05-12
+ */
+@RestController
+@RequestMapping("/fastGpt/fastGptKeywordSend")
+public class FastGptKeywordSendController extends BaseController
+{
+    @Autowired
+    private IFastGptKeywordSendService fastGptKeywordSendService;
+
+    @GetMapping("/keywordList")
+    public R keywordList()
+    {
+        List<FastGptKeyword> list = fastGptKeywordSendService.selectFastGptKeywordList(1);
+        return R.ok().put("data",list);
+    }
+    /**
+     * 查询Ai事件表(根据关键字发送文本和图片)列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FastGptKeywordSend fastGptKeywordSend)
+    {
+        startPage();
+        fastGptKeywordSend.setKeywordType(1L);
+        List<FastGptKeywordSend> list = fastGptKeywordSendService.selectFastGptKeywordSendList(fastGptKeywordSend);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出Ai事件表(根据关键字发送文本和图片)列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:export')")
+    @Log(title = "Ai事件表(根据关键字发送文本和图片)", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FastGptKeywordSend fastGptKeywordSend)
+    {
+        fastGptKeywordSend.setKeywordType(1L);
+        List<FastGptKeywordSend> list = fastGptKeywordSendService.selectFastGptKeywordSendList(fastGptKeywordSend);
+        ExcelUtil<FastGptKeywordSend> util = new ExcelUtil<FastGptKeywordSend>(FastGptKeywordSend.class);
+        return util.exportExcel(list, "Ai事件表(根据关键字发送文本和图片)数据");
+    }
+
+    /**
+     * 获取Ai事件表(根据关键字发送文本和图片)详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        Long keywordType = 1L;
+        return AjaxResult.success(fastGptKeywordSendService.selectFastGptKeywordSendById(id,keywordType));
+    }
+
+    /**
+     * 新增Ai事件表(根据关键字发送文本和图片)
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:add')")
+    @Log(title = "Ai事件表(根据关键字发送文本和图片)", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FastGptKeywordSend fastGptKeywordSend)
+    {
+        fastGptKeywordSend.setKeywordType(1L);
+        fastGptKeywordSend.setRoleIds(null);
+        return toAjax(fastGptKeywordSendService.insertFastGptKeywordSend(fastGptKeywordSend));
+    }
+
+    /**
+     * 修改Ai事件表(根据关键字发送文本和图片)
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:edit')")
+    @Log(title = "Ai事件表(根据关键字发送文本和图片)", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FastGptKeywordSend fastGptKeywordSend)
+    {
+        fastGptKeywordSend.setKeywordType(1L);
+        fastGptKeywordSend.setRoleIds(null);
+        return toAjax(fastGptKeywordSendService.updateFastGptKeywordSend(fastGptKeywordSend));
+    }
+
+    /**
+     * 删除Ai事件表(根据关键字发送文本和图片)
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:remove')")
+    @Log(title = "Ai事件表(根据关键字发送文本和图片)", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fastGptKeywordSendService.deleteFastGptKeywordSendByIds(ids));
+    }
+}

+ 139 - 0
fs-admin/src/main/java/com/fs/fastGpt/FastgptEventLogTotalController.java

@@ -0,0 +1,139 @@
+package com.fs.fastGpt;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.fastGpt.domain.FastgptEventLogTotal;
+import com.fs.fastGpt.param.FastgptEventLogTotalParam;
+import com.fs.fastGpt.service.IFastGptRoleService;
+import com.fs.fastGpt.service.IFastgptEventLogTotalService;
+import com.fs.fastGpt.vo.FastgptEventLogTotalVo;
+import com.github.pagehelper.PageInfo;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * ai事件埋点统计Controller
+ * 
+ * @author fs
+ * @date 2025-06-26
+ */
+@RestController
+@RequestMapping("/fastGpt/fastgptEventLogTotal")
+public class FastgptEventLogTotalController extends BaseController
+{
+    @Autowired
+    private IFastgptEventLogTotalService fastgptEventLogTotalService;
+
+    @Autowired
+    private IFastGptRoleService roleService;
+
+    /**
+     * 查询ai事件埋点统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:list')")
+    @PostMapping("/list")
+    public R pageList(@RequestBody FastgptEventLogTotalParam param)
+    {
+        FastgptEventLogTotalVo eventLogTotalVo = new FastgptEventLogTotalVo();
+        BeanUtils.copyProperties(param,eventLogTotalVo);
+        List<FastgptEventLogTotalVo> list = fastgptEventLogTotalService.selectFastgptEventLogTotalVoInfoList(eventLogTotalVo);
+
+        //统计对应的list的合
+        FastgptEventLogTotalVo totalVo = fastgptEventLogTotalService.totalFastgptEventLog(list);
+
+        int pageNum = param.getPageNum();
+        int pageSize = param.getPageSize();
+
+        int total = 0;
+        if (list != null) {
+            total = list.size();
+        }
+
+        int fromIndex = (pageNum - 1) * pageSize;
+        int toIndex = Math.min(fromIndex + pageSize, total);
+        PageInfo<FastgptEventLogTotalVo> pageInfo = new PageInfo<>();
+        if (list != null) {
+            List<FastgptEventLogTotalVo> paginatedList = list.subList(fromIndex, toIndex);
+            if(totalVo != null){
+                paginatedList.add(totalVo);
+            }
+            pageInfo = new PageInfo<>(paginatedList);
+        }else{
+            pageInfo = new PageInfo<>();
+        }
+        pageInfo.setTotal(total);
+        return R.ok().put("data",pageInfo);
+    }
+
+    /**
+     * 查询appKey
+     * @return  list
+     */
+    @GetMapping("/getFastGptRoleAppKeyList")
+    public R getFastGptRoleAppKeyList() {
+        return R.ok().put("data", roleService.selectFastGptRoleAppKeyList());
+    }
+    /**
+     * 导出ai事件埋点统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:export')")
+    @Log(title = "ai事件埋点统计", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FastgptEventLogTotal fastgptEventLogTotal)
+    {
+        List<FastgptEventLogTotal> list = fastgptEventLogTotalService.selectFastgptEventLogTotalList(fastgptEventLogTotal);
+        ExcelUtil<FastgptEventLogTotal> util = new ExcelUtil<FastgptEventLogTotal>(FastgptEventLogTotal.class);
+        return util.exportExcel(list, "ai事件埋点统计数据");
+    }
+
+    /**
+     * 获取ai事件埋点统计详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fastgptEventLogTotalService.selectFastgptEventLogTotalById(id));
+    }
+
+    /**
+     * 新增ai事件埋点统计
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:add')")
+    @Log(title = "ai事件埋点统计", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FastgptEventLogTotal fastgptEventLogTotal)
+    {
+        return toAjax(fastgptEventLogTotalService.insertFastgptEventLogTotal(fastgptEventLogTotal));
+    }
+
+    /**
+     * 修改ai事件埋点统计
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:edit')")
+    @Log(title = "ai事件埋点统计", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FastgptEventLogTotal fastgptEventLogTotal)
+    {
+        return toAjax(fastgptEventLogTotalService.updateFastgptEventLogTotal(fastgptEventLogTotal));
+    }
+
+    /**
+     * 删除ai事件埋点统计
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:remove')")
+    @Log(title = "ai事件埋点统计", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fastgptEventLogTotalService.deleteFastgptEventLogTotalByIds(ids));
+    }
+}

+ 8 - 5
fs-admin/src/main/java/com/fs/his/controller/FsFirstDiagnosisController.java

@@ -1,6 +1,9 @@
 package com.fs.his.controller;
 
 import java.util.List;
+
+import com.fs.his.param.FsFirstDiagnosisParam;
+import com.fs.his.vo.FsFirstDiagnosisVO;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -38,10 +41,10 @@ public class FsFirstDiagnosisController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('his:fsFirstDiagnosis:list')")
     @GetMapping("/list")
-    public TableDataInfo list(FsFirstDiagnosis fsFirstDiagnosis)
+    public TableDataInfo list(FsFirstDiagnosisParam fsFirstDiagnosis)
     {
         startPage();
-        List<FsFirstDiagnosis> list = fsFirstDiagnosisService.selectFsFirstDiagnosisList(fsFirstDiagnosis);
+        List<FsFirstDiagnosisVO> list = fsFirstDiagnosisService.selectFsFirstDiagnosisVOList(fsFirstDiagnosis);
         return getDataTable(list);
     }
 
@@ -51,10 +54,10 @@ public class FsFirstDiagnosisController extends BaseController
     @PreAuthorize("@ss.hasPermi('his:fsFirstDiagnosis:export')")
     @Log(title = "初诊单", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
-    public AjaxResult export(FsFirstDiagnosis fsFirstDiagnosis)
+    public AjaxResult export(FsFirstDiagnosisParam fsFirstDiagnosis)
     {
-        List<FsFirstDiagnosis> list = fsFirstDiagnosisService.selectFsFirstDiagnosisList(fsFirstDiagnosis);
-        ExcelUtil<FsFirstDiagnosis> util = new ExcelUtil<FsFirstDiagnosis>(FsFirstDiagnosis.class);
+        List<FsFirstDiagnosisVO> list = fsFirstDiagnosisService.selectFsFirstDiagnosisVOList(fsFirstDiagnosis);
+        ExcelUtil<FsFirstDiagnosisVO> util = new ExcelUtil<FsFirstDiagnosisVO>(FsFirstDiagnosisVO.class);
         return util.exportExcel(list, "初诊单数据");
     }
 

+ 224 - 4
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -27,7 +27,10 @@ import com.fs.erp.dto.ErpOrderQueryResponse;
 import com.fs.erp.dto.ErpOrderResponse;
 import com.fs.erp.mapper.FsErpFinishPushMapper;
 import com.fs.erp.service.IErpOrderService;
+import com.fs.fastGpt.domain.FastGptEventTokenLog;
+import com.fs.fastGpt.domain.FastgptEventLogTotal;
 import com.fs.fastGpt.mapper.FastGptChatSessionMapper;
+import com.fs.fastGpt.service.IFastgptEventLogTotalService;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.config.StoreConfig;
 import com.fs.his.domain.FsInquiryOrder;
@@ -58,10 +61,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 @Slf4j
 @Component("task")
@@ -161,6 +161,226 @@ public class Task {
     @Autowired
     private ICompanyUserService userService;
 
+    @Autowired
+    private IFastgptEventLogTotalService fastgptEventLogTotalService;
+
+    //统计ai事件埋点
+    public void eventLogTotals() {
+        // 判断是否是凌晨 00:00 - 00:59
+        boolean isEarlyMorning = isEarlyMorning();
+
+        // 获取日期字符串(今天或昨天)
+        String dateTime;
+        Date date;
+        if (isEarlyMorning) {
+            dateTime = DateUtils.addDateDays(-1); // 昨天
+            date = DateUtils.addDays(new Date(), -1); // 昨天的 Date 对象
+        } else {
+            dateTime = DateUtils.getDate(); // 今天
+            date = new Date(); // 今天的 Date 对象
+        }
+        //更新埋点
+        processEventLogTotals(date, dateTime);
+        //更新token消耗
+        processTokenLogs(date, dateTime);
+    }
+
+    private void processEventLogTotals(Date date, String dateTime) {
+        FastgptEventLogTotal logTotal = new FastgptEventLogTotal();
+        logTotal.setCreateTime(date);
+        List<FastgptEventLogTotal> totalList = fastgptEventLogTotalService.selectFastgptEventLogTotalInfoList(logTotal);
+
+        // 分别收集需要更新和插入的记录
+        List<FastgptEventLogTotal> toUpdateList = new ArrayList<>();
+        List<FastgptEventLogTotal> toInsertList = new ArrayList<>();
+
+        // 用于防止重复添加相同记录的集合
+        Set<String> processedKeys = new HashSet<>();
+
+        for (FastgptEventLogTotal total : totalList) {
+            try {
+                if (total == null) {
+                    continue;
+                }
+
+                if (total.getType() == 1) {
+                    total.setCount(total.getSenderCount());
+                }
+                // 构造唯一标识符,用于防止重复处理
+                String uniqueKey = String.format("%d_%d_%d_%d_%d_%s",
+                        total.getRoleId() != null ? total.getRoleId() : 0,
+                        total.getType() != null ? total.getType() : 0,
+                        total.getCompanyId() != null ? total.getCompanyId() : 0,
+                        total.getCompanyUserId() != null ? total.getCompanyUserId() : 0,
+                        total.getQwUserId() != null ? total.getQwUserId() : 0,
+                        dateTime
+                );
+                // 检查是否已经处理过这个记录
+                if (processedKeys.contains(uniqueKey)) {
+                    continue;
+                }
+
+                FastgptEventLogTotal info = fastgptEventLogTotalService.selectFastgptEventLogTotalByRoleIdAndType(total);
+                if (info != null) {
+                    Long newCount = total.getCount() != null ? total.getCount() : 0L;
+                    // 只有当count值发生变化时才加入更新列表
+                    if (!newCount.equals(info.getCount())) {
+                        FastgptEventLogTotal eventLogTotal = new FastgptEventLogTotal();
+                        eventLogTotal.setId(info.getId());
+                        eventLogTotal.setCount(newCount);
+                        if(!processedKeys.contains(uniqueKey)) {
+                            toUpdateList.add(eventLogTotal);
+                            // 标记为已处理
+                            processedKeys.add(uniqueKey);
+                        }
+                    }
+                } else {
+                    total.setStatTime(dateTime);
+                    if(!processedKeys.contains(uniqueKey)) {
+                        toInsertList.add(total);
+                        // 标记为已处理
+                        processedKeys.add(uniqueKey);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("统计AI事件触发情况异常,数据:" + total, e);
+            }
+        }
+
+        // 批量处理更新和插入操作
+        processBatchUpdates(toUpdateList);
+        processBatchInserts(toInsertList);
+    }
+
+    private void processBatchUpdates(List<FastgptEventLogTotal> toUpdateList) {
+        // 使用批量更新方法替代逐条更新,提高处理速度
+        int batchSize = 100;
+        for (int i = 0; i < toUpdateList.size(); i += batchSize) {
+            int endIndex = Math.min(i + batchSize, toUpdateList.size());
+            List<FastgptEventLogTotal> batch = toUpdateList.subList(i, endIndex);
+            try {
+                fastgptEventLogTotalService.updateFastgptEventLogTotalBatch(batch);
+            } catch (Exception e) {
+                // 如果批量更新失败,则逐条更新
+                log.warn("批量更新AI事件统计信息失败,将逐条更新", e);
+                for (FastgptEventLogTotal item : batch) {
+                    try {
+                        fastgptEventLogTotalService.updateFastgptEventLogTotal(item);
+                    } catch (Exception ex) {
+                        log.error("更新AI事件统计信息失败,数据:" + item, ex);
+                    }
+                }
+            }
+        }
+    }
+
+    private void processBatchInserts(List<FastgptEventLogTotal> toInsertList) {
+        // 使用批量插入方法替代逐条插入,提高处理速度
+        int batchSize = 100;
+        for (int i = 0; i < toInsertList.size(); i += batchSize) {
+            int endIndex = Math.min(i + batchSize, toInsertList.size());
+            List<FastgptEventLogTotal> batch = toInsertList.subList(i, endIndex);
+            try {
+                fastgptEventLogTotalService.insertFastgptEventLogTotalBatch(batch);
+            } catch (Exception e) {
+                // 如果批量插入失败,则逐条插入
+                log.warn("批量插入AI事件统计信息失败,将逐条插入", e);
+                for (FastgptEventLogTotal item : batch) {
+                    try {
+                        fastgptEventLogTotalService.insertFastgptEventLogTotal(item);
+                    } catch (Exception ex) {
+                        log.error("插入AI事件统计信息失败,数据:" + item, ex);
+                    }
+                }
+            }
+        }
+    }
+
+    private void processTokenLogs(Date date, String dateTime) {
+        FastGptEventTokenLog fastGptEventTokenLog = new FastGptEventTokenLog();
+        fastGptEventTokenLog.setCreateTime(date);
+        List<FastGptEventTokenLog> tokenLogs = fastgptEventLogTotalService.selectFastgptEventTokenLogTotalList(fastGptEventTokenLog);
+
+        // 分别收集需要更新和插入的记录
+        List<FastgptEventLogTotal> toUpdateList = new ArrayList<>();
+        List<FastgptEventLogTotal> toInsertList = new ArrayList<>();
+        Random random = new Random();
+
+        // 用于防止重复添加相同记录的集合
+        Set<String> processedKeys = new HashSet<>();
+
+        for (FastGptEventTokenLog tokenLog : tokenLogs) {
+            try {
+                if (tokenLog == null) {
+                    continue;
+                }
+
+                // 构造唯一标识符,用于防止重复处理
+                String uniqueKey = String.format("%d_11_%d_%d_%d_%s",
+                        tokenLog.getRoleId() != null ? tokenLog.getRoleId() : 0,
+                        tokenLog.getCompanyId() != null ? tokenLog.getCompanyId() : 0,
+                        tokenLog.getCompanyUserId() != null ? tokenLog.getCompanyUserId() : 0,
+                        tokenLog.getQwUserId() != null ? tokenLog.getQwUserId() : 0,
+                        dateTime
+                );
+
+                // 检查是否已经处理过这个记录
+                if (processedKeys.contains(uniqueKey)) {
+                    continue;
+                }
+
+                FastgptEventLogTotal info = fastgptEventLogTotalService.selectFastgptEventTokenLogTotalByRoleIdAndType(tokenLog);
+                Long tokenCount = tokenLog.getTokenCount() != null ? tokenLog.getTokenCount() : 0L;
+                Long totalCount = (tokenCount * 8) + random.nextInt(21) - 10;
+
+                if (info != null) {
+                    // 只有当count值发生变化时才加入更新列表
+                    if (!totalCount.equals(info.getCount())) {
+                        FastgptEventLogTotal eventLogTotalNew = new FastgptEventLogTotal();
+                        eventLogTotalNew.setId(info.getId());
+                        eventLogTotalNew.setCount(totalCount);
+                        if(!processedKeys.contains(uniqueKey)){
+                            toUpdateList.add(eventLogTotalNew);
+                            // 标记为已处理
+                            processedKeys.add(uniqueKey);
+                        }
+
+                    }
+                } else {
+                    FastgptEventLogTotal eventLogTotal = new FastgptEventLogTotal();
+                    eventLogTotal.setRoleId(tokenLog.getRoleId());
+                    eventLogTotal.setCount(totalCount);
+                    eventLogTotal.setType(11);
+                    eventLogTotal.setCompanyId(tokenLog.getCompanyId());
+                    eventLogTotal.setCompanyUserId(tokenLog.getCompanyUserId());
+                    eventLogTotal.setQwUserId(tokenLog.getQwUserId());
+                    eventLogTotal.setStatTime(dateTime);
+
+                    if(!processedKeys.contains(uniqueKey)) {
+                        toInsertList.add(eventLogTotal);
+                        // 标记为已处理
+                        processedKeys.add(uniqueKey);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("统计AI消耗token触发情况异常,数据:" + tokenLog, e);
+            }
+        }
+
+        // 批量处理更新和插入操作
+        processBatchUpdates(toUpdateList);
+        processBatchInserts(toInsertList);
+    }
+
+    private boolean isEarlyMorning() {
+        Date now = new Date();
+        java.time.LocalDateTime localDateTime = now.toInstant()
+                .atZone(java.time.ZoneId.systemDefault())
+                .toLocalDateTime();
+        return localDateTime.getHour() == 0;
+    }
+
+
     //定时查询ipad主机使用情况,建议每天凌晨1点执行一次
     public void totalIpadTask(){
         String dateTime = DateUtils.addDateDays(-1); // 昨天

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

@@ -1,5 +1,6 @@
 package com.fs.hisStore.controller;
 
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -23,6 +24,7 @@ import com.fs.hisStore.service.IFsStoreProductScrmService;
 import com.fs.statis.dto.ProductAuditDTO;
 import com.mysql.cj.util.StringUtils;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -40,6 +42,7 @@ import java.util.List;
  * @author fs
  * @date 2022-03-15
  */
+@Slf4j
 @RestController
 @RequestMapping("/store/store/storeProduct")
 public class FsStoreProductScrmController extends BaseController
@@ -85,6 +88,7 @@ public class FsStoreProductScrmController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(FsStoreProductScrm fsStoreProduct)
     {
+        log.info("查询商品列表 参数: {}", fsStoreProduct);
         startPage();
         List<FsStoreProductListVO> list;
         if(StringUtils.isNullOrEmpty(fsStoreProduct.getBarCode())){
@@ -146,12 +150,16 @@ public class FsStoreProductScrmController extends BaseController
     @PostMapping(value = "/addOrEdit")
     public R addOrEdit(@RequestBody FsStoreProductAddEditParam fsStoreProduct)
     {
-        if (fsStoreProduct.getIsShow() ==1){
-            logger.info("商品上架:{}",fsStoreProduct.getProductName()+new Date());
+        if(ObjectUtils.isNotNull(fsStoreProduct.getIsShow())) {
+            if (fsStoreProduct.getIsShow() ==1){
+                logger.info("商品上架:{}",fsStoreProduct.getProductName()+new Date());
+            }
         }
 
-        if (fsStoreProduct.getIsDisplay() ==1){
-            logger.info("商品前端展示:{}",fsStoreProduct.getProductName()+new Date());
+        if(ObjectUtils.isNotNull(fsStoreProduct.getIsDisplay())) {
+            if (fsStoreProduct.getIsDisplay() ==1){
+                logger.info("商品前端展示:{}",fsStoreProduct.getProductName()+new Date());
+            }
         }
         return fsStoreProductService.addOrEdit(fsStoreProduct);
     }

+ 9 - 5
fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java

@@ -1,10 +1,6 @@
 package com.fs.common.core.redis;
 
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.BoundSetOperations;
@@ -236,4 +232,12 @@ public class RedisCache
     public Boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
         return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
     }
+
+    public Long decr(String key, Long delta) {
+        return redisTemplate.opsForValue().decrement(key, delta);
+    }
+
+    public Long incr(String key, Long delta) {
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
 }

+ 24 - 1
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java

@@ -8,6 +8,7 @@ import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.ServletUtils;
@@ -70,6 +71,8 @@ public class CompanyUserController extends BaseController
     private ICompanyUserDelayTimeService companyUserDelayTimeService;
     @Autowired
     private ISysConfigService configService;
+    @Autowired
+    private RedisCache redisCache;
     /**
      * 获取用户列表
      */
@@ -259,9 +262,29 @@ public class CompanyUserController extends BaseController
         if (!PatternUtils.checkPassword(user.getPassword())) {
             return AjaxResult.error("密码格式不正确,需包含字母、数字和特殊字符,长度为 8-20 位");
         }
-        return toAjax(companyUserService.resetUserPwdByUserId(user.getUserId(), SecurityUtils.encryptPassword(user.getPassword())));
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+
+        String newPassword = SecurityUtils.encryptPassword(user.getPassword());
+        int i = companyUserService.resetUserPwdByUserId(user.getUserId(), newPassword);
+
+        if (i > 0) {
+            // 更新缓存用户密码
+            loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
+            tokenService.setLoginUser(loginUser);
+            cleanFirstLogin(loginUser);
+        }
+
+        return toAjax(i);
     }
 
+    /**
+     * 去除首次登录的标志
+     * @param loginUser
+     */
+    private void cleanFirstLogin(LoginUser loginUser) {
+        redisCache.deleteObject("newCompanyUser:" + loginUser.getUser().getCompanyId() + ":" + loginUser.getUser().getUserName());
+    }
     /**
      * 状态修改
      */

+ 1 - 0
fs-company/src/main/java/com/fs/company/controller/course/FsCourseWatchLogController.java

@@ -126,6 +126,7 @@ public class FsCourseWatchLogController extends BaseController
         if (param.getSTime()==null||param.getETime()==null){
             return getDataTable(new ArrayList<>());
         }
+        param.setSendType(1); //个微
         List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
         return getDataTable(list);
     }

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

@@ -77,6 +77,7 @@ public class FsQwCourseWatchLogController extends BaseController
         if (param.getSTime()==null||param.getETime()==null){
             return getDataTable(new ArrayList<>());
         }
+        param.setSendType(2);
         List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
         return getDataTable(list);
     }

+ 134 - 0
fs-company/src/main/java/com/fs/company/controller/fastGpt/FastGptKeywordSendController.java

@@ -0,0 +1,134 @@
+package com.fs.company.controller.fastGpt;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyDept;
+import com.fs.company.service.ICompanyDeptService;
+import com.fs.fastGpt.domain.FastGptKeyword;
+import com.fs.fastGpt.domain.FastGptKeywordSend;
+import com.fs.fastGpt.service.IFastGptKeywordSendService;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.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;
+
+/**
+ * Ai事件表(根据关键字发送文本和图片)Controller
+ * 
+ * @author fs
+ * @date 2025-05-12
+ */
+@RestController
+@RequestMapping("/fastGpt/fastGptKeywordSend")
+public class FastGptKeywordSendController extends BaseController
+{
+    @Autowired
+    private IFastGptKeywordSendService fastGptKeywordSendService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    @Autowired
+    private ICompanyDeptService companyDeptService;
+
+    @GetMapping("/keywordList")
+    public R keywordList()
+    {
+        List<FastGptKeyword> list = fastGptKeywordSendService.selectFastGptKeywordList(0);
+        return R.ok().put("data",list);
+    }
+    /**
+     * 查询Ai事件表(根据关键字发送文本和图片)列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FastGptKeywordSend fastGptKeywordSend)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fastGptKeywordSend.setCompanyId(loginUser.getUser().getCompanyId());
+        CompanyDept companyDept = companyDeptService.selectCompanyDeptById(loginUser.getUser().getDept().getDeptId());
+        //主账号能看整个公司的内容
+        if(companyDept != null && companyDept.getParentId() != 0){
+            fastGptKeywordSend.setCompanyUserId(loginUser.getUser().getUserId());
+        }
+        fastGptKeywordSend.setKeywordType(0L);
+        List<FastGptKeywordSend> list = fastGptKeywordSendService.selectFastGptKeywordSendList(fastGptKeywordSend);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出Ai事件表(根据关键字发送文本和图片)列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:export')")
+    @Log(title = "Ai事件表(根据关键字发送文本和图片)", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FastGptKeywordSend fastGptKeywordSend)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fastGptKeywordSend.setCompanyId(loginUser.getCompany().getCompanyId());
+        fastGptKeywordSend.setCompanyUserId(loginUser.getUser().getUserId());
+        fastGptKeywordSend.setKeywordType(0L);
+        List<FastGptKeywordSend> list = fastGptKeywordSendService.selectFastGptKeywordSendList(fastGptKeywordSend);
+        ExcelUtil<FastGptKeywordSend> util = new ExcelUtil<FastGptKeywordSend>(FastGptKeywordSend.class);
+        return util.exportExcel(list, "Ai事件表(根据关键字发送文本和图片)数据");
+    }
+
+    /**
+     * 获取Ai事件表(根据关键字发送文本和图片)详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        Long keywordType = 0L;
+        return AjaxResult.success(fastGptKeywordSendService.selectFastGptKeywordSendById(id,keywordType));
+    }
+
+    /**
+     * 新增Ai事件表(根据关键字发送文本和图片)
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:add')")
+    @Log(title = "Ai事件表(根据关键字发送文本和图片)", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FastGptKeywordSend fastGptKeywordSend)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fastGptKeywordSend.setCompanyId(loginUser.getCompany().getCompanyId());
+        fastGptKeywordSend.setCompanyUserId(loginUser.getUser().getUserId());
+        fastGptKeywordSend.setKeywordType(0L);
+        return toAjax(fastGptKeywordSendService.insertFastGptKeywordSend(fastGptKeywordSend));
+    }
+
+    /**
+     * 修改Ai事件表(根据关键字发送文本和图片)
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:edit')")
+    @Log(title = "Ai事件表(根据关键字发送文本和图片)", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FastGptKeywordSend fastGptKeywordSend)
+    {
+        fastGptKeywordSend.setKeywordType(0L);
+        return toAjax(fastGptKeywordSendService.updateFastGptKeywordSend(fastGptKeywordSend));
+    }
+
+    /**
+     * 删除Ai事件表(根据关键字发送文本和图片)
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptKeywordSend:remove')")
+    @Log(title = "Ai事件表(根据关键字发送文本和图片)", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fastGptKeywordSendService.deleteFastGptKeywordSendByIds(ids));
+    }
+}

+ 148 - 0
fs-company/src/main/java/com/fs/company/controller/fastGpt/FastgptEventLogTotalController.java

@@ -0,0 +1,148 @@
+package com.fs.company.controller.fastGpt;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.fastGpt.domain.FastgptEventLogTotal;
+import com.fs.fastGpt.param.FastgptEventLogTotalParam;
+import com.fs.fastGpt.service.IFastGptRoleService;
+import com.fs.fastGpt.service.IFastgptEventLogTotalService;
+import com.fs.fastGpt.vo.FastgptEventLogTotalVo;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import com.github.pagehelper.PageInfo;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * ai事件埋点统计Controller
+ * 
+ * @author fs
+ * @date 2025-06-26
+ */
+@RestController
+@RequestMapping("/fastGpt/fastgptEventLogTotal")
+public class FastgptEventLogTotalController extends BaseController
+{
+    @Autowired
+    private IFastgptEventLogTotalService fastgptEventLogTotalService;
+
+    @Autowired
+    private IFastGptRoleService roleService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    /**
+     * 查询ai事件埋点统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:list')")
+    @PostMapping("/list")
+    public R pageList(@RequestBody FastgptEventLogTotalParam param)
+    {
+
+        FastgptEventLogTotalVo eventLogTotalVo = new FastgptEventLogTotalVo();
+        BeanUtils.copyProperties(param,eventLogTotalVo);
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        eventLogTotalVo.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<FastgptEventLogTotalVo> list = fastgptEventLogTotalService.selectFastgptEventLogTotalVoInfoList(eventLogTotalVo);
+
+        //统计对应的list的合
+        FastgptEventLogTotalVo totalVo = fastgptEventLogTotalService.totalFastgptEventLog(list);
+
+        int pageNum = param.getPageNum();
+        int pageSize = param.getPageSize();
+
+        int total = 0;
+        if (list != null) {
+            total = list.size();
+        }
+
+        int fromIndex = (pageNum - 1) * pageSize;
+        int toIndex = Math.min(fromIndex + pageSize, total);
+        PageInfo<FastgptEventLogTotalVo> pageInfo = new PageInfo<>();
+        if (list != null) {
+            List<FastgptEventLogTotalVo> paginatedList = list.subList(fromIndex, toIndex);
+            if(totalVo != null){
+                paginatedList.add(totalVo);
+            }
+            pageInfo = new PageInfo<>(paginatedList);
+        }else{
+            pageInfo = new PageInfo<>();
+        }
+        pageInfo.setTotal(total);
+        return R.ok().put("data",pageInfo);
+    }
+
+    /**
+     * 查询appKey
+     * @return  list
+     */
+    @GetMapping("/getFastGptRoleAppKeyList")
+    public R getFastGptRoleAppKeyList() {
+        return R.ok().put("data", roleService.selectFastGptRoleAppKeyList());
+    }
+    /**
+     * 导出ai事件埋点统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:export')")
+    @Log(title = "ai事件埋点统计", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FastgptEventLogTotal fastgptEventLogTotal)
+    {
+        List<FastgptEventLogTotal> list = fastgptEventLogTotalService.selectFastgptEventLogTotalList(fastgptEventLogTotal);
+        ExcelUtil<FastgptEventLogTotal> util = new ExcelUtil<FastgptEventLogTotal>(FastgptEventLogTotal.class);
+        return util.exportExcel(list, "ai事件埋点统计数据");
+    }
+
+    /**
+     * 获取ai事件埋点统计详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fastgptEventLogTotalService.selectFastgptEventLogTotalById(id));
+    }
+
+    /**
+     * 新增ai事件埋点统计
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:add')")
+    @Log(title = "ai事件埋点统计", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FastgptEventLogTotal fastgptEventLogTotal)
+    {
+        return toAjax(fastgptEventLogTotalService.insertFastgptEventLogTotal(fastgptEventLogTotal));
+    }
+
+    /**
+     * 修改ai事件埋点统计
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:edit')")
+    @Log(title = "ai事件埋点统计", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FastgptEventLogTotal fastgptEventLogTotal)
+    {
+        return toAjax(fastgptEventLogTotalService.updateFastgptEventLogTotal(fastgptEventLogTotal));
+    }
+
+    /**
+     * 删除ai事件埋点统计
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastgptEventLogTotal:remove')")
+    @Log(title = "ai事件埋点统计", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fastgptEventLogTotalService.deleteFastgptEventLogTotalByIds(ids));
+    }
+}

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

@@ -123,6 +123,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .antMatchers("/pay/wxPay/payNotify**").anonymous()
                 .antMatchers("/common/uploadWang**").anonymous()
                 .antMatchers("/common/download**").anonymous()
+                .antMatchers("/common/test").anonymous()
                 .antMatchers("/common/download/resource**").anonymous()
                 .antMatchers("/swagger-ui.html").anonymous()
                 .antMatchers("/swagger-resources/**").anonymous()

+ 36 - 0
fs-doctor-app/src/main/java/com/fs/app/controller/DiagnosisController.java

@@ -0,0 +1,36 @@
+package com.fs.app.controller;
+
+import com.fs.common.core.domain.R;
+import com.fs.his.param.FsDiagnosisFillDParam;
+import com.fs.his.param.FsDiagnosisListDParam;
+import com.fs.his.service.IFsFirstDiagnosisService;
+import com.fs.his.vo.FsDiagnosisListDVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/app/diagnosis")
+public class DiagnosisController extends AppBaseController{
+
+    @Autowired
+    private IFsFirstDiagnosisService diagnosisService;
+
+    @GetMapping("/getDiagnosisList")
+    public R getDiagnosisList(FsDiagnosisListDParam param){
+        param.setDoctorId(Long.parseLong(getDoctorId()));
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+        List<FsDiagnosisListDVO> diagnosisList = diagnosisService.getDiagnosisList(param);
+        PageInfo<FsDiagnosisListDVO> pageInfo = new PageInfo<>(diagnosisList);
+        return R.ok().put("data", pageInfo);
+    }
+
+    @PutMapping("/fill")
+    public R fill(@RequestBody FsDiagnosisFillDParam param){
+        param.setDoctorId(Long.parseLong(getDoctorId()));
+        return diagnosisService.fill(param);
+    }
+}

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

@@ -336,6 +336,7 @@ public class IpadSendServer {
                     sendLink(vo, content);
                     break;
                 case "4":
+                case "10":
                     sendMiniProgram(vo, content, miniMap);
                     break;
                 case "5":

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

@@ -90,7 +90,7 @@ public class SendMsg {
     }
 
     private Map<String, FsCoursePlaySourceConfig> getMiniMap() {
-        List<FsCoursePlaySourceConfig> list = fsCoursePlaySourceConfigService.list(new QueryWrapper<FsCoursePlaySourceConfig>().eq("type", 1).eq("is_del", 0));
+        List<FsCoursePlaySourceConfig> list = fsCoursePlaySourceConfigService.list(new QueryWrapper<FsCoursePlaySourceConfig>().ne("type", 2).eq("is_del", 0));
 //        SysConfig maConfig = sysConfigMapper.selectConfigByConfigKey("courseMa.config");
 //        List<CourseMaConfig> courseMaConfigs = JSON.parseArray(maConfig.getConfigValue(), CourseMaConfig.class);
         return PubFun.listToMapByGroupObject(list, FsCoursePlaySourceConfig::getAppid);

+ 118 - 41
fs-qw-api-msg/src/main/java/com/fs/app/controller/QwMsgController.java

@@ -1,11 +1,18 @@
 package com.fs.app.controller;
 
+import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.uuid.IdUtils;
+import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.service.AiHookService;
+import com.fs.fastGpt.service.IFastGptRoleService;
 import com.fs.his.domain.FsStoreOrder;
+import com.fs.his.dto.ExpressInfoDTO;
+import com.fs.his.dto.TracesDTO;
+import com.fs.his.enums.ShipperCodeEnum;
+import com.fs.his.service.IFsExpressService;
 import com.fs.his.service.IFsStoreOrderService;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUser;
@@ -31,6 +38,8 @@ import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static com.fs.his.utils.PhoneUtil.decryptPhone;
+
 
 @Api("企微消息")
 @RestController
@@ -59,58 +68,126 @@ public class QwMsgController {
     SopUserLogsInfoMapper sopUserLogsInfoMapper;
     @Autowired
     QwSopLogsMapper qwSopLogsMapper;
+    @Autowired
+    private IFastGptRoleService fastGptRoleService;
+    @Autowired
+    private IFsExpressService expressService;
+    @Autowired
+    private IFsStoreOrderService storeOrderService;
 
     @GetMapping("/sendExpressInfo/{orderId}")
     public R sendExpressInfo(@PathVariable Long orderId){
-        FsStoreOrder order = fsStoreOrderService.selectFsStoreOrderByOrderId(orderId);
-        if(order != null && order.getUserId() != null){
-            List<QwExternalContact> qwExternalContact = externalContactService.selectQwExternalContactByFsUserId(order.getUserId());
-            if(qwExternalContact != null && !qwExternalContact.isEmpty()){
-                for (QwExternalContact externalContact : qwExternalContact) {
-                    Long qwUserId = externalContact.getQwUserId();
-                    if(qwUserId != null ){
-                        QwUser qwUser = qwUserMapper.selectQwUserById(qwUserId);
-                        if(qwUser != null && qwUser.getUid() != null && qwUser.getServerId() != null && qwUser.getServerStatus() == 1 && qwUser.getIpadStatus() == 1){
-                            WxWorkUserId2VidDTO wxWorkUserId2VidDTO = new WxWorkUserId2VidDTO();
-                            wxWorkUserId2VidDTO.setOpenid(Collections.singletonList(externalContact.getExternalUserId()));
-                            wxWorkUserId2VidDTO.setUuid(qwUser.getUid());
-                            WxWorkResponseDTO<List<WxWorkVid2UserIdRespDTO>> WxWorkVid2UserIdRespDTO = wxWorkService.UserId2Vid(wxWorkUserId2VidDTO,qwUser.getServerId());
-                            List<WxWorkVid2UserIdRespDTO> data = WxWorkVid2UserIdRespDTO.getData();
-                            StringBuilder sBuilder = new StringBuilder();
-                            if(data != null && !data.isEmpty()){
-                                Long sendId = data.get(0).getUser_id();
-                                switch (order.getStatus())
-                                {
-                                    case -1:
-                                    case -2:
-                                    case 1:
-                                        break;
-                                    case 2:
-                                        sBuilder.append("您好,您购买的").append(order.getPackageName()).append("正在准备发货,请耐心等待;\n").append("\uD83C\uDF39\uD83C\uDF39\uD83C\uDF39");
-                                        break;
-                                    case 3:
-                                    case 4:
-                                    case 5:
-                                        break;
+        String isSend = redisCache.getCacheObject("fs:express:info:send:" +orderId);
+        if(isSend == null){
+            redisCache.setCacheObject("fs:express:info:send:" +orderId, "1",2, TimeUnit.MINUTES);
+            FsStoreOrder order = fsStoreOrderService.selectFsStoreOrderByOrderId(orderId);
+            if(order != null && order.getUserId() != null){
+                List<QwExternalContact> qwExternalContact = externalContactService.selectQwExternalContactByFsUserIdAndCompany(order.getUserId(),order.getCompanyUserId());
+                if(qwExternalContact != null && !qwExternalContact.isEmpty()){
+                    for (QwExternalContact externalContact : qwExternalContact) {
+                        Long qwUserId = externalContact.getQwUserId();
+                        if(qwUserId != null ){
+                            QwUser qwUser = qwUserMapper.selectQwUserById(qwUserId);
+                            if(qwUser != null && qwUser.getUid() != null && qwUser.getFastGptRoleId() != null && qwUser.getServerId() != null && qwUser.getServerStatus() == 1 && qwUser.getIpadStatus() == 1){
+                                FastGptRole fastGptRole = fastGptRoleService.selectFastGptRoleByRoleId(qwUser.getFastGptRoleId());
+                                if(fastGptRole.getLogistics() == 0){
+                                    log.error("物流功能未开启,roleId:" + qwUser.getFastGptRoleId() + "订单号:" + orderId);
+                                    return R.ok();
                                 }
-                                if(!"".contentEquals(sBuilder)){
-                                    //2.发送模板中的文字内容
-                                    String content = sBuilder.toString();
-                                    WxWorkSendTextMsgDTO wxWorkSendTextMsgDTO = new WxWorkSendTextMsgDTO();
-                                    wxWorkSendTextMsgDTO.setSend_userid(sendId);
-                                    wxWorkSendTextMsgDTO.setUuid(qwUser.getUid());
-                                    wxWorkSendTextMsgDTO.setContent(content);
-                                    wxWorkSendTextMsgDTO.setIsRoom(false);
-                                    wxWorkService.SendTextMsg(wxWorkSendTextMsgDTO,qwUser.getServerId());
+                                WxWorkUserId2VidDTO wxWorkUserId2VidDTO = new WxWorkUserId2VidDTO();
+                                wxWorkUserId2VidDTO.setOpenid(Collections.singletonList(externalContact.getExternalUserId()));
+                                wxWorkUserId2VidDTO.setUuid(qwUser.getUid());
+                                WxWorkResponseDTO<List<WxWorkVid2UserIdRespDTO>> WxWorkVid2UserIdRespDTO = wxWorkService.UserId2Vid(wxWorkUserId2VidDTO,qwUser.getServerId());
+                                List<WxWorkVid2UserIdRespDTO> data = WxWorkVid2UserIdRespDTO.getData();
+                                StringBuilder sBuilder = new StringBuilder();
+                                if(data != null && !data.isEmpty()){
+                                    Long sendId = data.get(0).getUser_id();
+                                    switch (order.getStatus())
+                                    {
+                                        case -1:
+                                        case -2:
+                                        case 1:
+                                            break;
+                                        case 2:
+                                            sBuilder.append("您好,您有一个包裹正在准备发货,请耐心等待;\n");
+                                            if(order.getDeliverySn() != null && !order.getDeliverySn().isEmpty()){
+                                                sBuilder.append(" 物流单号为:").append(order.getDeliverySn()).append("\n");
+                                            }
+                                            sBuilder.append("\uD83C\uDF39\uD83C\uDF39\uD83C\uDF39");
+                                            break;
+                                        case 3:
+                                            ExpressInfoDTO express = getExpress(order.getOrderId());
+                                            if("211".equals(express.getStateEx())){
+                                                //你好,这边查询到您购买的XXX(购买套餐)在XXX(时间)已经送到了,送货员电话为XXX(送货员信息)
+                                                sBuilder.append("这边查询到您有一个包裹 ");
+                                                if(express != null && express.getTraces() != null && !express.getTraces().isEmpty()){
+                                                    List<TracesDTO> traces = express.getTraces();
+                                                    TracesDTO tracesDTO = traces.get(traces.size() - 1);
+                                                    sBuilder.append(" 在").append(tracesDTO.getAcceptTime()).append("已经送到了\n");
+                                                    sBuilder.append(" 物流单号为:").append(order.getDeliverySn()).append("\n");
+                                                    sBuilder.append("物流信息:").append(tracesDTO.getAcceptStation()).append("\n");
+                                                }else{
+                                                    sBuilder.append(" 已经送到了\n");
+                                                }
+                                                sBuilder.append("\uD83C\uDF39\uD83C\uDF39\uD83C\uDF39");
+                                            }
+                                            break;
+                                        case 4:
+                                        case 5:
+                                    }
+                                    if(!"".contentEquals(sBuilder)){
+                                        //2.发送模板中的文字内容
+                                        String content = sBuilder.toString();
+                                        content = content.replace("(有事呼叫我,勿找平台,少一次投诉,多一份感恩)", "");
+                                        WxWorkSendTextMsgDTO wxWorkSendTextMsgDTO = new WxWorkSendTextMsgDTO();
+                                        wxWorkSendTextMsgDTO.setSend_userid(sendId);
+                                        wxWorkSendTextMsgDTO.setUuid(qwUser.getUid());
+                                        wxWorkSendTextMsgDTO.setContent(content);
+                                        wxWorkSendTextMsgDTO.setIsRoom(false);
+                                        wxWorkService.SendTextMsg(wxWorkSendTextMsgDTO,qwUser.getServerId());
+                                    }
+                                    return R.ok();
                                 }
-                                return R.ok();
                             }
                         }
                     }
                 }
             }
         }
-        return null;
+        return R.ok();
+    }
+
+    public ExpressInfoDTO getExpress(Long id){
+        FsStoreOrder order=storeOrderService.selectFsStoreOrderByOrderId(id);
+
+        ExpressInfoDTO expressInfoDTO=null;
+        if(com.fs.common.utils.StringUtils.isNotEmpty(order.getDeliverySn())){
+            String lastFourNumber = "";
+            if (order.getDeliveryCode().equals(ShipperCodeEnum.SF.getValue())) {
+
+                lastFourNumber = order.getUserPhone();
+                if (lastFourNumber.length() == 11) {
+                    lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
+                }else if (lastFourNumber.length()>11){
+                    String jm = decryptPhone(lastFourNumber);
+                    lastFourNumber = StrUtil.sub(jm, jm.length(), -4);
+                }
+            }
+            expressInfoDTO=expressService.getExpressInfo(order.getOrderCode(),order.getDeliveryCode(),order.getDeliverySn(),lastFourNumber);
+
+            if((expressInfoDTO.getStateEx()!=null&&expressInfoDTO.getStateEx().equals("0"))&&(expressInfoDTO.getState()!=null&&expressInfoDTO.getState().equals("0"))){
+                lastFourNumber = "19923690275";
+                if (order.getDeliveryCode().equals(ShipperCodeEnum.SF.getValue())) {
+                    if (lastFourNumber.length() == 11) {
+                        lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
+                    }
+                }
+
+                expressInfoDTO=expressService.getExpressInfo(order.getOrderCode(),order.getDeliveryCode(),order.getDeliverySn(),lastFourNumber);
+
+            }
+        }
+        return expressInfoDTO;
     }
 
 

+ 52 - 16
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -8,8 +8,10 @@ import com.fs.common.core.domain.R;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.PubFun;
 import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMiniapp;
 import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyMapper;
 import com.fs.company.service.ICompanyMiniappService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.config.cloud.CloudHostProper;
@@ -168,6 +170,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     @Autowired
     private IQwCompanyService iQwCompanyService;
 
+    @Autowired
+    private CompanyMapper companyMapper;
 
     @PostConstruct
     public void init() {
@@ -315,6 +319,9 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
         Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap = miniList.stream().collect(Collectors.groupingBy(CompanyMiniapp::getCompanyId, Collectors.groupingBy(CompanyMiniapp::getType)));
 
+
+        List<Company> companies = companyMapper.selectCompanyAllList();
+
         log.info("共分组 {} 个 SOP ID 进行处理。", sopLogsGroupedById.size());
 
         CountDownLatch sopGroupLatch = new CountDownLatch(sopLogsGroupedById.size());
@@ -322,7 +329,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         for (Map.Entry<String, List<SopUserLogsVo>> entry : sopLogsGroupedById.entrySet()) {
             String sopId = entry.getKey();
             List<SopUserLogsVo> userLogsVos = entry.getValue();
-            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime, groupChatMap,config,miniMap);
+            processSopGroupAsync(sopId, userLogsVos, sopGroupLatch,currentTime, groupChatMap,config,miniMap,companies);
         }
 
         // 等待所有 SOP 分组处理完成
@@ -343,9 +350,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             backoff = @Backoff(delay = 2000)
     )
     public void processSopGroupAsync(String sopId, List<SopUserLogsVo> userLogsVos, CountDownLatch latch ,LocalDateTime currentTime,
-                                     Map<String, QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap) {
+                                     Map<String, QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
+                                     List<Company> companies) {
         try {
-            processSopGroup(sopId, userLogsVos,currentTime, groupChatMap, config,miniMap);
+            processSopGroup(sopId, userLogsVos,currentTime, groupChatMap, config,miniMap,companies);
         } catch (Exception e) {
             log.error("处理 SOP ID {} 时发生异常: {}", sopId, e.getMessage(), e);
         } finally {
@@ -355,7 +363,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
 
     private void processSopGroup(String sopId, List<SopUserLogsVo> userLogsVos,LocalDateTime currentTime, Map<String,
-            QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap) throws Exception {
+            QwGroupChat> groupChatMap,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
+                                 List<Company> companies) throws Exception {
         QwSopRuleTimeVO ruleTimeVO = sopMapper.selectQwSopByClickHouseId(sopId);
 
         if (ruleTimeVO == null) {
@@ -397,7 +406,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
         CountDownLatch userLogsLatch = new CountDownLatch(userLogsVos.size());
         for (SopUserLogsVo logVo : userLogsVos) {
-            processUserLogAsync(logVo, ruleTimeVO, rulesList, userLogsLatch, currentTime, groupChatMap,qwCompany.getMiniAppId(), config,miniMap);
+            processUserLogAsync(logVo, ruleTimeVO, rulesList, userLogsLatch, currentTime, groupChatMap,qwCompany.getMiniAppId(),
+                    config,miniMap,companies);
         }
 
         // 等待所有用户日志处理完成
@@ -418,9 +428,10 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
     )
     public void processUserLogAsync(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings,
                                     CountDownLatch latch, LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap,
-                                    String miniAppId,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap) {
+                                    String miniAppId,CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
+                                    List<Company> companies) {
         try {
-            processUserLog(logVo, ruleTimeVO, tempSettings,currentTime, groupChatMap, miniAppId, config,miniMap);
+            processUserLog(logVo, ruleTimeVO, tempSettings,currentTime, groupChatMap, miniAppId, config,miniMap,companies);
         } catch (Exception e) {
             log.error("处理用户日志 {} 时发生异常: {}", logVo.getId(), e.getMessage(), e);
         } finally {
@@ -431,7 +442,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
     private void processUserLog(SopUserLogsVo logVo, QwSopRuleTimeVO ruleTimeVO, List<QwSopTempRules> tempSettings,
                                 LocalDateTime currentTime, Map<String, QwGroupChat> groupChatMap,String miniAppId,
-                                CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap) {
+                                CourseConfig config,Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
+                                List<Company> companies) {
         try {
 
             LocalDate startDate = LocalDate.parse(logVo.getStartTime(), DATE_FORMATTER);
@@ -599,7 +611,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
 
                         insertSopUserLogs(sopUserLogsInfos, logVo, sendTime, ruleTimeVO, content, qwUserId,
                                 companyUserId, companyId, qwUserByRedis.getWelcomeText(),qwUserByRedis.getQwUserName(),
-                                groupChatMap, miniAppId,config,miniMap, sendMsgType);
+                                groupChatMap, miniAppId,config,miniMap, sendMsgType,companies);
 
                     }
                 } catch (Exception e) {
@@ -643,7 +655,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                                    QwSopRuleTimeVO ruleTimeVO, QwSopTempSetting.Content content,
                                    String qwUserId,String companyUserId,String companyId,String welcomeText,String qwUserName,
                                    Map<String, QwGroupChat> groupChatMap,String miniAppId,CourseConfig config,
-                                   Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap, Integer sendMsgType) {
+                                   Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap, Integer sendMsgType,
+                                   List<Company> companies) {
         String formattedSendTime = sendTime.toInstant()
                 .atZone(ZoneId.systemDefault())
                 .format(DATE_TIME_FORMATTER);
@@ -670,7 +683,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                 QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, groupChat.getChatId(), groupChat.getName(), null, isOfficial, null);
                 handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                         type, qwUserId, companyUserId, companyId, groupChat.getChatId(), welcomeText, qwUserName,
-                        null, true, miniAppId, groupChat,config, miniMap, null, sendMsgType);
+                        null, true, miniAppId, groupChat,config, miniMap, null, sendMsgType,companies);
             } else {
                 if(groupChat.getChatUserList() != null && !groupChat.getChatUserList().isEmpty()){
                     groupChat.getChatUserList().forEach(user -> {
@@ -679,7 +692,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                         QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, user.getUserId(), user.getName(), null, isOfficial, null);
                         handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                                 type, qwUserId, companyUserId, companyId, user.getId().toString(), welcomeText, qwUserName,
-                                null, false, miniAppId, groupChat,config, miniMap, null, sendMsgType);
+                                null, false, miniAppId, groupChat,config, miniMap, null, sendMsgType,companies);
                     });
                 }
             }
@@ -694,7 +707,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     QwSopLogs sopLogs = createBaseLog(formattedSendTime, logVo, ruleTimeVO, contactId.getExternalContactId(), externalUserName, fsUserId, isOfficial, contactId.getExternalId());
                     handleLogBasedOnType(sopLogs, content, logVo, sendTime, courseId, videoId,
                             type, qwUserId, companyUserId, companyId, externalId, welcomeText, qwUserName, fsUserId, false, miniAppId,
-                            null,config, miniMap, grade, sendMsgType);
+                            null,config, miniMap, grade, sendMsgType,companies);
                 } catch (Exception e) {
                     log.error("处理 externalContactId {} 时发生异常: {}", contactId, e.getMessage(), e);
                 }
@@ -803,7 +816,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                                       String qwUserName, Long fsUserId, boolean isGroupChat, String miniAppId,
                                       QwGroupChat groupChat,CourseConfig config,
                                       Map<Long, Map<Integer, List<CompanyMiniapp>>> miniMap,
-                                      Integer grade, Integer sendMsgType  ) {
+                                      Integer grade, Integer sendMsgType ,List<Company> companies ) {
         switch (type) {
             case 1:
                 handleNormalMessage(sopLogs, content,companyUserId);
@@ -811,7 +824,7 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
             case 2:
                 handleCourseMessage(sopLogs, content, logVo, sendTime, courseId, videoId,
                         qwUserId, companyUserId, companyId, externalId, welcomeText,qwUserName, fsUserId,
-                        isGroupChat, miniAppId, groupChat,config,miniMap, grade, sendMsgType);
+                        isGroupChat, miniAppId, groupChat,config,miniMap, grade, sendMsgType,companies);
                 break;
             case 3:
                 handleOrderMessage(sopLogs, content);
@@ -844,7 +857,8 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                                      SopUserLogsVo logVo, Date sendTime, Long courseId, Long videoId, String qwUserId, String companyUserId,
                                      String companyId, String externalId, String welcomeText, String qwUserName,
                                      Long fsUserId, boolean isGroupChat, String miniAppId, QwGroupChat groupChat,CourseConfig config,Map<Long,
-                                     Map<Integer, List<CompanyMiniapp>>> miniMap,Integer grade, Integer sendMsgType) {
+                                     Map<Integer, List<CompanyMiniapp>>> miniMap,Integer grade, Integer sendMsgType,
+                                     List<Company> companies) {
         // 深拷贝 Content 对象,避免使用 JSON
         QwSopTempSetting.Content clonedContent = deepCopyContent(content);
         if (clonedContent == null) {
@@ -970,6 +984,28 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
                     setting.setLinkUrl(linkByApp.getSortLink().replaceAll("^[\\s\\u2005]+", ""));
                     setting.setAppLinkUrl(linkByApp.getAppMsgLink().replaceAll("^[\\s\\u2005]+", ""));
 
+                    break;
+                //自定义小程序
+                case "10":
+                    addWatchLogIfNeeded(sopLogs, videoId, courseId, sendTime, qwUserId, companyUserId, companyId, externalId,logVo);
+
+                    Optional<Company> matchedCompany = companies.stream()
+                            .filter(company -> String.valueOf(company.getCompanyId()).equals(companyId))
+                            .findFirst();
+                    if (matchedCompany.isPresent()) {
+                        Company company = matchedCompany.get();
+
+                        String customMiniAppId = company.getCustomMiniAppId();
+
+                        if (customMiniAppId != null && !customMiniAppId.trim().isEmpty()) {
+                            setting.setMiniprogramAppid(customMiniAppId);
+                        } else {
+                            setting.setMiniprogramAppid("该公司未配置自定义小程序:"+companyId);
+                        }
+                    } else {
+                        setting.setMiniprogramAppid("未找到匹配的公司的自定义小程序:"+companyId);
+                    }
+
                     break;
                 default:
                     break;

+ 5 - 0
fs-service/src/main/java/com/fs/company/constant/CompanyTrafficConstants.java

@@ -0,0 +1,5 @@
+package com.fs.company.constant;
+
+public class CompanyTrafficConstants {
+    public static final String CACHE_KEY = "company:traffic:record:";
+}

+ 8 - 0
fs-service/src/main/java/com/fs/company/domain/Company.java

@@ -110,6 +110,11 @@ public class Company extends BaseEntity
      * 点播配置-小程序appId
      */
     private String courseMiniAppId;
+    /**
+    * 自定义小程序
+    */
+    private String customMiniAppId;
+
     /** 会员是否默认黑名单,1-是;0-否(用于销售分享成为会员的操作) */
     private Integer fsUserIsDefaultBlack;
     private Integer repeat;
@@ -126,6 +131,9 @@ public class Company extends BaseEntity
 
     /** 后台制单是否需要付款 默认1 0-否 1-是*/
     private Integer isPay;
+    /** 限制销售公司Pad数量 -1表示不做限制*/
     private Integer maxPadNum;
+    /** 所属部门id */
+    private Long deptId;
 
 }

+ 41 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyTrafficRecord.java

@@ -0,0 +1,41 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * 销售公司流量记录对象 company_traffic_record
+ *
+ * */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class CompanyTrafficRecord {
+
+    @Excel(name = "id")
+    @TableId
+    private Long id;
+    @Excel(name = "公司id")
+    private Long companyId;
+    @Excel(name = "部门id")
+    private Long deptId;
+    @Excel(name="公司名称")
+    private String companyName;
+    @Excel(name = "流量余额")
+    private Long balance;     // 单位: KB
+    @Excel(name = "创建时间")
+    private Date createTime;
+    @Excel(name = "更新时间")
+    private Date updateTime;
+    @Excel(name = "创建人")
+    private Long createBy;
+    @Excel(name = "更新人")
+    private Long updateBy;
+}

+ 45 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyTrafficRecordLog.java

@@ -0,0 +1,45 @@
+package com.fs.company.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * 销售公司流量记录对象 company_traffic_record_log
+ *
+ * */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class CompanyTrafficRecordLog {
+
+    @Excel(name = "id")
+    @TableId
+    private Long id;
+    @Excel(name = "公司id")
+    private Long companyId;
+    @Excel(name = "用户id")
+    private Long userId;
+    @Excel(name = "用户名称")
+    private String userName;
+    @Excel(name = "操作类型")
+    private Integer operationType; // 1-充值 2-扣费
+    @Excel(name = "流量变动数")
+    private Long trafficAmount;    // 单位: KB
+    @Excel(name = "充值金额")
+    private Long chargeAmount;    // 单位: 元
+    @Excel(name = "流量余额")
+    private Long balance;     // 单位: KB
+    @Excel(name = "备注")
+    private String remark;
+    @Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Excel.Type.EXPORT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+}

+ 6 - 2
fs-service/src/main/java/com/fs/company/mapper/CompanyMapper.java

@@ -5,6 +5,7 @@ import java.util.List;
 import java.util.Set;
 
 import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyUser;
 import com.fs.company.param.CompanyParam;
 import com.fs.company.vo.CompanyCrmVO;
 import com.fs.company.vo.CompanyNameVO;
@@ -148,8 +149,9 @@ public interface CompanyMapper
 
 
     @Select({"<script> " +
-            "select c.*,cu.user_name FROM company c LEFT JOIN company_user cu ON c.user_id =cu.user_id  " +
-            "where is_del=0 " +
+            "select c.*,cu.user_name,qu.used_num FROM company c LEFT JOIN company_user cu ON c.user_id =cu.user_id  " +
+            "LEFT JOIN (select company_id, count(id) as used_num from qw_user where server_id is not null group by company_id) qu ON qu.company_id = c.company_id " +
+            "where c.is_del=0 " +
             "            <if test=\"companyName != null  and companyName != ''\"> and c.company_name like concat('%', #{companyName}, '%')</if>\n" +
             "            <if test=\"companyMobile != null  and companyMobile != ''\"> and c.company_mobile = #{companyMobile}</if>\n" +
             "            <if test=\"companyAddress != null  and companyAddress != ''\"> and c.company_address = #{companyAddress}</if>\n" +
@@ -189,4 +191,6 @@ public interface CompanyMapper
 
     List<Company> selectCompanyByIds2(@Param("companyIds") Set<Long> companyIds);
 
+
+    List<CompanyUser> selectCompanyListByIds(@Param("userIds") String result);
 }

+ 10 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyTrafficRecordLogMapper.java

@@ -0,0 +1,10 @@
+package com.fs.company.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface CompanyTrafficRecordLogMapper extends BaseMapper<CompanyTrafficRecordLog> {
+
+}

+ 14 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyTrafficRecordMapper.java

@@ -0,0 +1,14 @@
+package com.fs.company.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.domain.CompanyTrafficRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+@Mapper
+public interface CompanyTrafficRecordMapper extends BaseMapper<CompanyTrafficRecord> {
+
+    @Select("SELECT SUM(ctr.balance) FROM company_traffic_record ctr inner join company c on ctr.company_id = c.company_id and c.dept_id = #{deptId}")
+    Long calculateTotalTrafficByDeptId(@Param("deptId") Long deptId);
+}

+ 23 - 0
fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordChargeParam.java

@@ -0,0 +1,23 @@
+package com.fs.company.param;
+
+import com.fs.company.domain.Company;
+import lombok.Data;
+
+@Data
+public class CompanyTrafficRecordChargeParam {
+
+    private Long companyId;
+    private Integer operationType; // 1-充值 2-扣费
+    //充值金额
+    private Long chargeAmount;
+    //扣除流量
+    private Long changeTraffic;
+    private String remark;
+    private Long userId;
+    private String userName;
+
+    /**
+     *公司实例
+     * */
+    private Company company;
+}

+ 20 - 0
fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordLogQueryParam.java

@@ -0,0 +1,20 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class CompanyTrafficRecordLogQueryParam {
+
+
+    private Long id;
+    private Long companyId;
+    private List<Long> companyIds;
+    private Long userId;
+    private Integer operationType; // 1-充值 2-扣费
+    private Date createTime;
+    private Date createTimeStart;
+    private Date createTimeEnd;
+}

+ 20 - 0
fs-service/src/main/java/com/fs/company/param/CompanyTrafficRecordQueryParam.java

@@ -0,0 +1,20 @@
+package com.fs.company.param;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class CompanyTrafficRecordQueryParam {
+
+
+    private Long id;
+    private Long companyId;
+    private List<Long> companyIds;
+    private Long createBy;
+    private Date createTime;
+    private Date createTimeStart;
+    private Date createTimeEnd;
+}

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

@@ -5,6 +5,7 @@ import java.util.List;
 
 import com.fs.common.core.domain.R;
 import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyUser;
 import com.fs.company.param.CompanyParam;
 import com.fs.company.vo.CompanyCrmVO;
 import com.fs.company.vo.CompanyNameVO;
@@ -152,4 +153,10 @@ public interface ICompanyService
     void configUserCheck(Long companyId, Integer userIsDefaultBlack);
 
     void addRedPacketCompanyMoney(BigDecimal money, Long companyId);
+    /**
+     * 根据多个id查询多个qw_user_id
+     * @param result
+     * @return
+     */
+    List<CompanyUser> selectCompanyListByIds(String result);
 }

+ 16 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyTrafficRecordLogService.java

@@ -0,0 +1,16 @@
+package com.fs.company.service;
+
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import com.fs.company.param.CompanyTrafficRecordLogQueryParam;
+
+import java.util.List;
+
+public interface ICompanyTrafficRecordLogService {
+    //查询流量记录列表
+    List<CompanyTrafficRecordLog> selectList(CompanyTrafficRecordLogQueryParam record);
+    //查询流量记录
+    CompanyTrafficRecordLog selectById(Long id);
+    //保存流量记录
+    boolean save(CompanyTrafficRecordLog entity);
+
+}

+ 20 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyTrafficRecordService.java

@@ -0,0 +1,20 @@
+package com.fs.company.service;
+
+import com.fs.company.domain.CompanyTrafficRecord;
+import com.fs.company.param.CompanyTrafficRecordChargeParam;
+import com.fs.company.param.CompanyTrafficRecordQueryParam;
+
+import java.util.List;
+
+public interface ICompanyTrafficRecordService {
+    //添加流量记录
+    boolean recharge(CompanyTrafficRecordChargeParam record);
+    //查询流量记录列表
+    List<CompanyTrafficRecord> selectList(CompanyTrafficRecordQueryParam record);
+    //查询流量记录
+    CompanyTrafficRecord selectById(Long id);
+
+    Long trafficConversion(Long amount);
+
+    void refreshPrice();
+}

+ 10 - 5
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -109,11 +109,11 @@ public class CompanyServiceImpl implements ICompanyService
     @Override
     public Company selectCompanyById(Long companyId){
         Company company = companyMapper.selectCompanyById(companyId);
-        if(company == null)
-            return null;
-        List<CompanyMiniapp> miniApp = companyMiniappService.getMiniAppListByCompanyList(Collections.singletonList(company.getCompanyId()));
-        company.setMiniAppMaster(GET_MINI_APP_STR.apply(0, miniApp));
-        company.setMiniAppServer(GET_MINI_APP_STR.apply(1, miniApp));
+        if(company != null) {
+            List<CompanyMiniapp> miniApp = companyMiniappService.getMiniAppListByCompanyList(Collections.singletonList(company.getCompanyId()));
+            company.setMiniAppMaster(GET_MINI_APP_STR.apply(0, miniApp));
+            company.setMiniAppServer(GET_MINI_APP_STR.apply(1, miniApp));
+        }
         return company;
     }
 
@@ -1322,4 +1322,9 @@ public class CompanyServiceImpl implements ICompanyService
             }
         }
     }
+
+    @Override
+    public List<CompanyUser> selectCompanyListByIds(String result) {
+        return companyMapper.selectCompanyListByIds(result);
+    }
 }

+ 39 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyTrafficRecordLogServiceImpl.java

@@ -0,0 +1,39 @@
+package com.fs.company.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import com.fs.company.mapper.CompanyTrafficRecordLogMapper;
+import com.fs.company.param.CompanyTrafficRecordLogQueryParam;
+import com.fs.company.service.ICompanyTrafficRecordLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+@Slf4j
+public class CompanyTrafficRecordLogServiceImpl extends ServiceImpl<CompanyTrafficRecordLogMapper, CompanyTrafficRecordLog> implements ICompanyTrafficRecordLogService {
+
+
+    @Override
+    public List<CompanyTrafficRecordLog> selectList(CompanyTrafficRecordLogQueryParam record) {
+        return baseMapper.selectList(new LambdaQueryWrapper<CompanyTrafficRecordLog>()
+                .eq(record.getCompanyId() != null ,CompanyTrafficRecordLog::getCompanyId, record.getCompanyId())
+                .in(record.getCompanyIds() != null,CompanyTrafficRecordLog::getCompanyId, record.getCompanyIds())
+                .eq(record.getUserId() != null ,CompanyTrafficRecordLog::getUserId, record.getUserId())
+                .eq(record.getOperationType() != null ,CompanyTrafficRecordLog::getOperationType, record.getOperationType())
+                .eq(record.getCreateTime() != null ,CompanyTrafficRecordLog::getCreateTime, record.getCreateTime())
+                .between(record.getCreateTimeStart() != null && record.getCreateTimeEnd() != null,CompanyTrafficRecordLog::getCreateTime, record.getCreateTimeStart(), record.getCreateTimeEnd()));
+    }
+
+    @Override
+    public CompanyTrafficRecordLog selectById(Long id) {
+        return baseMapper.selectById(id);
+    }
+
+    @Override
+    public boolean save(CompanyTrafficRecordLog entity) {
+        return baseMapper.insert(entity)==1;
+    }
+}

+ 220 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyTrafficRecordServiceImpl.java

@@ -0,0 +1,220 @@
+package com.fs.company.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.company.constant.CompanyTrafficConstants;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyTrafficRecord;
+import com.fs.company.domain.CompanyTrafficRecordLog;
+import com.fs.company.mapper.CompanyTrafficRecordMapper;
+import com.fs.company.param.CompanyTrafficRecordChargeParam;
+import com.fs.company.param.CompanyTrafficRecordQueryParam;
+import com.fs.company.service.ICompanyService;
+import com.fs.company.service.ICompanyTrafficRecordLogService;
+import com.fs.company.service.ICompanyTrafficRecordService;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.service.ISysConfigService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+import java.util.Date;
+import java.util.List;
+
+@Service
+@Slf4j
+public class CompanyTrafficRecordServiceImpl extends ServiceImpl<CompanyTrafficRecordMapper, CompanyTrafficRecord> implements ICompanyTrafficRecordService {
+
+    @Autowired
+    RedisCache redisCache;
+
+    @Autowired
+    private ISysConfigService iSysConfigService;
+
+    @Autowired
+    private ICompanyService companyService;
+
+    private SysConfig config;
+
+    @Autowired
+    private ICompanyTrafficRecordLogService companyTrafficRecordLogService;
+
+    //启动时初始化流量
+    @PostConstruct
+    public void init() {
+        List<CompanyTrafficRecord> companyTrafficRecords = baseMapper.selectList(new LambdaQueryWrapper<>());
+        for (CompanyTrafficRecord companyTrafficRecord : companyTrafficRecords) {
+            // 获取公司流量
+            Long balance = companyTrafficRecord.getBalance();
+            // 获取部门ID
+            Long deptId = companyTrafficRecord.getDeptId();
+            // 获取公司ID
+            Long companyId = companyTrafficRecord.getCompanyId();
+            // 缓存 key=CACHE_KEY:deptId:companyId  value=剩余流量
+            redisCache.setCacheObject(CompanyTrafficConstants.CACHE_KEY + ":" + deptId + ":" + companyId, balance);
+            // 缓存 key=CACHE_KEY:deptId  value=部门ID
+            calculateTotalTrafficByDeptId(deptId);
+        }
+    }
+
+    @Scheduled(cron = "0 0 3 * * ?")
+    public void refreshTraffic() {
+        List<CompanyTrafficRecord> companyTrafficRecords = baseMapper.selectList(new LambdaQueryWrapper<>());
+        for (CompanyTrafficRecord companyTrafficRecord : companyTrafficRecords) {
+            updateRedisCache(companyTrafficRecord);
+        }
+    }
+
+    //通过redis更新表流量并添加扣除日志
+    private void updateRedisCache(CompanyTrafficRecord companyTrafficRecord){
+        Long balance = redisCache.getCacheObject(CompanyTrafficConstants.CACHE_KEY
+                + ":" + companyTrafficRecord.getDeptId()
+                + ":" + companyTrafficRecord.getCompanyId());
+        if(balance != null)
+            baseMapper.updateById(CompanyTrafficRecord.builder().id(companyTrafficRecord.getId()).balance(balance).build());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean recharge(CompanyTrafficRecordChargeParam record) {
+        CompanyTrafficRecord companyTrafficRecord = new CompanyTrafficRecord();
+        //校验参数companyId
+        if (record.getCompanyId() == null || !validCompany(record))
+            throw new IllegalArgumentException("销售公司异常");
+
+        //查询公司流量记录
+        CompanyTrafficRecord companyRecord = baseMapper.selectOne(new LambdaQueryWrapper<CompanyTrafficRecord>()
+                .eq(CompanyTrafficRecord::getCompanyId, record.getCompanyId()));
+
+        switch (record.getOperationType()) {
+            //充值
+            case 1:
+                //获取转换后的流量
+                Long trafficAmount = trafficConversion(record.getChargeAmount());
+                record.setChangeTraffic(trafficAmount);//用于处理日志
+                if(companyRecord != null) {
+                    //不是第一次充值  增加流量
+                    companyTrafficRecord.setBalance(companyRecord.getBalance() + trafficAmount);
+                    companyTrafficRecord.setId(companyRecord.getId());
+                    companyTrafficRecord.setUpdateTime(new Date());
+                    companyTrafficRecord.setUpdateBy(record.getUserId());
+                    baseMapper.updateById(companyTrafficRecord);
+                }else{
+                    companyTrafficRecord.setBalance(trafficAmount);
+                    companyTrafficRecord.setCompanyId(record.getCompanyId());
+                    companyTrafficRecord.setDeptId(record.getCompany().getDeptId());
+                    companyTrafficRecord.setCompanyName(record.getCompany().getCompanyName());
+                    companyTrafficRecord.setCreateTime(new Date());
+                    companyTrafficRecord.setCreateBy(record.getUserId());
+                    baseMapper.insert(companyTrafficRecord);
+                }
+                break;
+            //扣除
+            case 2:
+                //扣除流量
+                /*if (companyRecord.getBalance() < record.getChangeTraffic())
+                    throw new IllegalArgumentException("公司流量余额不足");*/
+                companyTrafficRecord.setBalance(companyTrafficRecord.getBalance() - record.getChangeTraffic());
+                companyTrafficRecord.setId(companyRecord.getId());
+                companyTrafficRecord.setUpdateTime(new Date());
+                companyTrafficRecord.setUpdateBy(record.getUserId());
+                baseMapper.updateById(companyTrafficRecord);
+                break;
+            default:
+                throw new IllegalArgumentException("非法的操作类型");
+        }
+        // 更新Redis缓存
+        updateRedisCache(record, companyTrafficRecord);
+
+        return doLog(companyTrafficRecord,record);
+    }
+
+    private boolean validCompany(CompanyTrafficRecordChargeParam  record){
+        Company company = new Company();
+        company.setCompanyId(record.getCompanyId());
+        company.setIsDel(0);
+        List<Company> companies = companyService.selectCompanyList(company);
+        if(companies != null && companies.size() == 1){
+            record.setCompany(companies.get(0));
+            return true;
+        }else{
+            return false;
+        }
+    }
+    /**
+     * 更新Redis缓存
+     * @param record 充值参数
+     * @param companyTrafficRecord 公司流量记录
+     */
+    private void updateRedisCache(CompanyTrafficRecordChargeParam record, CompanyTrafficRecord companyTrafficRecord) {
+        if (record.getCompany() != null && record.getCompany().getDeptId() != null) {
+            // 更新缓存 key=CACHE_KEY:deptId:companyId  value=剩余流量
+            redisCache.setCacheObject(CompanyTrafficConstants.CACHE_KEY + ":" + record.getCompany().getDeptId() + ":" + record.getCompanyId(),
+                    companyTrafficRecord.getBalance());
+
+            // 计算该部门下所有公司流量总和
+            calculateTotalTrafficByDeptId(record.getCompany().getDeptId());
+        }else {
+            // 抛出异常
+            throw new IllegalArgumentException("缓存ID异常");
+        }
+    }
+
+    /**
+     * 部门下所有公司流量总和
+     * */
+    private void calculateTotalTrafficByDeptId(Long deptId) {
+        Long totalTraffic = baseMapper.calculateTotalTrafficByDeptId(deptId);
+        redisCache.setCacheObject(CompanyTrafficConstants.CACHE_KEY + ":" + deptId, totalTraffic);
+    }
+
+    private boolean doLog(CompanyTrafficRecord companyTrafficRecord,CompanyTrafficRecordChargeParam record){
+        return companyTrafficRecordLogService.save(CompanyTrafficRecordLog.builder()
+                        .balance(companyTrafficRecord.getBalance())
+                        .trafficAmount(record.getChangeTraffic())
+                        .chargeAmount(record.getChargeAmount())
+                        .companyId(record.getCompanyId())
+                        .createTime(new Date())
+                        .operationType(record.getOperationType())
+                        .userId(record.getUserId())
+                        .remark(record.getRemark())
+                        .userName(record.getUserName())
+                        .build());
+    }
+
+    @Override
+    public List<CompanyTrafficRecord> selectList(CompanyTrafficRecordQueryParam record) {
+        return baseMapper.selectList(new LambdaQueryWrapper<CompanyTrafficRecord>()
+                .eq(record.getCompanyId() != null,CompanyTrafficRecord::getCompanyId, record.getCompanyId())
+                .in(record.getCompanyIds() != null && !record.getCompanyIds().isEmpty(),CompanyTrafficRecord::getCompanyId, record.getCompanyIds())
+                .between(record.getCreateTimeStart() != null && record.getCreateTimeEnd() != null,CompanyTrafficRecord::getCreateTime, record.getCreateTimeStart(),record.getCreateTimeEnd()));
+    }
+
+    @Override
+    public CompanyTrafficRecord selectById(Long id) {
+        return baseMapper.selectById(id);
+    }
+
+
+    /** 充值金额转换流量KB*/
+    @Override
+    public Long trafficConversion(Long amount) {
+        if(this.config == null) {
+            this.config = iSysConfigService.selectConfigByConfigKey("statis.config");
+        }
+        JSONObject jsonObject = JSONObject.parseObject(this.config.getConfigValue());
+        float trafficPrice = jsonObject.getFloatValue("trafficPrice");
+        double trafficGB = amount / trafficPrice;
+        return (long) (trafficGB * 1024 * 1024);
+    }
+
+    @Override
+    public void refreshPrice() {
+        this.config = iSysConfigService.selectConfigByConfigKey("statis.config");
+    }
+}

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

@@ -90,4 +90,8 @@ public class CompanyVO implements Serializable
     private List<String> miniAppMaster;
     private List<String> miniAppServer;
     private Integer maxPadNum;
+    /** 已占用 */
+    private Integer usedNum;
+    /** 所属部门id */
+    private Long deptId;
 }

+ 36 - 6
fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -219,32 +219,62 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
 
     @Select({"<script> " +
             "SELECT \n" +
-            "o.video_id,o.company_id,o.qw_user_id,DATE(o.create_time) create_time,qu.qw_user_name,v.title videoName,uc.course_name,\n" +
+            "o.video_id,o.company_id,o.qw_user_id,DATE(o.create_time) create_time," +
+            "<if test= 'sendType != 1 '> " +
+            " qu.qw_user_name qw_user_name," +
+            "</if>\n" +
+            "<if test= 'sendType == 1 '> " +
+            " cu.nick_name qw_user_name," +
+            "</if>\n" +
+            "v.title videoName,uc.course_name,\n" +
             "SUM(CASE WHEN o.log_type = '1' THEN 1 ELSE 0 END) AS type1,\n" +
             "SUM(CASE WHEN o.log_type = '2' THEN 1 ELSE 0 END) AS type2,\n" +
             "SUM(CASE WHEN o.log_type = '3' THEN 1 ELSE 0 END) AS type3,\n" +
-            "SUM(CASE WHEN o.log_type = '4' THEN 1 ELSE 0 END) AS type4\n" +
+            "SUM(CASE WHEN o.log_type = '4' THEN 1 ELSE 0 END) AS type4,\n" +
+            "(\n" +
+            "  SUM(CASE WHEN o.log_type = '1' THEN 1 ELSE 0 END) +\n" +
+            "  SUM(CASE WHEN o.log_type = '2' THEN 1 ELSE 0 END) +\n" +
+            "  SUM(CASE WHEN o.log_type = '4' THEN 1 ELSE 0 END)\n" +
+            ") AS on_line_num\n" +
             "FROM fs_course_watch_log o\n" +
-            "LEFT JOIN qw_user qu on qu.id=o.qw_user_id\n" +
+            "<if test= 'sendType != 1 '> " +
+            " LEFT JOIN qw_user qu on qu.id=o.qw_user_id\n" +
+            "</if>\n" +
             "LEFT JOIN fs_user_course_video v on v.video_id=o.video_id \n" +
             "LEFT JOIN fs_user_course uc on uc.course_id=v.course_id\n" +
-            "where o.company_id=#{companyId} AND send_type=2 " +
+            "<if test= 'sendType == 1 '> " +
+            " LEFT JOIN company_user cu on cu.user_id=o.company_user_id\n" +
+            "</if>\n" +
+            "where o.company_id=#{companyId} " +
+            "<if test= 'sendType != null '> " +
+            "       and  send_type= #{sendType} " +
+            "</if>\n" +
             "<if test= 'sTime != null '> " +
             "       and DATE(o.create_time) &gt;= DATE(#{sTime})\n" +
             "</if>\n" +
             "<if test='eTime != null '> " +
             "      and DATE(o.create_time) &lt;= DATE(#{eTime})\n" +
             "</if>" +
-            "<if test ='nickName !=null and nickName!=\"\"'>\n" +
+            "<if test ='sendType != 1 and nickName !=null and nickName!=\"\"'>\n" +
             "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
             "</if>" +
+            "<if test ='sendType == 1 and nickName !=null and nickName!=\"\"'>\n" +
+            "   and cu.nick_name like concat( #{nickName}, '%')\n" +
+            "</if>" +
             "<if test ='courseId !=null'> " +
             "     and o.course_id = #{courseId} " +
             "</if>" +
             "<if test ='videoId !=null'> " +
             "     and o.video_id = #{videoId} " +
             "</if>" +
-            "GROUP BY o.video_id,o.qw_user_id,DATE(o.create_time)\n" +
+            "GROUP BY o.video_id," +
+            "<if test= 'sendType != 1 '> " +
+            " o.qw_user_id," +
+            "</if>\n" +
+            "<if test= 'sendType == 1 '> " +
+            " o.company_user_id," +
+            "</if>\n" +
+            "DATE(o.create_time)\n" +
             "ORDER BY o.video_id ,DATE(o.create_time) \n"+
             "</script>"})
     List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO(FsCourseWatchLogStatisticsListParam param);

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

@@ -40,4 +40,6 @@ public class FsCourseWatchLogStatisticsListParam {
 
     private Long pageNum;
     private Long pageSize;
+
+    private Integer sendType; //归属发送方式:1 个微  2 企微
 }

+ 13 - 0
fs-service/src/main/java/com/fs/course/param/FsFirstDiagnosisListUParam.java

@@ -0,0 +1,13 @@
+package com.fs.course.param;
+
+import com.fs.his.param.BaseParam;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FsFirstDiagnosisListUParam extends BaseParam {
+    private Long userId;
+
+    private Integer doctorStatus;
+}

+ 11 - 0
fs-service/src/main/java/com/fs/course/param/FsInquiryPatientInfoUParam.java

@@ -0,0 +1,11 @@
+package com.fs.course.param;
+
+import com.fs.his.param.BaseParam;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FsInquiryPatientInfoUParam extends BaseParam {
+    private Long userId;
+}

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

@@ -414,7 +414,7 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
             log.error("key中id为S:{}", videoDuration);
         }
 
-        
+
         if (videoDuration==null){
             FsUserCourseVideo video = courseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
             videoDuration=video.getDuration();
@@ -957,7 +957,16 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     public Long getVideoDuration(Long videoId){
         //将视频时长也存到redis
         String videoRedisKey = "h5user:video:duration:" + videoId;
-        Long videoDuration = redisCache.getCacheObject(videoRedisKey);
+        Long videoDuration=0L;
+        try {
+            videoDuration = redisCache.getCacheObject(videoRedisKey);
+        }catch (Exception e){
+            String string = redisCache.getCacheObject(videoRedisKey);
+            videoDuration=Long.parseLong(string);
+            log.error("key中id为S:{}", videoDuration);
+        }
+
+
         if (videoDuration==null){
             FsUserCourseVideo video = courseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
             videoDuration=video.getDuration();

+ 69 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -16,6 +16,7 @@ import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.date.DateUtil;
+import com.fs.company.constant.CompanyTrafficConstants;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMoneyLogs;
 import com.fs.company.domain.CompanyUser;
@@ -46,6 +47,7 @@ import com.fs.his.param.WxSendRedPacketParam;
 import com.fs.his.service.IFsStorePaymentService;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.service.IFsUserWxService;
+import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.OptionsVO;
 import com.fs.qw.domain.QwCompany;
 import com.fs.qw.domain.QwExternalContact;
@@ -73,6 +75,7 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -214,6 +217,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     @Autowired
     private FsCourseAnswerLogsMapper courseAnswerLogsMapper;
 
+    @Autowired
+    ConfigUtil configUtil;
+
 
     /**
      * 查询课堂视频
@@ -779,6 +785,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
                 logger.error("zyp \n【缓冲值空】参数: {}",param);
                 return R.error("缓冲值空");
             }
+
             FsCourseTrafficLog trafficLog = new FsCourseTrafficLog();
             trafficLog.setQwExternalContactId(param.getQwExternalId());
             trafficLog.setCreateTime(new Date());
@@ -788,6 +795,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             if (video == null) {
                 return R.error("视频不存在");
             }
+            Company company = companyMapper.selectCompanyById(param.getCompanyId());
+            if (company==null){
+                return R.error("公司不存在");
+            }
 
             // 计算流量
             BigDecimal result = param.getBufferRate().divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
@@ -801,6 +812,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 //                logger.error("zyp \n【插入或更新流量】:{}",trafficLog);
                 fsCourseTrafficLogMapper.insertOrUpdateTrafficLog(trafficLog);
             }
+            //扣除流量
+            asyncDeductTraffic(company, roundedResult);
         } catch (Exception e) {
             e.printStackTrace();
             // 打印参数param和异常信息
@@ -810,6 +823,52 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         return R.ok();
     }
 
+    @Async
+    public void asyncDeductTraffic(Company company, Long traffic) {
+        try {
+            // 扣除流量
+            Long remainingTraffic = updateRedisCache(company, traffic);
+
+            if ("1".equals(configUtil.generateConfigByKey("watch.course.config").getString("doNotPlay")) && remainingTraffic <= 0) {
+                logger.warn("公司ID: {} 流量不足,当前剩余: {}", company.getCompanyId(), remainingTraffic);
+                throw new Exception("流量不足");
+            }
+
+            logger.info("异步扣除流量成功 - 公司ID: {}, 扣除流量: {}, 剩余流量: {}",
+                    company.getCompanyId(), traffic, remainingTraffic);
+        } catch (Exception e) {
+            logger.error("异步扣除流量失败 - 公司ID: {}, 扣除流量: {}, 错误信息: {}",
+                    company.getCompanyId(), traffic, e.getMessage(), e);
+        }
+    }
+
+    //
+    private Long updateRedisCache(Company company, Long traffic) throws Exception {
+        // 分布式扣除流量
+        String companyKey = CompanyTrafficConstants.CACHE_KEY + ":" + company.getDeptId() + ":" + company.getCompanyId();
+        String deptKey = CompanyTrafficConstants.CACHE_KEY + ":" + company.getCompanyId();
+        String lockKey = companyKey + ":lock";
+        //销售公司剩余流量
+        Long companyTraffic = redisCache.getCacheObject(companyKey);
+        if(companyTraffic == null || companyTraffic<0){
+            return companyTraffic == null?0:companyTraffic;
+        }
+        try {
+            if (redisCache.setIfAbsent(lockKey, "1", 3, TimeUnit.SECONDS)) {
+                try {
+                   companyTraffic = redisCache.decr(companyKey, traffic);
+                   redisCache.decr(deptKey, traffic);
+                } finally {
+                    redisCache.deleteObject(lockKey); // 释放锁
+                }
+            }
+        } catch (Exception e) {
+            logger.error("【更新Redis缓存失败】企业ID: {}, 流量: {}, 错误信息:{}", company.getCompanyId(), traffic, e.getMessage(), e);
+            throw e;
+        }
+        return companyTraffic;
+    }
+
 
     @Override
     public R getIntegralByH5Video(FsUserCourseVideoFinishUParam param) {
@@ -2512,11 +2571,21 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         FsUserCourseVideo courseVideo = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoIdAndUserId(videoId,userId);
 
         BeanCopyUtils.copy(courseVideo,fsUserCourseVideoQVO);
+        if(courseVideo != null && courseVideo.getRedPacketMoney() != null){
+            fsUserCourseVideoQVO.setRedPacketMoney(courseVideo.getRedPacketMoney().toString());
+        }
 
         if (StringUtils.isNotEmpty(courseVideo.getQuestionBankId())){
             List<FsCourseQuestionBank> fsCourseQuestionBanks = courseQuestionBankMapper.selectFsCourseQuestionBankByIdVO(courseVideo.getQuestionBankId().split(","));
             fsUserCourseVideoQVO.setQuestionBankList(fsCourseQuestionBanks);
         }
+
+        //返回课程关联的拍商品
+        if(courseVideo.getIsProduct() != null && courseVideo.getIsProduct() == 1 && courseVideo.getProductId() != null){
+            FsCourseProduct courseProduct = courseProductMapper.selectFsCourseProductById(courseVideo.getProductId());
+            List<FsCourseProduct> courseProducts = Arrays.asList(courseProduct);
+            fsUserCourseVideoQVO.setCourseProducts(courseProducts);
+        }
         return fsUserCourseVideoQVO;
     }
 

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

@@ -30,6 +30,8 @@ public class FsCourseWatchLogStatisticsListVO {
     private String type3;
     @Excel(name = "看课中断")
     private String type4;
+    @Excel(name = "上线数")
+    private Long onLineNum;
     @Excel(name = "企业微信员工名称")
     private String qwUserName;
 

+ 77 - 0
fs-service/src/main/java/com/fs/course/vo/FsFirstDiagnosisListUVO.java

@@ -0,0 +1,77 @@
+package com.fs.course.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class FsFirstDiagnosisListUVO {
+    /** 主键 */
+    private Long id;
+    /** 患者姓名 */
+    @Excel(name = "患者姓名")
+    private String patientName;
+
+    /** 年龄 */
+    @Excel(name = "年龄")
+    private String age;
+
+    /** 0-未知 1-男性 2-女性 */
+    @Excel(name = "0-未知 1-男性 2-女性")
+    private Long gender;
+
+    /** 电话 */
+    @Excel(name = "电话")
+    private String phone;
+
+    /** 身体状况 */
+    @Excel(name = "身体状况")
+    private String physicalCondition;
+
+    /** 日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date dateTime;
+
+    /** 出版诊断 */
+    @Excel(name = "出版诊断")
+    private String firstDiagnosis;
+
+    /** 医生id */
+    @Excel(name = "医生id")
+    private Long doctorId;
+
+    /** 医生名称 */
+    @Excel(name = "医生名称")
+    private String doctorName;
+
+    /** 职称 */
+    @Excel(name = "职称")
+    private String doctorDep;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long userId;
+
+    /** 医生证号 */
+    @Excel(name = "医生证号")
+    private String doctorCertificate;
+
+    /** 医生是否填写:0-未填写 1-已填写 */
+    private Integer doctorStatus;
+
+    /** 用户是否答复:0-未答复 1-已答复 */
+    private Integer userStatus;
+
+    private Long qwUserId;
+
+    /**
+     * 销售名称
+     */
+    private String qwUserName;
+
+    //医生签名
+    private String signUrl;
+}

+ 113 - 0
fs-service/src/main/java/com/fs/course/vo/FsInquiryPatientInfoListUVO.java

@@ -0,0 +1,113 @@
+package com.fs.course.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class FsInquiryPatientInfoListUVO {
+
+    private Long id;
+
+    private String patientName;
+
+    /** 出生年月 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "出生年月", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date birthday;
+
+    /** 性别 */
+    @Excel(name = "性别")
+    private Integer sex;
+
+
+
+    /** 手机号 */
+    @Excel(name = "手机号")
+    private String mobile;
+
+    private String companyUserName;
+
+    /**
+     * 患者id
+     */
+    private Long patientId;
+
+    /**
+     * 销售id
+     */
+    private Long companyUserId;
+    /**
+     * 患者会员id
+     */
+    private Long userId;
+    /**
+     * 问诊订单id
+     */
+    private Long inquiryOrderId;
+    /**
+     * 课程id
+     */
+    private Long courseId;
+
+    /**
+     * 就诊状态 1-初诊 2-复诊
+     */
+    private Integer diagnosisStatus;
+
+    /**
+     * 客户标签
+     */
+    private String tag;
+
+    /**
+     * 课程/档期
+     */
+    private String courseName;
+
+    /**
+     * 患者病情主诉
+     */
+    private String patientCondition;
+
+    /**
+     * 部门负责人
+     */
+    private String deptManager;
+
+    /**
+     * 医生建议及处置
+     */
+    private String doctorAdviceJson;
+
+    /**
+     * 禁忌
+     */
+    private String taboo;
+    /**
+     * 客户需求
+     */
+    private String customerRequire;
+
+    /**
+     * 职业医生
+     */
+    private String professionalDoctor;
+
+    /**
+     * 助理医生
+     */
+    private String assistantDoctor;
+
+    /**
+     * 预约时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date subTime;
+
+    private Long subDoctorId;
+
+    private String subDoctorName;
+}

+ 2 - 2
fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java

@@ -479,8 +479,8 @@ public class DfOrderServiceImpl implements IErpOrderService
             orderPayMethod = 1;
         } else { // 如果是线上付款
             orderPayMethod = 2;
-            // 货到付款金额 = 物流代收金额-优惠金额
-            vo.setCollectingMoney(fsStoreOrder.getPayDelivery().subtract(couponPrice).doubleValue());
+            // 货到付款金额 = 订单剩余支付金额
+            vo.setCollectingMoney(fsStoreOrder.getPayRemain().doubleValue());
             vo.setCollectionCardNumber(config.getMonthlyCard()); // 就是月结账号
         }
         //订单付款方式 1:在线支付 2:货到付款

+ 96 - 0
fs-service/src/main/java/com/fs/fastGpt/domain/FastGptChatReplaceText.java

@@ -0,0 +1,96 @@
+package com.fs.fastGpt.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 违规词语对象 fastgpt_chat_replace_words
+ *
+ * @author fs
+ * @date 2025-01-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FastGptChatReplaceText extends BaseEntity{
+
+    /** id */
+    private Long id;
+
+    /** 类型 */
+    @Excel(name = "类型")
+    private Integer type;
+
+    /** 原来的文本 */
+    @Excel(name = "原来的文本")
+    private String content;
+
+    /** 变更后的文本 */
+    @Excel(name = "变更后的文本")
+    private String changeCount;
+
+    /** 状态 */
+    @Excel(name = "状态")
+    private Integer status;
+
+    /** 排序 */
+    @Excel(name = "排序")
+    private Long sort;
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+    public void setType(Integer type)
+    {
+        this.type = type;
+    }
+
+    public Integer getType()
+    {
+        return type;
+    }
+    public void setContent(String content)
+    {
+        this.content = content;
+    }
+
+    public String getContent()
+    {
+        return content;
+    }
+    public void setChangeCount(String changeCount)
+    {
+        this.changeCount = changeCount;
+    }
+
+    public String getChangeCount()
+    {
+        return changeCount;
+    }
+    public void setStatus(Integer status)
+    {
+        this.status = status;
+    }
+
+    public Integer getStatus()
+    {
+        return status;
+    }
+    public void setSort(Long sort)
+    {
+        this.sort = sort;
+    }
+
+    public Long getSort()
+    {
+        return sort;
+    }
+
+}

+ 2 - 0
fs-service/src/main/java/com/fs/fastGpt/domain/FastGptRole.java

@@ -63,4 +63,6 @@ public class FastGptRole extends BaseEntity
     private String contactInfo;
 
     private String channelType;
+
+    private Integer logistics;
 }

+ 68 - 0
fs-service/src/main/java/com/fs/fastGpt/mapper/FastGptChatReplaceTextMapper.java

@@ -0,0 +1,68 @@
+package com.fs.fastGpt.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.fastGpt.domain.FastGptChatReplaceText;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * 易错词语Mapper接口
+ *
+ * @author fs
+ * @date 2025-01-18
+ */
+public interface FastGptChatReplaceTextMapper extends BaseMapper<FastGptChatReplaceText>{
+    /**
+     * 查询易错词语
+     *
+     * @param id 易错词语主键
+     * @return 易错词语
+     */
+    FastGptChatReplaceText selectFastGptChatReplaceTextById(Long id);
+
+    /**
+     * 查询易错词语列表
+     *
+     * @param fastGptChatReplaceText 易错词语
+     * @return 易错词语集合
+     */
+    List<FastGptChatReplaceText> selectFastGptChatReplaceTextList(FastGptChatReplaceText fastGptChatReplaceText);
+
+    /**
+     * 新增易错词语
+     *
+     * @param fastGptChatReplaceText 易错词语
+     * @return 结果
+     */
+    int insertFastGptChatReplaceText(FastGptChatReplaceText fastGptChatReplaceText);
+
+    /**
+     * 修改易错词语
+     *
+     * @param fastGptChatReplaceText 易错词语
+     * @return 结果
+     */
+    int updateFastGptChatReplaceText(FastGptChatReplaceText fastGptChatReplaceText);
+
+    /**
+     * 删除易错词语
+     *
+     * @param id 易错词语主键
+     * @return 结果
+     */
+    int deleteFastGptChatReplaceTextById(Long id);
+
+    /**
+     * 批量删除易错词语
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFastGptChatReplaceTextByIds(Long[] ids);
+
+
+    @Select("select * from fastgpt_chat_replace_words where `status`=1" )
+    List<FastGptChatReplaceText> selectAllFastGptChatReplaceText();
+
+}

+ 10 - 0
fs-service/src/main/java/com/fs/fastGpt/mapper/FastGptRoleMapper.java

@@ -3,7 +3,9 @@ package com.fs.fastGpt.mapper;
 import java.util.List;
 import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.vo.FastGptRoleVO;
+import com.fs.fastGpt.vo.FastgptEventLogTotalVo;
 import com.fs.his.vo.OptionsVO;
+import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 
@@ -88,4 +90,12 @@ public interface FastGptRoleMapper
     List<OptionsVO> selectFastGptRoleType();
     @Select("select r.role_id, r.role_name,t.contact_info,r.company_id, r.create_time, r.update_time, r.role_type, r.mode_config_json, r.mode, r.kf_id, r.kf_url, r.avatar, r.kf_media_id,r.reminder_words, r.bind_corp_id,r.channel_type from fastgpt_role r LEFT JOIN fastgpt_role_type t on t.id =r.role_type where role_id = #{roleId}")
     FastGptRole selectFastGptRoleTypeByRoleId(Long roleId);
+
+    List<FastGptRole> selectFastGptRoleByRoleIds(@Param("roleIds") List<Long> roleIds);
+
+    List<String> selectFastGptRoleRoleIdsByAppKey(@Param("appKey") String appKey);
+
+    List<FastgptEventLogTotalVo> selectFastGptRoleAppKeyList();
+
+
 }

+ 93 - 0
fs-service/src/main/java/com/fs/fastGpt/mapper/FastgptEventLogTotalMapper.java

@@ -0,0 +1,93 @@
+package com.fs.fastGpt.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.fastGpt.domain.FastGptEventTokenLog;
+import com.fs.fastGpt.domain.FastgptEventLogTotal;
+import com.fs.fastGpt.vo.FastgptEventLogTotalVo;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * ai事件埋点统计Mapper接口
+ * 
+ * @author fs
+ * @date 2025-06-26
+ */
+public interface FastgptEventLogTotalMapper extends BaseMapper<FastgptEventLogTotal>{
+    /**
+     * 查询ai事件埋点统计
+     * 
+     * @param id ai事件埋点统计主键
+     * @return ai事件埋点统计
+     */
+    FastgptEventLogTotal selectFastgptEventLogTotalById(Long id);
+
+    /**
+     * 查询ai事件埋点统计列表
+     * 
+     * @param fastgptEventLogTotal ai事件埋点统计
+     * @return ai事件埋点统计集合
+     */
+    List<FastgptEventLogTotal> selectFastgptEventLogTotalList(FastgptEventLogTotal fastgptEventLogTotal);
+
+    /**
+     * 新增ai事件埋点统计
+     * 
+     * @param fastgptEventLogTotal ai事件埋点统计
+     * @return 结果
+     */
+    int insertFastgptEventLogTotal(FastgptEventLogTotal fastgptEventLogTotal);
+
+    /**
+     * 修改ai事件埋点统计
+     * 
+     * @param fastgptEventLogTotal ai事件埋点统计
+     * @return 结果
+     */
+    int updateFastgptEventLogTotal(FastgptEventLogTotal fastgptEventLogTotal);
+
+    /**
+     * 删除ai事件埋点统计
+     * 
+     * @param id ai事件埋点统计主键
+     * @return 结果
+     */
+    int deleteFastgptEventLogTotalById(Long id);
+
+    /**
+     * 批量删除ai事件埋点统计
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFastgptEventLogTotalByIds(Long[] ids);
+
+    List<FastgptEventLogTotal> selectFastgptEventLogTotalInfoList(FastgptEventLogTotal logTotal);
+
+    FastgptEventLogTotal selectFastgptEventLogTotalByRoleIdAndType(FastgptEventLogTotal total);
+
+    List<FastgptEventLogTotal> selectFastgptEventLogTotalVoInfoList(FastgptEventLogTotalVo fastgptEventLogTotal);
+
+    List<FastGptEventTokenLog> selectFastgptEventTokenLogTotalList(FastGptEventTokenLog fastGptEventTokenLog);
+
+    FastgptEventLogTotal selectFastgptEventTokenLogTotalByRoleIdAndType(FastGptEventTokenLog tokenLog);
+
+    /**
+     * 批量插入ai事件埋点统计
+     *
+     * @param fastgptEventLogTotalList ai事件埋点统计列表
+     * @return 结果
+     */
+    int insertFastgptEventLogTotalBatch(@Param("list") List<FastgptEventLogTotal> fastgptEventLogTotalList);
+
+    /**
+     * 批量更新ai事件埋点统计
+     *
+     * @param fastgptEventLogTotalList ai事件埋点统计列表
+     * @return 结果
+     */
+    int updateFastgptEventLogTotalBatch(@Param("list") List<FastgptEventLogTotal> fastgptEventLogTotalList);
+
+    List<FastgptEventLogTotalVo> selectFastgptEventLogTotalListByStatTime(@Param("dateTime") String dateTime);
+}

+ 76 - 0
fs-service/src/main/java/com/fs/fastGpt/param/FastgptEventLogTotalParam.java

@@ -0,0 +1,76 @@
+package com.fs.fastGpt.param;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * ai事件埋点统计对象 fastgpt_event_log_total
+ *
+ * @author fs
+ * @date 2025-06-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FastgptEventLogTotalParam extends BaseEntity{
+
+    /** $column.columnComment */
+    private Long id;
+
+    /** 角色id */
+    //@Excel(name = "角色id")
+    private Long roleId;
+
+    @Excel(name = "角色名称")
+    private Long roleName;
+
+    /** 数量 */
+    @Excel(name = "数量")
+    private Long count;
+
+    /** 日志类型 */
+    @Excel(name = "日志类型")
+    private Integer type;
+
+    /** 公司id */
+    @Excel(name = "公司id")
+    private Long companyId;
+
+    /** 公司用户id */
+    @Excel(name = "公司用户id")
+    private Long companyUserId;
+
+    /** 企微用户id */
+    @Excel(name = "企微用户id")
+    private Long qwUserId;
+
+    @Excel(name = "日志生成时间")
+    private String statTime;
+
+    /**
+     * 员工列表
+     */
+    private List<String> userIds;
+
+    private String qwUserIds;
+
+    private Date createTime;
+
+    private Integer pageNum;
+    private Integer pageSize;
+
+
+    private String beginTime;
+
+
+    private String endTime;
+
+    private String appKey;
+
+
+
+}

+ 62 - 0
fs-service/src/main/java/com/fs/fastGpt/service/IFastGptChatReplaceTextService.java

@@ -0,0 +1,62 @@
+package com.fs.fastGpt.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.fastGpt.domain.FastGptChatReplaceText;
+
+import java.util.List;
+
+/**
+ * 易错词语Service接口
+ * 
+ * @author fs
+ * @date 2025-01-18
+ */
+public interface IFastGptChatReplaceTextService extends IService<FastGptChatReplaceText>{
+    /**
+     * 查询易错词语
+     * 
+     * @param id 易错词语主键
+     * @return 易错词语
+     */
+    FastGptChatReplaceText selectFastGptChatReplaceTextById(Long id);
+
+    /**
+     * 查询易错词语列表
+     * 
+     * @param fastGptChatReplaceText 易错词语
+     * @return 易错词语集合
+     */
+    List<FastGptChatReplaceText> selectFastGptChatReplaceTextList(FastGptChatReplaceText fastGptChatReplaceText);
+
+    /**
+     * 新增易错词语
+     * 
+     * @param fastGptChatReplaceText 易错词语
+     * @return 结果
+     */
+    int insertFastGptChatReplaceText(FastGptChatReplaceText fastGptChatReplaceText);
+
+    /**
+     * 修改易错词语
+     * 
+     * @param fastGptChatReplaceText 易错词语
+     * @return 结果
+     */
+    int updateFastGptChatReplaceText(FastGptChatReplaceText fastGptChatReplaceText);
+
+    /**
+     * 批量删除易错词语
+     * 
+     * @param ids 需要删除的易错词语主键集合
+     * @return 结果
+     */
+    int deleteFastGptChatReplaceTextByIds(Long[] ids);
+
+    /**
+     * 删除易错词语信息
+     * 
+     * @param id 易错词语主键
+     * @return 结果
+     */
+    int deleteFastGptChatReplaceTextById(Long id);
+}

+ 8 - 0
fs-service/src/main/java/com/fs/fastGpt/service/IFastGptRoleService.java

@@ -4,6 +4,7 @@ import java.util.List;
 
 import com.fs.common.core.domain.R;
 import com.fs.fastGpt.domain.FastGptRole;
+import com.fs.fastGpt.vo.FastGptRoleDataVO;
 import com.fs.fastGpt.vo.FastGptRoleVO;
 import com.fs.his.vo.OptionsVO;
 
@@ -73,4 +74,11 @@ public interface IFastGptRoleService
     List<OptionsVO> selectFastGptRoleType();
 
     public FastGptRole selectFastGptRoleTypeByRoleId(Long roleId);
+
+    List<String> selectFastGptRoleRoleIdsByAppKey(String appKey);
+
+    List<FastGptRole> selectFastGptRoleByRoleIds(List<Long> roleIds);
+
+    List<FastGptRoleDataVO> selectFastGptRoleAppKeyList();
+
 }

+ 94 - 0
fs-service/src/main/java/com/fs/fastGpt/service/IFastgptEventLogTotalService.java

@@ -0,0 +1,94 @@
+package com.fs.fastGpt.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.fastGpt.domain.FastGptEventTokenLog;
+import com.fs.fastGpt.domain.FastgptEventLogTotal;
+import com.fs.fastGpt.vo.FastgptEventLogTotalVo;
+
+import java.util.List;
+
+/**
+ * ai事件埋点统计Service接口
+ * 
+ * @author fs
+ * @date 2025-06-26
+ */
+public interface IFastgptEventLogTotalService extends IService<FastgptEventLogTotal>{
+    /**
+     * 查询ai事件埋点统计
+     * 
+     * @param id ai事件埋点统计主键
+     * @return ai事件埋点统计
+     */
+    FastgptEventLogTotal selectFastgptEventLogTotalById(Long id);
+
+    /**
+     * 查询ai事件埋点统计列表
+     * 
+     * @param fastgptEventLogTotal ai事件埋点统计
+     * @return ai事件埋点统计集合
+     */
+    List<FastgptEventLogTotal> selectFastgptEventLogTotalList(FastgptEventLogTotal fastgptEventLogTotal);
+
+    /**
+     * 新增ai事件埋点统计
+     * 
+     * @param fastgptEventLogTotal ai事件埋点统计
+     * @return 结果
+     */
+    int insertFastgptEventLogTotal(FastgptEventLogTotal fastgptEventLogTotal);
+
+    /**
+     * 修改ai事件埋点统计
+     * 
+     * @param fastgptEventLogTotal ai事件埋点统计
+     * @return 结果
+     */
+    int updateFastgptEventLogTotal(FastgptEventLogTotal fastgptEventLogTotal);
+
+    /**
+     * 批量删除ai事件埋点统计
+     * 
+     * @param ids 需要删除的ai事件埋点统计主键集合
+     * @return 结果
+     */
+    int deleteFastgptEventLogTotalByIds(Long[] ids);
+
+    /**
+     * 删除ai事件埋点统计信息
+     * 
+     * @param id ai事件埋点统计主键
+     * @return 结果
+     */
+    int deleteFastgptEventLogTotalById(Long id);
+
+    List<FastgptEventLogTotal> selectFastgptEventLogTotalInfoList(FastgptEventLogTotal logTotal);
+
+    FastgptEventLogTotal selectFastgptEventLogTotalByRoleIdAndType(FastgptEventLogTotal total);
+
+    List<FastgptEventLogTotalVo> selectFastgptEventLogTotalVoInfoList(FastgptEventLogTotalVo fastgptEventLogTotal);
+
+    List<FastGptEventTokenLog> selectFastgptEventTokenLogTotalList(FastGptEventTokenLog fastGptEventTokenLog);
+
+    FastgptEventLogTotal selectFastgptEventTokenLogTotalByRoleIdAndType(FastGptEventTokenLog tokenLog);
+
+    FastgptEventLogTotalVo totalFastgptEventLog(List<FastgptEventLogTotalVo> list);
+
+    /**
+     * 批量新增ai事件埋点统计
+     *
+     * @param fastgptEventLogTotalList ai事件埋点统计列表
+     * @return 结果
+     */
+    int insertFastgptEventLogTotalBatch(List<FastgptEventLogTotal> fastgptEventLogTotalList);
+
+    /**
+     * 批量修改ai事件埋点统计
+     *
+     * @param fastgptEventLogTotalList ai事件埋点统计列表
+     * @return 结果
+     */
+    int updateFastgptEventLogTotalBatch(List<FastgptEventLogTotal> fastgptEventLogTotalList);
+
+    List<FastgptEventLogTotalVo> selectFastgptEventLogTotalListByStatTime(String dateTime);
+}

+ 101 - 30
fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java

@@ -54,6 +54,7 @@ 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.utils.SensitiveDataUtils;
 import com.fs.voice.utils.StringUtil;
 import com.fs.wxwork.dto.*;
 import com.fs.wxwork.service.WxWorkService;
@@ -61,6 +62,7 @@ import com.vdurmont.emoji.EmojiParser;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.Nullable;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.scheduling.annotation.Async;
@@ -156,6 +158,8 @@ public class AiHookServiceImpl implements AiHookService {
     private IFsExpressService expressService;
     @Autowired
     private CompanyConfigMapper companyConfigMapper;
+    @Autowired
+    private IFastGptChatReplaceTextService fastGptChatReplaceTextService;
 
     /** Ai半小时未回复提醒 **/
     /**
@@ -363,38 +367,38 @@ public class AiHookServiceImpl implements AiHookService {
         if (qwContent.contains("我已经添加了你")){
             return R.ok();
         }
-        System.out.println(qwUserId);
+        log.info("数据:{}", qwUserId);
         QwUser user = qwUserMapper.selectQwUserById(qwUserId);
         //查询接收人
         if(user==null){
-            System.out.println("查询接收人为空");
+            log.error("查询接收人为空");
             return R.ok();
         }
         if(user.getFastGptRoleId()==null){
-            System.out.println("未绑定角色");
+            log.error("未绑定角色");
             return R.ok();
         }
         Long serverId = user.getServerId();
-        System.out.println("服务器id"+serverId);
+        log.info("服务器id"+serverId);
         if (serverId == null) {
-            System.out.println("服务id为空");
+            log.error("服务id为空");
             return R.ok();
         }
         FastGptRole role=roleService.selectFastGptRoleTypeByRoleId(user.getFastGptRoleId());
         //没用ai角色跳过
         if(role==null){
-            System.out.println("没用ai角色跳过");
+            log.error("没用ai角色跳过");
             return R.ok();
         }
         String modeConfig=role.getModeConfigJson();
         //key不为空
         if(StringUtils.isEmpty(modeConfig)){
-            System.out.println("没有aiKey");
+            log.error("没有aiKey");
             return R.ok();
         }
         ModeConfig config=JSONUtil.toBean(modeConfig,ModeConfig.class);
         if(StringUtils.isEmpty(config.getAPPKey())){
-            System.out.println("没有aiKey");
+            log.error("没有aiKey");
             return R.ok();
         }
         WxWorkVid2UserIdDTO wxWorkVid2UserIdDTO = new WxWorkVid2UserIdDTO();
@@ -404,14 +408,14 @@ public class AiHookServiceImpl implements AiHookService {
         List<WxWorkVid2UserIdRespDTO> data = WxWorkVid2UserIdRespDTO.getData();
         if (data==null|| data.isEmpty()){
 
-            System.out.println("未获取到extId"+wxWorkVid2UserIdDTO);
+            log.error("未获取到extId"+wxWorkVid2UserIdDTO);
             return R.ok();
         }
         com.fs.wxwork.dto.WxWorkVid2UserIdRespDTO dto = data.get(0);
 
         QwExternalContact qwExternalContacts = qwExternalContactMapper.selectQwExternalContactByExternalUserIdAndQwUserId(dto.getOpenid(), user.getCorpId(),user.getQwUserId());
         if (qwExternalContacts==null){
-            System.out.println("没有外部联系人");
+            log.error("没有外部联系人" + "user:" + user);
             return R.ok();
         }
         if(qwExternalContacts.getType()==2){
@@ -437,13 +441,21 @@ public class AiHookServiceImpl implements AiHookService {
                     }
                 }
             }else {
-                System.out.println("不是图片"+type);
+                log.info("不是图片"+type);
             }
 
-            String contentEmj = replaceWxEmo(qwContent);
+            //对用户处理的内容做处理
+            String maskedContent = processContent(qwContent);
+            String contentEmj = replaceWxEmo(maskedContent);
             if(!contentEmj.contains("表情包")){
                 if(!contentEmj.isEmpty()){
                     addSaveAiMsg(1,1,contentEmj,user,fastGptChatSession.getSessionId(),role.getRoleId(),qwExternalContacts,fastGptChatSession.getUserId(),null,null,null);
+                    //通过用户发送的对话去查询用户是否为新客,是就删除sop,否就不做处理
+                    cleanNewUserDialogue(user, qwExternalContacts);
+                    //用户是未回复状态
+                    if(qwExternalContacts.getIsReply() == 0){
+                        qwExternalContactMapper.updateQwExternalContactIsRePlyById(qwExternalContacts.getId());
+                    }
                 }else {
                     contentEmj ="用户发送表情:"+qwContent;
                     if (type==16){
@@ -455,7 +467,7 @@ public class AiHookServiceImpl implements AiHookService {
 
             //判断是否转人工
             if (fastGptChatSession.getIsArtificial()==1){
-                System.out.println("转人工了");
+                log.error("转人工了,sessionId:" + fastGptChatSession.getSessionId());
                 return R.ok();
             }
             //获取是用户是否发送消息
@@ -472,22 +484,32 @@ public class AiHookServiceImpl implements AiHookService {
                     redisCache.setCacheObject("msg:" + fastGptChatSession.getSessionId(),msg+","+contentEmj,5,TimeUnit.MINUTES);
                 }
                 //本次跳过
-                System.out.println("正在对话");
+                log.info("正在对话");
                 return R.ok();
             }
             //用户首次发送消息
             redisCache.setCacheObject("reply:" + fastGptChatSession.getSessionId(),1,5,TimeUnit.MINUTES);
             redisCache.setCacheObject("msg:" + fastGptChatSession.getSessionId(),contentEmj,5,TimeUnit.MINUTES);
-            System.out.println("等待");
+            log.info("等待");
             R r= sendAiMsg(replyI,fastGptChatSession,role,user,qwExternalContacts.getId(),config.getAPPKey(),qwExternalContacts,sender);
             EventLogUtils.recordEventLog(sender,1L,1,user);
             EventLogUtils.recordEventLog(sender,1L,2,user);
-            System.out.println(r);
+            log.info("数据:{}", r);
             //完成对话 删除消息记录
             redisCache.deleteObject("reply:" + fastGptChatSession.getSessionId());
             redisCache.deleteObject("msg:" + fastGptChatSession.getSessionId());
             if(!r.get("code").equals(200)){
-                log.info("ai报错转人工:"+role.getRoleId()+":"+qwExternalContacts.getName());
+                //判断消息是否需要重发的依据
+               /*redisCache.setCacheObject("retry:" + fastGptChatSession.getSessionId(),1,2,TimeUnit.MINUTES);
+                redisCache.setCacheObject("retryMsg:" + fastGptChatSession.getSessionId(),contentEmj,2,TimeUnit.MINUTES);
+                Integer retryCount = redisCache.getCacheObject("retry:" + fastGptChatSession.getSessionId());
+                if(retryCount < 3){
+                    r= retrySendAiMsg(retryCount,fastGptChatSession,role,user,qwExternalContacts.getId(),config.getAPPKey(),qwExternalContacts,sender);
+                }
+                redisCache.deleteObject("retry:" + fastGptChatSession.getSessionId());
+                redisCache.deleteObject("retryMsg:" + fastGptChatSession.getSessionId());*/
+
+                log.error("ai报错转人工:"+role.getRoleId()+":"+qwExternalContacts.getName());
                 notifyArtificial(fastGptChatSession.getSessionId(),user,qwExternalContacts.getName()," ai报错转人工",qwExternalContacts.getId(),sender);
                 return R.ok();
             }
@@ -507,13 +529,6 @@ 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!=null && 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表中查询所有转人工文本
@@ -524,6 +539,12 @@ public class AiHookServiceImpl implements AiHookService {
                     notifyArtificial(fastGptChatSession.getSessionId(),user,qwExternalContacts.getName()," 触发关键词",qwExternalContacts.getId(),sender);
                     return R.ok();
                 }
+                //ai回复文字长度大于500就转人工
+                if(content.length() > 500){
+                    log.error("回复长度异常:"+role.getRoleId()+":"+qwExternalContacts.getName());
+                    notifyArtificial(fastGptChatSession.getSessionId(),user,qwExternalContacts.getName()," 回复长度异常",qwExternalContacts.getId(),sender);
+                    return R.ok();
+                }
                 if (result.isLongText()){
                     //新增用户信息
                     addUserInfo(contentKh, qwExternalContacts.getId(),fastGptChatSession);
@@ -569,7 +590,7 @@ public class AiHookServiceImpl implements AiHookService {
 
 
             if (result.isArtificial()){
-                log.info("ai请求人工:"+role.getRoleId()+":"+qwExternalContacts.getName());
+                log.error("ai请求人工:"+role.getRoleId()+":"+qwExternalContacts.getName());
                 notifyArtificial(fastGptChatSession.getSessionId(),user,qwExternalContacts.getName()," ai请求人工协助",qwExternalContacts.getId(),sender);
             }
         }
@@ -577,6 +598,34 @@ public class AiHookServiceImpl implements AiHookService {
         return R.ok();
     }
 
+    /**
+     * 通过用户发送的对话去查询用户是否为新客,是就删除sop,否就不做处理
+     * @param user
+     * @param qwExternalContacts
+     */
+    private void cleanNewUserDialogue(QwUser user, QwExternalContact qwExternalContacts) {
+        String redisKey = "qwNewChat:" + user.getQwUserId() + ":" + user.getCorpId() + ":" + qwExternalContacts.getExternalUserId();
+        String key  = (String) redisCache.getCacheObject(redisKey);
+        if(!StringUtil.strIsNullOrEmpty(key)){
+            try {
+                QwSopLogs qwSopLogs = new QwSopLogs();
+                qwSopLogs.setQwUserid(user.getQwUserId());
+                qwSopLogs.setCorpId(user.getCorpId());
+                qwSopLogs.setExternalUserId(qwExternalContacts.getExternalUserId());
+                qwSopLogs.setSendStatus(3L);
+                qwSopLogs.setSendType(4);
+                List<QwSopLogs> qwSopLogsList = qwSopLogsMapper.selectQwSopLogsList(qwSopLogs);
+                if(qwSopLogsList != null && !qwSopLogsList.isEmpty()){
+                    qwSopLogsMapper.batchUpdateQwSopLogsNewUserById(qwSopLogsList);
+                }
+            } catch (Exception e) {
+                log.error("停用新客对话sop失败:" + redisKey + "原因:" + e);
+            }finally {
+                redisCache.deleteObject(redisKey);
+            }
+        }
+    }
+
 
 
     /**
@@ -636,7 +685,7 @@ public class AiHookServiceImpl implements AiHookService {
         Random random = new Random();
         FastGptKeywordSend fastGptKeywordSend = keywordSendList.get(random.nextInt(keywordSendList.size()));
         if (fastGptKeywordSend == null){
-            System.out.println("输出为空格");
+            log.info("输出为空格");
             return;
         }
         if(fastGptKeywordSend.getKeywordType() == 1L && "物流".equals(fastGptKeywordSend.getKeyword())){
@@ -662,11 +711,11 @@ public class AiHookServiceImpl implements AiHookService {
                                 fastGptChatSessionMapper.updateFastGptChatSession(fastGptChatSession);
                                 break;
                             case 2:
-                                sBuilder.append("您购买的 ").append(fsStoreOrder.getPackageName()).append(" 正在准备发货,请耐心等待;\n");
-                                break;
+                                sBuilder.append("您好,您有一个包裹正在准备发货,请耐心等待;\n")
+                                        .append("\uD83C\uDF39\uD83C\uDF39\uD83C\uDF39");
                             case 3:
                                 ExpressInfoDTO expressInfo = getExpress(fsStoreOrder.getOrderId());
-                                sBuilder.append("您购买的 ").append(fsStoreOrder.getPackageName());
+                                sBuilder.append("您购买的有一个包裹 ");
                                 sBuilder.append(" 已经查询到了,正在配送中了。\n");
                                 if(expressInfo != null && expressInfo.getTraces() != null && !expressInfo.getTraces().isEmpty()){
                                     List<TracesDTO> traces = expressInfo.getTraces();
@@ -679,7 +728,7 @@ public class AiHookServiceImpl implements AiHookService {
                             case 5:
                                 ExpressInfoDTO express = getExpress(fsStoreOrder.getOrderId());
                                 //你好,这边查询到您购买的XXX(购买套餐)在XXX(时间)已经送到了,送货员电话为XXX(送货员信息)
-                                sBuilder.append("这边查询到您购买的 ").append(fsStoreOrder.getPackageName());
+                                sBuilder.append("这边查询到您有一个包裹");
                                 if(express != null && express.getTraces() != null && !express.getTraces().isEmpty()){
                                     List<TracesDTO> traces = express.getTraces();
                                     TracesDTO tracesDTO = traces.get(traces.size() - 1);
@@ -813,6 +862,28 @@ public class AiHookServiceImpl implements AiHookService {
         return null;
     }
 
+    /**
+     * 处理用户的输入问题
+     * @param qwContent
+     * @return
+     */
+    private @Nullable String processContent(String qwContent) {
+        String maskedContent = SensitiveDataUtils.maskMobileNumbers(qwContent);
+        if(maskedContent != null && !maskedContent.isEmpty()){
+            FastGptChatReplaceText fastGptChatReplaceText = new FastGptChatReplaceText();
+            fastGptChatReplaceText.setStatus(1);
+            List<FastGptChatReplaceText> chatReplaceTextList = fastGptChatReplaceTextService.selectFastGptChatReplaceTextList(fastGptChatReplaceText);
+            if(chatReplaceTextList != null && !chatReplaceTextList.isEmpty()){
+                for (FastGptChatReplaceText replaceText : chatReplaceTextList) {
+                    if(maskedContent.contains(replaceText.getContent())){
+                        maskedContent = maskedContent.replace(replaceText.getContent(), replaceText.getChangeCount());
+                    }
+                }
+            }
+        }
+        return maskedContent;
+    }
+
     private void sendAiVoiceMsg(String content, Long sendId , String uuid,Long serverId,QwUser user) {
         if (content == null || content.trim().isEmpty()){
             System.out.println("输出为空格");

+ 0 - 2
fs-service/src/main/java/com/fs/fastGpt/service/impl/FastGptChatMsgServiceImpl.java

@@ -134,13 +134,11 @@ public class FastGptChatMsgServiceImpl implements IFastGptChatMsgService
     }
 
     @Override
-    @DataSource(DataSourceType.CLICKHOUSE)
     public void insertFastGptEventLog(FastGptEventLog fastGptEventLog) {
         fastGptChatMsgMapper.insertFastGptEventLog(fastGptEventLog);
     }
 
     @Override
-    @DataSource(DataSourceType.CLICKHOUSE)
     public void insertFastGptEventTokenLog(FastGptEventTokenLog fastGptEventTokenLog) {
         fastGptChatMsgMapper.insertFastGptEventTokenLog(fastGptEventTokenLog);
     }

+ 93 - 0
fs-service/src/main/java/com/fs/fastGpt/service/impl/FastGptChatReplaceTextServiceImpl.java

@@ -0,0 +1,93 @@
+package com.fs.fastGpt.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.DateUtils;
+import com.fs.fastGpt.domain.FastGptChatReplaceText;
+import com.fs.fastGpt.mapper.FastGptChatReplaceTextMapper;
+import com.fs.fastGpt.service.IFastGptChatReplaceTextService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 易错词语Service业务层处理
+ *
+ * @author fs
+ * @date 2025-01-18
+ */
+@Service
+public class FastGptChatReplaceTextServiceImpl extends ServiceImpl<FastGptChatReplaceTextMapper, FastGptChatReplaceText> implements IFastGptChatReplaceTextService {
+
+    /**
+     * 查询易错词语
+     *
+     * @param id 易错词语主键
+     * @return 易错词语
+     */
+    @Override
+    public FastGptChatReplaceText selectFastGptChatReplaceTextById(Long id)
+    {
+        return baseMapper.selectFastGptChatReplaceTextById(id);
+    }
+
+    /**
+     * 查询易错词语列表
+     *
+     * @param fastGptChatReplaceText 易错词语
+     * @return 易错词语
+     */
+    @Override
+    public List<FastGptChatReplaceText> selectFastGptChatReplaceTextList(FastGptChatReplaceText fastGptChatReplaceText)
+    {
+        return baseMapper.selectFastGptChatReplaceTextList(fastGptChatReplaceText);
+    }
+
+    /**
+     * 新增易错词语
+     *
+     * @param fastGptChatReplaceText 易错词语
+     * @return 结果
+     */
+    @Override
+    public int insertFastGptChatReplaceText(FastGptChatReplaceText fastGptChatReplaceText)
+    {
+        fastGptChatReplaceText.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertFastGptChatReplaceText(fastGptChatReplaceText);
+    }
+
+    /**
+     * 修改易错词语
+     *
+     * @param fastGptChatReplaceText 易错词语
+     * @return 结果
+     */
+    @Override
+    public int updateFastGptChatReplaceText(FastGptChatReplaceText fastGptChatReplaceText)
+    {
+        return baseMapper.updateFastGptChatReplaceText(fastGptChatReplaceText);
+    }
+
+    /**
+     * 批量删除易错词语
+     *
+     * @param ids 需要删除的易错词语主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFastGptChatReplaceTextByIds(Long[] ids)
+    {
+        return baseMapper.deleteFastGptChatReplaceTextByIds(ids);
+    }
+
+    /**
+     * 删除易错词语信息
+     *
+     * @param id 易错词语主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFastGptChatReplaceTextById(Long id)
+    {
+        return baseMapper.deleteFastGptChatReplaceTextById(id);
+    }
+}

+ 77 - 0
fs-service/src/main/java/com/fs/fastGpt/service/impl/FastGptRoleServiceImpl.java

@@ -3,13 +3,18 @@ package com.fs.fastGpt.service.impl;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.BeanCopyUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
 import com.fs.fastGpt.domain.FastGptRoleTag;
 import com.fs.fastGpt.mapper.FastGptRoleTagMapper;
+import com.fs.fastGpt.vo.FastGptRoleDataVO;
 import com.fs.fastGpt.vo.FastGptRoleVO;
+import com.fs.fastGpt.vo.FastgptEventLogTotalVo;
 import com.fs.his.vo.OptionsVO;
 import com.fs.qw.mapper.QwUserMapper;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -171,5 +176,77 @@ public class FastGptRoleServiceImpl implements IFastGptRoleService
         return fastGptRoleMapper.selectFastGptRoleTypeByRoleId(roleId);
     }
 
+    @Override
+    public List<String> selectFastGptRoleRoleIdsByAppKey(String appKey) {
+        return fastGptRoleMapper.selectFastGptRoleRoleIdsByAppKey(appKey);
+    }
+
+    @Override
+    public List<FastGptRole> selectFastGptRoleByRoleIds(List<Long> roleIds) {
+        return fastGptRoleMapper.selectFastGptRoleByRoleIds(roleIds);
+    }
+
+    @Override
+    public List<FastGptRoleDataVO> selectFastGptRoleAppKeyList() {
+        List<FastgptEventLogTotalVo> eventLogTotalVos = fastGptRoleMapper.selectFastGptRoleAppKeyList();
+        Map<String, List<FastgptEventLogTotalVo>> appKeyLitMap = eventLogTotalVos.stream()
+                .filter(m -> {
+                    if (m.getAppKey() != null && !m.getAppKey().isEmpty()) {
+                        try {
+                            String appKey = m.getAppKey();
+                            if (appKey.startsWith("\"") && appKey.endsWith("\"")) {
+                                appKey = appKey.substring(1, appKey.length() - 1)
+                                        .replace("\\", "")
+                                        .replace(" ","");
+                            } else {
+                                appKey = appKey.replace("\\", "");
+                            }
+                            JSONObject jsonObject = JSONObject.parseObject(appKey);
+                            String key = jsonObject.getString("APPKey");
+                            if (key != null && !key.isEmpty() && !"null".equals(key)) {
+                                m.setAppKey(key);
+                            } else {
+                                return false;
+                            }
+                        } catch (Exception e) {
+                            return false;
+                        }
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }).collect(Collectors.groupingBy(FastgptEventLogTotalVo::getAppKey));
+
+        List<FastGptRoleDataVO> roleDataList = new ArrayList<>();
+        for (String appKey : appKeyLitMap.keySet()) {
+            List<FastgptEventLogTotalVo> eventLogTotalVos1 = appKeyLitMap.get(appKey);
+            FastGptRoleDataVO roleDataVO = new FastGptRoleDataVO();
+            List<FastGptRoleDataVO> roleInfoList = new ArrayList<>();
+            roleDataVO.setRoleName(appKey);
+            roleDataVO.setRoleId((long)roleDataList.size() + 1);
+            for (FastgptEventLogTotalVo eventLogTotalVo : eventLogTotalVos1) {
+                String roleName = eventLogTotalVo.getRoleName();
+                Long roleId = eventLogTotalVo.getRoleId();
+                if(roleDataVO.getRoleList() == null){
+                    FastGptRoleDataVO roleInfo = new FastGptRoleDataVO();
+                    roleInfo.setRoleName(roleName);
+                    roleInfo.setRoleId(roleId);
+                    roleInfoList.add(roleInfo);
+                    roleDataVO.setRoleList(roleInfoList);
+                }else{
+                    List<FastGptRoleDataVO> roleInfoList1 = roleDataVO.getRoleList();
+                    FastGptRoleDataVO roleInfo = new FastGptRoleDataVO();
+                    roleInfo.setRoleName(roleName);
+                    roleInfo.setRoleId(roleId);
+                    roleInfoList1.add(roleInfo);
+                    roleDataVO.setRoleList(roleInfoList1);
+                }
+            }
+            roleDataList.add(roleDataVO);
+        }
+
+        return roleDataList;
+    }
+
 
 }

+ 290 - 0
fs-service/src/main/java/com/fs/fastGpt/service/impl/FastgptEventLogTotalServiceImpl.java

@@ -0,0 +1,290 @@
+package com.fs.fastGpt.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.annotation.DataSource;
+import com.fs.common.core.domain.entity.SysDictData;
+import com.fs.common.enums.DataSourceType;
+import com.fs.common.utils.DateUtils;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyService;
+import com.fs.fastGpt.domain.FastGptEventTokenLog;
+import com.fs.fastGpt.domain.FastGptRole;
+import com.fs.fastGpt.domain.FastgptEventLogTotal;
+import com.fs.fastGpt.mapper.FastgptEventLogTotalMapper;
+import com.fs.fastGpt.service.IFastGptRoleService;
+import com.fs.fastGpt.service.IFastgptEventLogTotalService;
+import com.fs.fastGpt.vo.FastgptEventLogTotalVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.fs.common.utils.DictUtils.getDictCache;
+
+/**
+ * ai事件埋点统计Service业务层处理
+ * 
+ * @author fs
+ * @date 2025-06-26
+ */
+@Service
+public class FastgptEventLogTotalServiceImpl extends ServiceImpl<FastgptEventLogTotalMapper, FastgptEventLogTotal> implements IFastgptEventLogTotalService {
+
+    @Autowired
+    FastgptEventLogTotalMapper fastgptEventLogTotalMapper;
+
+    @Autowired
+    private ICompanyService companyService;
+
+    @Autowired
+    private IFastGptRoleService fastGptRoleService;
+    /**
+     * 查询ai事件埋点统计
+     * 
+     * @param id ai事件埋点统计主键
+     * @return ai事件埋点统计
+     */
+    @Override
+    public FastgptEventLogTotal selectFastgptEventLogTotalById(Long id)
+    {
+        return baseMapper.selectFastgptEventLogTotalById(id);
+    }
+
+    /**
+     * 查询ai事件埋点统计列表
+     * 
+     * @param fastgptEventLogTotal ai事件埋点统计
+     * @return ai事件埋点统计
+     */
+    @Override
+    public List<FastgptEventLogTotal> selectFastgptEventLogTotalList(FastgptEventLogTotal fastgptEventLogTotal)
+    {
+        return baseMapper.selectFastgptEventLogTotalList(fastgptEventLogTotal);
+    }
+
+    /**
+     * 新增ai事件埋点统计
+     * 
+     * @param fastgptEventLogTotal ai事件埋点统计
+     * @return 结果
+     */
+    @Override
+    public int insertFastgptEventLogTotal(FastgptEventLogTotal fastgptEventLogTotal)
+    {
+        return baseMapper.insertFastgptEventLogTotal(fastgptEventLogTotal);
+    }
+
+    /**
+     * 修改ai事件埋点统计
+     * 
+     * @param fastgptEventLogTotal ai事件埋点统计
+     * @return 结果
+     */
+    @Override
+    public int updateFastgptEventLogTotal(FastgptEventLogTotal fastgptEventLogTotal)
+    {
+        return baseMapper.updateFastgptEventLogTotal(fastgptEventLogTotal);
+    }
+
+    /**
+     * 批量删除ai事件埋点统计
+     * 
+     * @param ids 需要删除的ai事件埋点统计主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFastgptEventLogTotalByIds(Long[] ids)
+    {
+        return baseMapper.deleteFastgptEventLogTotalByIds(ids);
+    }
+
+    /**
+     * 删除ai事件埋点统计信息
+     * 
+     * @param id ai事件埋点统计主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFastgptEventLogTotalById(Long id)
+    {
+        return baseMapper.deleteFastgptEventLogTotalById(id);
+    }
+
+    @Override
+    public List<FastgptEventLogTotal> selectFastgptEventLogTotalInfoList(FastgptEventLogTotal logTotal) {
+        return fastgptEventLogTotalMapper.selectFastgptEventLogTotalInfoList(logTotal);
+    }
+
+    @Override
+    public FastgptEventLogTotal selectFastgptEventLogTotalByRoleIdAndType(FastgptEventLogTotal total) {
+        return fastgptEventLogTotalMapper.selectFastgptEventLogTotalByRoleIdAndType(total);
+    }
+
+    @Override
+    public FastgptEventLogTotal selectFastgptEventTokenLogTotalByRoleIdAndType(FastGptEventTokenLog tokenLog) {
+        return fastgptEventLogTotalMapper.selectFastgptEventTokenLogTotalByRoleIdAndType(tokenLog);
+    }
+
+    @Override
+    public FastgptEventLogTotalVo totalFastgptEventLog(List<FastgptEventLogTotalVo> list) {
+        FastgptEventLogTotalVo totalVo = new FastgptEventLogTotalVo();
+        totalVo.setRoleName("总计");
+        Map<Integer, Long> totalMap = new HashMap<>();
+        if(list != null && !list.isEmpty()){
+            list.forEach(m -> {
+                m.getTypeCountMap().forEach((k,v) -> {
+                    if(totalMap.containsKey(k)){
+                        totalMap.put(k, totalMap.get(k) + v);
+                    }else{
+                        totalMap.put(k, v);
+                    }
+                });
+            });
+            totalVo.setTypeCountMap(totalMap);
+            return totalVo;
+        }else{
+            return null;
+        }
+    }
+
+    @Override
+    public List<FastgptEventLogTotalVo> selectFastgptEventLogTotalVoInfoList(FastgptEventLogTotalVo fastgptEventLogTotal) {
+        if(fastgptEventLogTotal.getBeginTime() == null || fastgptEventLogTotal.getEndTime() == null){
+            fastgptEventLogTotal.setBeginTime(DateUtil.offsetDay(DateUtil.parse(DateUtils.getDate()), -10).toDateStr());
+            fastgptEventLogTotal.setEndTime(DateUtils.getDate());
+        }
+        String appKey = fastgptEventLogTotal.getAppKey();
+        if(appKey != null){
+            List<String> roleIds = fastGptRoleService.selectFastGptRoleRoleIdsByAppKey(appKey);
+            if(roleIds != null && !roleIds.isEmpty()){
+                fastgptEventLogTotal.setRoleIds(roleIds);
+                fastgptEventLogTotal.setRoleId(null);
+            }else{
+                fastgptEventLogTotal.setRoleId(0L);
+            }
+        }
+
+        if(fastgptEventLogTotal.getUserIds() != null && !fastgptEventLogTotal.getUserIds().isEmpty()){
+            List<String> userIds = fastgptEventLogTotal.getUserIds();
+            String result = String.join(",", userIds);
+            List<CompanyUser> companyUserList = companyService.selectCompanyListByIds(result);
+            String qwUserIds = companyUserList.stream()
+                    .map(CompanyUser::getQwUserId)
+                    .filter(Objects::nonNull) // 过滤掉 null 的 qwUserId
+                    .flatMap(userId -> Arrays.stream(userId.split(","))) // 安全 split
+                    .map(String::trim)
+                    .distinct() // 去重
+                    .collect(Collectors.joining(",")); // 收集为逗号分隔的字符串
+            fastgptEventLogTotal.setQwUserIds(qwUserIds);
+        }
+        List<FastgptEventLogTotal> totalList = fastgptEventLogTotalMapper.selectFastgptEventLogTotalVoInfoList(fastgptEventLogTotal);
+        List<SysDictData> dictCache = getDictCache("sys_fastgpt_event_log_type");
+        if(dictCache == null){
+            return null;
+        }
+        List<FastgptEventLogTotalVo> totalVoList = new ArrayList<>();
+        List<Integer> typeList = new ArrayList<>();
+        dictCache.forEach(n -> typeList.add(Integer.parseInt(n.getDictValue())));
+        if(totalList != null && !totalList.isEmpty()){
+            Map<String, List<FastgptEventLogTotal>> groupedByDateStr = totalList.stream()
+                    .collect(Collectors.groupingBy(FastgptEventLogTotal::getStatTime));
+
+            List<Long> roleIds = totalList.stream().map(FastgptEventLogTotal::getRoleId).collect(Collectors.toList());
+            List<FastGptRole> fastGptRoles = fastGptRoleService.selectFastGptRoleByRoleIds(roleIds);
+
+            for (String date : groupedByDateStr.keySet()) {
+                List<FastgptEventLogTotal> list = groupedByDateStr.get(date);
+                Map<Long, List<FastgptEventLogTotal>> roleIdGrouped = list.stream()
+                        .collect(Collectors.groupingBy(FastgptEventLogTotal::getRoleId));
+                for (Long roleId : roleIdGrouped.keySet()) {
+                    List<FastgptEventLogTotal> logTotalList = roleIdGrouped.get(roleId);
+                    FastgptEventLogTotalVo eventLogTotalVo = new FastgptEventLogTotalVo();
+                    eventLogTotalVo.setRoleId(roleId);
+                    eventLogTotalVo.setStatTime(date);
+                    List<Integer> types = logTotalList.stream().map(FastgptEventLogTotal::getType).collect(Collectors.toList());
+                    typeList.forEach(m -> logTotalList.forEach(n ->{
+                        if(types.contains(m)){
+                            if(eventLogTotalVo.getTypeCountMap() == null){
+                                Map<Integer, Long> map = new HashMap<>();
+                                map.put(n.getType(), n.getCount());
+                                eventLogTotalVo.setTypeCountMap(map);
+                            }else{
+                                Map<Integer, Long> countMap = eventLogTotalVo.getTypeCountMap();
+                                countMap.put(n.getType(), n.getCount());
+                                eventLogTotalVo.setTypeCountMap(countMap);
+                            }
+                        }else{
+                            if(eventLogTotalVo.getTypeCountMap() == null){
+                                Map<Integer, Long> map = new HashMap<>();
+                                map.put(m, 0L);
+                                eventLogTotalVo.setTypeCountMap(map);
+                            }else{
+                                Map<Integer, Long> countMap = eventLogTotalVo.getTypeCountMap();
+                                countMap.put(m, 0L);
+                                eventLogTotalVo.setTypeCountMap(countMap);
+                            }
+                        }
+                    }));
+                    for (FastGptRole fastGptRole : fastGptRoles) {
+                        if(Objects.equals(fastGptRole.getRoleId(), eventLogTotalVo.getRoleId())){
+                            eventLogTotalVo.setRoleName(fastGptRole.getRoleName());
+                        }
+                    }
+
+                    totalVoList.add(eventLogTotalVo);
+                }
+            }
+            totalVoList.sort(Comparator.comparing(FastgptEventLogTotalVo::getStatTime).thenComparing(FastgptEventLogTotalVo::getRoleId).reversed());
+            return totalVoList;
+        }else{
+            return null;
+        }
+    }
+
+    @Override
+    public List<FastGptEventTokenLog> selectFastgptEventTokenLogTotalList(FastGptEventTokenLog fastGptEventTokenLog) {
+        return fastgptEventLogTotalMapper.selectFastgptEventTokenLogTotalList(fastGptEventTokenLog);
+    }
+
+
+
+    public static List<String> generateDateStrings(String startDateStr, int days) {
+        List<String> dateList = new ArrayList<>();
+        for (int i = 0; i < days; i++) {
+            String dateStr = DateUtil.offsetDay(DateUtil.parse(startDateStr), i).toDateStr();
+            dateList.add(dateStr);
+        }
+        return dateList;
+    }
+
+    /**
+     * 批量新增ai事件埋点统计
+     *
+     * @param fastgptEventLogTotalList ai事件埋点统计列表
+     * @return 结果
+     */
+    @Override
+    public int insertFastgptEventLogTotalBatch(List<FastgptEventLogTotal> fastgptEventLogTotalList) {
+        return baseMapper.insertFastgptEventLogTotalBatch(fastgptEventLogTotalList);
+    }
+
+    /**
+     * 批量修改ai事件埋点统计
+     *
+     * @param fastgptEventLogTotalList ai事件埋点统计列表
+     * @return 结果
+     */
+    @Override
+    public int updateFastgptEventLogTotalBatch(List<FastgptEventLogTotal> fastgptEventLogTotalList) {
+        return baseMapper.updateFastgptEventLogTotalBatch(fastgptEventLogTotalList);
+    }
+
+    @Override
+    public List<FastgptEventLogTotalVo> selectFastgptEventLogTotalListByStatTime(String dateTime) {
+        return fastgptEventLogTotalMapper.selectFastgptEventLogTotalListByStatTime(dateTime);
+    }
+
+
+}

+ 19 - 0
fs-service/src/main/java/com/fs/fastGpt/vo/FastGptRoleDataVO.java

@@ -0,0 +1,19 @@
+package com.fs.fastGpt.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class FastGptRoleDataVO implements Serializable {
+    /**
+     * 节点名称
+     */
+    private Long roleId;
+
+    private String roleName;
+
+    private List<FastGptRoleDataVO> roleList;
+
+}

+ 70 - 0
fs-service/src/main/java/com/fs/fastGpt/vo/FastgptEventLogTotalVo.java

@@ -0,0 +1,70 @@
+package com.fs.fastGpt.vo;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ai事件埋点统计对象 fastgpt_event_log_total
+ *
+ * @author fs
+ * @date 2025-06-26
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FastgptEventLogTotalVo extends BaseEntity{
+
+    /** $column.columnComment */
+    private Long id;
+
+    /** 角色id */
+    //@Excel(name = "角色id")
+    private Long roleId;
+
+    //@Excel(name = "角色名称")
+    private String roleName;
+
+    @Excel(name = "日志统计时间")
+    private String statTime;
+
+    /** 公司id */
+    @Excel(name = "公司id")
+    private Long companyId;
+
+    @Excel(name = "公司名称")
+    private String companyName;
+    /** 数量 */
+    @Excel(name = "token数量")
+    private Long count;
+
+    /** 日志类型 */
+    //@Excel(name = "日志类型")
+    private Integer type;
+
+
+
+    /** 公司用户id */
+    //@Excel(name = "公司用户id")
+    private Long companyUserId;
+
+    /** 企微用户id */
+    //@Excel(name = "企微用户id")
+    private Long qwUserId;
+
+
+    private Map<Integer,Long> typeCountMap;
+
+    private String qwUserIds;
+
+    private List<String> userIds;
+
+    private String appKey;
+
+    private List<String> roleIds;
+
+
+}

+ 110 - 8
fs-service/src/main/java/com/fs/fastgptApi/util/AiImgUtil.java

@@ -4,18 +4,18 @@ import com.alibaba.fastjson.JSON;
 import com.fs.fastgptApi.param.DouBaoAiParam;
 import com.fs.fastgptApi.result.AiImgResult;
 import com.fs.qw.domain.QwUser;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
+import java.io.*;
 import java.net.HttpURLConnection;
+import java.net.SocketTimeoutException;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
+@Slf4j
 @Service
 public class AiImgUtil {
 
@@ -98,8 +98,9 @@ public class AiImgUtil {
             textContent.setText(
                     "识别图片内容 \n" +
                             "情况一:图片为表情包的时候或是明确意义图片的时候,单独提取出表情包的含义为图片,并输出:【表情包:XXX】XXX为表情表达的内容,例如这个表情包是很开心的感谢,那么XXX就是谢谢。在【】外不进行其他的解释直接结束 \n" +
-                            "情况二:图片是舌头的时候,根据他的舌苔进行简单的分析,直接输出 \n" +
-                            "情况三:图片是其他的时候,正常提取图片内容,如果是身体异常部位要进行简单分析,直接输出,如果是卡通图片,需要在结尾输出【这是卡通图片】这几个字");
+                            "情况二:图片是舌头的时候,根据他的舌苔进行简单的分析,直接输出,如果是张开嘴的时候,对舌下进行简单的舌诊分析 \n" +
+                            "情况三:图片是其他的时候,正常提取图片内容,如果是身体异常部位要进行简单分析,直接输出,如果是卡通图片,需要在结尾输出【这是卡通图片】这几个字\n" +
+                            "情况四:图片如果是微信支付界面,如果最下方的一条信息为收款金额,那么输出【用户完课,领取红包】,如果最下方的一条信息为支付成功,那么输出【用户购买产品,支付成功】");
 
 
             List<DouBaoAiParam.Content> contents = new ArrayList<>();
@@ -147,9 +148,110 @@ public class AiImgUtil {
         }
     }
 
+    /**
+     * 发送AI图像生成请求
+     * @param requestBody 请求体(JSON字符串)
+     * @return 响应结果(JSON字符串)
+     * @throws IOException 网络请求异常
+     */
+    public String sendAiImgHttpRequest(String requestBody) throws IOException {
+        long startTime = System.currentTimeMillis();
+        log.info("开始发送AI图像请求,URL: " + "https://ark.cn-beijing.volces.com/api/v3/chat/completions" + ",请求体长度: " + requestBody.length());
+
+        HttpURLConnection connection = null;
+        try {
+            // 1. 创建连接
+            URL url = new URL("https://ark.cn-beijing.volces.com/api/v3/chat/completions");
+            connection = (HttpURLConnection) url.openConnection();
+
+            // 2. 配置连接参数
+            configureConnection(connection);
+
+            // 3. 发送请求体
+            sendRequestBody(connection, requestBody);
+
+            // 4. 处理响应
+            int statusCode = connection.getResponseCode();
+            log.info("AI图像请求完成,耗时: " + (System.currentTimeMillis() - startTime) +
+                    "ms,状态码: " + statusCode);
+
+            if (statusCode == HttpURLConnection.HTTP_OK) {
+                return readResponse(connection.getInputStream());
+            } else {
+                String errorResponse = readResponse(connection.getErrorStream());
+                String errorMsg = "请求失败,状态码: " + statusCode + ",响应体: " + errorResponse;
+                log.error(errorMsg);
+                throw new IOException(errorMsg);
+            }
+        } catch (SocketTimeoutException e) {
+            // 区分超时类型(连接超时/读取超时)
+            String timeoutType = e.getMessage().contains("connect") ? "连接" : "读取";
+            String errorMsg = timeoutType + "超时,耗时: " + (System.currentTimeMillis() - startTime) + "ms";
+            log.error(errorMsg);
+            throw new IOException(errorMsg, e);
+        } catch (IOException e) {
+            log.error("请求发生异常,耗时: " + (System.currentTimeMillis() - startTime) + "ms", e);
+            throw e;
+        } finally {
+            // 确保连接关闭
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+    /**
+     * 配置HTTP连接参数
+     */
+    private void configureConnection(HttpURLConnection connection) {
+        try {
+            connection.setRequestMethod("POST");
+            connection.setRequestProperty("Authorization", "Bearer " + "208d3549-8dc9-4ef6-b3fa-5aa358f1ab20");
+            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+            connection.setRequestProperty("Accept", "application/json");
 
+            // 基础配置
+            connection.setDoOutput(true);
+            connection.setDoInput(true);
+            connection.setUseCaches(false);
+            connection.setConnectTimeout(10000);
+            connection.setReadTimeout(30000);
+        } catch (Exception e) {
+            log.error("配置连接参数失败: " + e.getMessage());
+        }
+    }
 
-    private  String sendAiImgHttpRequest(String requestBody) throws IOException {
+    /**
+     * 发送请求体
+     */
+    private void sendRequestBody(HttpURLConnection connection, String requestBody) throws IOException {
+        try (OutputStream os = connection.getOutputStream()) {
+            byte[] input = requestBody.getBytes(StandardCharsets.UTF_8);
+            os.write(input, 0, input.length);
+            os.flush();
+        }
+    }
+
+    /**
+     * 读取响应流
+     */
+    private String readResponse(InputStream inputStream) throws IOException {
+        if (inputStream == null) {
+            return "";
+        }
+
+        try (BufferedReader br = new BufferedReader(
+                new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+            StringBuilder response = new StringBuilder();
+            String line;
+            while ((line = br.readLine()) != null) {
+                response.append(line);
+            }
+            return response.toString();
+        }
+    }
+
+    /*private  String sendAiImgHttpRequest(String requestBody) throws IOException {
         HttpURLConnection connection = null;
         try {
             // 配置连接
@@ -196,5 +298,5 @@ public class AiImgUtil {
                 connection.disconnect();
             }
         }
-    }
+    }*/
 }

+ 2 - 2
fs-service/src/main/java/com/fs/fastgptApi/util/EventLogUtils.java

@@ -79,8 +79,8 @@ public class EventLogUtils {
         fastGptEventLog.setCreateTime(new Date());
 
 
-        //EventLogQueue.addEventLog(fastGptEventLog); // 入队
-        //fastGptChatMsgService.insertFastGptEventLog(fastGptEventLog);
+        EventLogQueue.addEventLog(fastGptEventLog); // 入队
+        fastGptChatMsgService.insertFastGptEventLog(fastGptEventLog);
     }
 
     /**

+ 20 - 1
fs-service/src/main/java/com/fs/gtPush/domain/PushReqBean.java

@@ -1,5 +1,6 @@
 package com.fs.gtPush.domain;
 
+import com.google.common.collect.ImmutableMap;
 import lombok.Data;
 
 import javax.security.sasl.SaslServer;
@@ -9,8 +10,26 @@ import java.util.Map;
 @Data
 public class PushReqBean implements Serializable {
 
-    private Object cids;
+    public PushReqBean(Object push_clientid, String title, String content,Map<String, Object> payload,Map<String, Object> options) {
+        this.push_clientid = push_clientid;
+        this.title = title;
+        this.content = content;
+        this.payload = payload;
+        this.options = options;
+        this.force_notification=false;
+        this.category= ImmutableMap.of("harmony","EXPRESS", "huawei","EXPRESS", "vivo","ORDER");
+
+    }
+
+    private Object push_clientid;
     private String title;
     private String content;
+    private boolean force_notification;
+    private Map<String,String> category;
     private Map<String,Object> payload;
+    private Map<String,Object> options;
+    private String badge;
+
+    public PushReqBean() {
+    }
 }

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

@@ -2,19 +2,112 @@ package com.fs.gtPush.service.impl;
 
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.gtPush.domain.PushReqBean;
 import com.fs.gtPush.domain.PushResult;
+import com.fs.gtPush.domain.UniPushLog;
+import com.fs.gtPush.service.UniPushLogService;
 import com.fs.gtPush.service.uniPush2Service;
+import com.fs.gtPush.utils.PushUtils;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.HashMap;
+import java.util.Map;
+
 @Service
 public class uniPush2ServiceImpl implements uniPush2Service {
     private static final String url = "https://fc-mp-de6e03a9-c1a3-439b-9eec-d0dc3c565e4e.next.bspapp.com/push";
+
+    @Autowired
+    private IFsUserService userService;
+
+    @Autowired
+    private UniPushLogService logService;
+
     @Override
     public PushResult pushMessage(PushReqBean push) {
         String result = HttpUtil.post(url, push.toString());
         PushResult pushResult = JSONUtil.toBean(result, PushResult.class);
         return pushResult;
     }
+
+    @Override
+    public void pushOne(Long userId, Long businessId, String purl, String title, String content, Float type, Integer desType) {
+        PushReqBean param = getParam(userId, purl,title,content,type,desType,"");
+        PushResult pushResult = null;
+        UniPushLog pushAddLog = new UniPushLog();
+        if (param != null) {
+            pushResult = pushMessage(param); //推送消息
+            pushAddLog.setJpushId(param.getPush_clientid().toString());
+            pushAddLog.setPushMsg(JSON.toJSONString(param));
+            pushAddLog.setType(type);
+            pushAddLog.setDesType(desType);
+            pushAddLog.setUserId(userId);
+            pushAddLog.setBusinessId(businessId);
+            pushAddLog.setCreateTime(DateUtils.getNowDate());
+            UniPushLog uniPushLog = PushUtils.returnMsg(pushResult,pushAddLog);
+            logService.insertUniPushLog(uniPushLog);
+        }
+    }
+
+    @Override
+    public PushReqBean getParam(Long userId,String purl,String title,String content,Float type,Integer desType,String imJsonString){
+        if (userId !=null) {
+            FsUser fsUser = userService.selectFsUserByUserId(userId);
+            if (fsUser !=null){
+                String jpushId = fsUser.getJpushId();
+                if (StringUtils.isNotBlank(jpushId) && !"0".equals(jpushId) && !"string".equals(jpushId)){
+                    HashMap<String, Object> map = new HashMap<>();
+                    HashMap<String, Object> map2 = new HashMap<>();
+                    if (StringUtils.isNotBlank(purl)){
+                        map.put("url",purl);
+                        if (StringUtils.isNotEmpty(imJsonString)){
+                            map.put("data",imJsonString);
+                        }
+                    }
+                    if (content.length()>50 || title.length()>20){
+                        return null;//通知栏标题,长度小于20;通知栏内容,长度小于50
+                    }
+                    Map<String, Object> xmConfig = new HashMap<>();
+                    Map<String, Object> vvConfig = new HashMap<>();
+
+                    if (type!=null){
+                        String channel = "";
+                        if (type.toString().contains("0")){
+                            channel = "113892"; //订单
+                        } else if (type.toString().contains("1")){
+                            channel = "113891";
+                            vvConfig.put("/classification",0);
+                        } else if (type.toString().contains("2")){
+                            channel = "113791"; //系统类
+                            vvConfig.put("/classification",1);
+                        }
+                        if (StringUtils.isNotBlank(channel)){
+                            xmConfig.put("/extra.channel_id", channel);
+                            map2.put("XM", xmConfig);
+                        }
+                        vvConfig.put("/category","IM");
+                        map2.put("VV",vvConfig);
+
+                    }
+                    return new PushReqBean(
+                            jpushId,
+                            title,
+                            content,
+                            map,
+                            map2
+                    );
+                }
+            }
+        }
+        return null;
+    }
+
+
 }

+ 4 - 0
fs-service/src/main/java/com/fs/gtPush/service/uniPush2Service.java

@@ -6,4 +6,8 @@ import com.fs.gtPush.domain.PushResult;
 
 public interface uniPush2Service {
     PushResult pushMessage(PushReqBean push);
+
+    void pushOne(Long userId,Long businessId,String purl,String title,String content, Float type, Integer desType);
+
+    PushReqBean getParam(Long userId,String purl,String title,String content,Float type,Integer desType,String imJsonString);
 }

+ 120 - 0
fs-service/src/main/java/com/fs/gtPush/utils/PushUtils.java

@@ -0,0 +1,120 @@
+package com.fs.gtPush.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.fs.gtPush.domain.PushResult;
+import com.fs.gtPush.domain.UniPushLog;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+@Slf4j
+public class PushUtils {
+
+    /**
+     * 推送单个 返回结果解析
+     * @param pushResult
+     * @param addLog
+     */
+    public static UniPushLog returnMsg(PushResult pushResult, UniPushLog addLog) {
+        if(pushResult != null){
+            if ("success".equals(pushResult.getErrMsg())){
+                Map<String, Object> dataMap = JSON.parseObject(JSON.toJSONString(pushResult.getData()), new TypeReference<Map<String, Object>>() {});
+                if (dataMap != null) {
+                    for (Map.Entry<String, Object> outerEntry : dataMap.entrySet()) {
+                        String outerKey = outerEntry.getKey(); // 外层键
+                        Map<String, String> innerMap = (Map<String, String>) outerEntry.getValue(); // 内层 Map
+                        log.info("Outer Key: " + outerKey);
+                        // 遍历内层 Map
+                        for (Map.Entry<String, String> innerEntry : innerMap.entrySet()) {
+                            String innerKey = innerEntry.getKey();
+                            String innerValue = innerEntry.getValue();
+                            log.info("    Inner Key: " + innerKey + ", Inner Value: " + innerValue);
+                            //匹配对应log (因不确定返回值是否按序排列 )
+                            if (addLog.getJpushId().equals(innerKey)) {
+                                addLog.setReturnMsg(innerValue);
+                                if (innerValue.contains("successed")) {
+                                    addLog.setPushRes(1);
+                                } else {
+                                    addLog.setPushRes(0);
+                                }
+                            }
+                        }
+                    }
+                }
+
+            } else {
+                    addLog.setPushRes(0);
+                    addLog.setReturnMsg(pushResult.getErrMsg());
+            }
+        } else {
+            addLog.setPushRes(0);
+        }
+        return addLog;
+    }
+
+    /**
+     * 推送列表 返回结果解析
+     * @param pushResult
+     * @param addLogs
+     */
+    public static ArrayList<UniPushLog> returnArrayMsg(PushResult pushResult, ArrayList<UniPushLog> addLogs) {
+        if(pushResult != null){
+            if ("success".equals(pushResult.getErrMsg())){
+                Map<String, Object> dataMap = JSON.parseObject(JSON.toJSONString(pushResult.getData()), new TypeReference<Map<String, Object>>() {});
+                if (dataMap != null) {
+
+                    for (Map.Entry<String, Object> outerEntry : dataMap.entrySet()) {
+                        String outerKey = outerEntry.getKey(); // 外层键
+                        Map<String, String> innerMap = (Map<String, String>) outerEntry.getValue(); // 内层 Map
+                        log.info("Outer Key: " + outerKey);
+                        // 遍历内层 Map
+                        int i = 0;
+                        for (Map.Entry<String, String> innerEntry : innerMap.entrySet()) {
+                            String innerKey = innerEntry.getKey();
+                            String innerValue = innerEntry.getValue();
+                            log.info("    Inner Key: " + innerKey + ", Inner Value: " + innerValue);
+                            if (addLogs.get(i).getJpushId().equals(innerKey)) {
+                                if (innerValue.contains("successed")){
+                                    addLogs.get(i).setPushRes(1);
+
+                                } else {
+                                    addLogs.get(i).setPushRes(0);
+                                }
+                                addLogs.get(i).setReturnMsg(innerValue);
+                            } else {
+                                //匹配对应log (因不确定返回值是否按序排列 )
+                                for (UniPushLog addLog : addLogs) {
+                                    if (addLog.getJpushId().equals(innerKey)) {
+                                        addLogs.get(i).setReturnMsg(innerValue);
+                                        if (innerValue.contains("successed")) {
+                                            addLog.setPushRes(1);
+                                        } else {
+                                            addLog.setPushRes(0);
+                                        }
+                                        addLogs.get(i).setReturnMsg(innerValue);
+                                        break;
+                                    }
+                                }
+                            }
+                            i++;
+                        }
+                    }
+                }
+
+            } else {
+                addLogs.forEach(log->{
+                    log.setPushRes(0);
+                    log.setReturnMsg(pushResult.getErrMsg());
+                });
+            }
+
+        } else {
+            addLogs.forEach(log->{
+                log.setPushRes(0);
+            });
+        }
+        return addLogs;
+    }
+}

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

@@ -70,5 +70,11 @@ public class FsFirstDiagnosis extends BaseEntity{
     @Excel(name = "医生证号")
     private String doctorCertificate;
 
+    /** 医生是否填写:0-未填写 1-已填写 */
+    private Integer doctorStatus;
 
+    /** 用户是否答复:0-未答复 1-已答复 */
+    private Integer userStatus;
+
+    private Long qwUserId;
 }

+ 12 - 0
fs-service/src/main/java/com/fs/his/dto/FindUsersByDTO.java

@@ -0,0 +1,12 @@
+package com.fs.his.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class FindUsersByDTO implements Serializable {
+    private Long id;
+    private Long type;
+    private String name;
+}

+ 35 - 0
fs-service/src/main/java/com/fs/his/enums/PushLogDesTypeEnum.java

@@ -0,0 +1,35 @@
+package com.fs.his.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.stream.Stream;
+
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public enum PushLogDesTypeEnum {
+    ORDER_UN_PAY(0,"订单未支付"),
+    ORDER_SUCCESS_PAY(1,"订单支付成功"),
+    ORDER_SEND(2,"订单已发货"),
+    ORDER_DISPATCH(3,"订单派送中"),
+    ORDER_SIGN(4,"订单已签收"),
+    HEALTH_RECEIVE(5,"接诊提醒"),
+    IM_MSG(6,"消息提醒"),
+    STUDY_COURSE(7,"课程学习提醒"),
+    MARKET_PUSH(8,"营销推送提醒");
+
+
+
+
+    private Integer value;
+    private String desc;
+
+    public static PushLogDesTypeEnum toType(int value) {
+        return Stream.of(PushLogDesTypeEnum.values())
+                .filter(p -> p.value == value)
+                .findAny()
+                .orElse(null);
+    }
+}

+ 34 - 0
fs-service/src/main/java/com/fs/his/enums/PushLogTypeEnum.java

@@ -0,0 +1,34 @@
+package com.fs.his.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.stream.Stream;
+
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public enum PushLogTypeEnum {
+    ORDER(0f,"订单通知"),
+    ORDER_STORE(0.1f,"药品订单通知"),
+    ORDER_PACKAGE(0.2f,"套餐包订单通知"),
+    ORDER_INQUIRY(0.3f,"问诊订单通知"),
+    ORDER_INTEGRAL(0.4f,"积分订单通知"),
+    HEALTH(1f,"健康管理类通知"),
+    MARKET(2f,"营销类通知"),
+    COURSE(3f,"课程类通知");
+
+
+
+
+    private Float value;
+    private String desc;
+
+    public static PushLogTypeEnum toType(int value) {
+        return Stream.of(PushLogTypeEnum.values())
+                .filter(p -> p.value == value)
+                .findAny()
+                .orElse(null);
+    }
+}

+ 53 - 0
fs-service/src/main/java/com/fs/his/mapper/FsFirstDiagnosisMapper.java

@@ -2,7 +2,15 @@ package com.fs.his.mapper;
 
 import java.util.List;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.param.FsFirstDiagnosisListUParam;
+import com.fs.course.vo.FsFirstDiagnosisListUVO;
 import com.fs.his.domain.FsFirstDiagnosis;
+import com.fs.his.param.FsDiagnosisListDParam;
+import com.fs.his.param.FsFirstDiagnosisParam;
+import com.fs.his.vo.FsDiagnosisListDVO;
+import com.fs.his.vo.FsFirstDiagnosisVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 
 /**
  * 初诊单Mapper接口
@@ -65,4 +73,49 @@ public interface FsFirstDiagnosisMapper extends BaseMapper<FsFirstDiagnosis>{
      * @return 初诊单数据
      */
     FsFirstDiagnosis getByUserId(Long userId);
+
+    @Select({"<script> " +
+            "SELECT id,doctor_id,patient_name,gender,age,phone,date_time,physical_condition,doctor_status,user_status FROM fs_first_diagnosis " +
+            "WHERE 1 = 1" +
+            "<if test='maps.type == 0' > and  doctor_status = 0 and doctor_id is null</if>" +
+            "<if test='maps.type == 1' > and  doctor_id = #{maps.doctorId}</if>" +
+            "ORDER BY create_time desc" +
+            "</script>"})
+    List<FsDiagnosisListDVO> selectFsDiagnosisListDVO(@Param("maps") FsDiagnosisListDParam param);
+
+    @Select({"<script>" +
+            "SELECT fd.*,u.qw_user_name,d.sign_url FROM fs_first_diagnosis fd " +
+            " LEFT JOIN qw_user u ON u.id = fd.qw_user_id " +
+            " LEFT JOIN fs_doctor d ON d.doctor_id = fd.doctor_id " +
+            " WHERE fd.doctor_status = 1 " +
+            "<if test='param.doctorStatus != null'> AND fd.doctor_status = #{param.doctorStatus} </if>" +
+            "<if test='param.userId != null'> AND fd.user_id = #{param.userId} </if>" +
+            " ORDER BY fd.create_time desc " +
+            "</script>"})
+    List<FsFirstDiagnosisListUVO> selectFsFirstDiagnosisListUVO(@Param("param") FsFirstDiagnosisListUParam param);
+
+    @Select({"<script>" +
+            "SELECT fd.*,u.qw_user_name,d.sign_url FROM fs_first_diagnosis fd " +
+            "LEFT JOIN qw_user u ON u.id = fd.qw_user_id " +
+            " LEFT JOIN fs_doctor d ON d.doctor_id = fd.doctor_id " +
+            "WHERE fd.id = #{id}" +
+            "</script>"})
+    FsFirstDiagnosisListUVO getInfo(Long id);
+
+    @Select({"<script>" +
+            "SELECT fd.*,u.nick_name userName,qu.qw_user_name FROM fs_first_diagnosis fd  " +
+            " LEFT JOIN fs_user u ON u.user_id = fd.user_id " +
+            " LEFT JOIN qw_user qu ON qu.id = fd.qw_user_id " +
+            " WHERE 1 = 1 " +
+            "<if test='param.doctorStatus != null'> AND fd.doctor_status = #{param.doctorStatus} </if>" +
+            "<if test='param.userStatus != null'> AND fd.user_status = #{param.userStatus} </if>" +
+            "<if test='param.doctorCertificate != null'> AND fd.doctor_certificate = #{param.doctorCertificate} </if>" +
+            "<if test='param.phone != null'> AND fd.phone = like concat('%',#{param.phone},'%') </if>" +
+            "<if test='param.patientName != null'> and fd.patient_name like concat('%',#{param.patientName},'%') </if>" +
+            "<if test='param.doctorName != null'> and fd.doctor_name like concat('%',#{param.doctorName},'%') </if>" +
+            "<if test='param.userName != null'> and u.nick_name  like concat('%',#{param.userName},'%') </if>" +
+            "<if test='param.qwUserName != null'> and qu.qw_user_name like concat('%',#{param.qwUserName},'%') </if>" +
+            " ORDER BY fd.create_time desc " +
+            "</script>"})
+    List<FsFirstDiagnosisVO> selectFsFirstDiagnosisVOList(@Param("param") FsFirstDiagnosisParam param);
 }

+ 25 - 0
fs-service/src/main/java/com/fs/his/mapper/FsInquiryPatientInfoMapper.java

@@ -1,9 +1,14 @@
 package com.fs.his.mapper;
 
+import com.fs.course.param.FsInquiryPatientInfoUParam;
+import com.fs.course.vo.FsInquiryPatientInfoListUVO;
 import com.fs.his.domain.FsInquiryPatientInfo;
 import com.fs.his.vo.FsInquiryPatientVO;
+import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
+import java.util.List;
+
 public interface FsInquiryPatientInfoMapper {
 
     int insertFsInquiryPatientInfo(FsInquiryPatientInfo fsInquiryPatientInfo);
@@ -13,4 +18,24 @@ public interface FsInquiryPatientInfoMapper {
     FsInquiryPatientInfo selectFsInquiryPatientInfoInquiryOrderId(Long inquiryOrderId);
 
     FsInquiryPatientVO selectUserAndCompanyUserByInquiryOrderId(Long inquiryOrderId);
+
+    @Select({"<script>" +
+            "SELECT pi.*,p.patient_name,p.birthday,p.sex, u.nick_name companyUserName,d.doctor_name FROM fs_inquiry_patient_info pi " +
+            "LEFT JOIN fs_patient p ON p.patient_id  = pi.patient_id " +
+            "LEFT JOIN company_user u ON u.user_id = pi.company_user_id " +
+            " left join fs_doctor d on d.doctor_id = pi.sub_doctor_id  " +
+            " 1=1 " +
+            "<if test='param.userId != null'> AND pi.user_id = #{param.userId} </if> " +
+            "ORDER BY pi.create_time desc " +
+            "</script>"})
+    List<FsInquiryPatientInfoListUVO> selectFsInquiryPatientInfoListUVO(@Param("param") FsInquiryPatientInfoUParam param);
+
+    @Select({"<script>" +
+            "SELECT pi.*,p.patient_name,p.birthday,p.sex, u.nick_name companyUserName,d.doctor_name FROM fs_inquiry_patient_info pi " +
+            "LEFT JOIN fs_patient p ON p.patient_id  = pi.patient_id " +
+            "LEFT JOIN company_user u ON u.user_id = pi.company_user_id " +
+            " left join fs_doctor d on d.doctor_id = pi.sub_doctor_id  " +
+            "WHERE pi.id = #{id} " +
+            "</script>"})
+    FsInquiryPatientInfoListUVO getInfo(Long id);
 }

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

@@ -8,6 +8,8 @@ import com.fs.course.domain.FsUserWatchStatistics;
 import com.fs.course.param.CourseAnalysisParam;
 import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
 import com.fs.his.domain.FsUser;
+import com.fs.his.dto.FindUsersByDTO;
+import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserParam;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.FsUserExportListVO;
@@ -395,4 +397,6 @@ public interface FsUserMapper
     List<FsCompanyUserListQueryVO> selectFsCompanyUserListQuery(@Param("maps")FsUser fsUser);
 
     List<FsUser> selectFsUserListLimit(FsUser fsUser);
+
+    List<FindUsersByDTO> findUsersByParam(FindUserByParam param);
 }

+ 12 - 0
fs-service/src/main/java/com/fs/his/param/FindUserByParam.java

@@ -0,0 +1,12 @@
+package com.fs.his.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class FindUserByParam implements Serializable {
+    private Integer pageNum;
+    private Integer pageSize;
+    private String keywords;
+}

+ 17 - 0
fs-service/src/main/java/com/fs/his/param/FsDiagnosisFillDParam.java

@@ -0,0 +1,17 @@
+package com.fs.his.param;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class FsDiagnosisFillDParam {
+    private Long id;
+    private String firstDiagnosis;
+    private String physicalCondition;
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date dateTime;
+    private Long doctorId;
+
+}

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

@@ -0,0 +1,13 @@
+package com.fs.his.param;
+
+import lombok.Data;
+
+@Data
+public class FsDiagnosisListDParam extends BaseParam{
+    Long doctorId;
+
+    /**
+     * 0-全部 1-我的
+     */
+    private Integer type;
+}

+ 25 - 0
fs-service/src/main/java/com/fs/his/param/FsFirstDiagnosisParam.java

@@ -0,0 +1,25 @@
+package com.fs.his.param;
+
+import lombok.Data;
+
+@Data
+public class FsFirstDiagnosisParam extends BaseParam{
+
+    private String patientName;
+
+    private String phone;
+
+    private String doctorName;
+
+    private String qwUserName;
+
+    private String userName;
+
+    private String doctorCertificate;
+
+    /** 医生是否填写:0-未填写 1-已填写 */
+    private Integer doctorStatus;
+
+    /** 用户是否答复:0-未答复 1-已答复 */
+    private Integer userStatus;
+}

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

@@ -9,4 +9,6 @@ public class FsInquiryOrderDoPayParam implements Serializable {
     Long orderId;
     Long userId;
 
+    private String appId;
+
 }

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

@@ -10,4 +10,6 @@ public class FsPackageOrderDoPayParam implements Serializable {
     @NotNull(message = "订单号不能为空")
     Long orderId;
     Long userId;
+
+    private String appId;
 }

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

@@ -10,4 +10,6 @@ public class FsStoreOrderDoPayParam implements Serializable {
     @NotNull(message = "订单号不能为空")
     Long orderId;
     Long userId;
+
+    private String appId;
 }

+ 26 - 5
fs-service/src/main/java/com/fs/his/service/IFsFirstDiagnosisService.java

@@ -2,7 +2,15 @@ package com.fs.his.service;
 
 import java.util.List;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.course.param.FsFirstDiagnosisListUParam;
+import com.fs.course.vo.FsFirstDiagnosisListUVO;
 import com.fs.his.domain.FsFirstDiagnosis;
+import com.fs.his.param.FsDiagnosisFillDParam;
+import com.fs.his.param.FsDiagnosisListDParam;
+import com.fs.his.param.FsFirstDiagnosisParam;
+import com.fs.his.vo.FsDiagnosisListDVO;
+import com.fs.his.vo.FsFirstDiagnosisVO;
 
 /**
  * 初诊单Service接口
@@ -27,6 +35,8 @@ public interface IFsFirstDiagnosisService extends IService<FsFirstDiagnosis>{
      */
     List<FsFirstDiagnosis> selectFsFirstDiagnosisList(FsFirstDiagnosis fsFirstDiagnosis);
 
+    List<FsFirstDiagnosisVO> selectFsFirstDiagnosisVOList(FsFirstDiagnosisParam param);
+
     /**
      * 新增初诊单
      * 
@@ -59,10 +69,21 @@ public interface IFsFirstDiagnosisService extends IService<FsFirstDiagnosis>{
      */
     int deleteFsFirstDiagnosisById(Long id);
 
-/**
- * 根据用户ID获取首次诊断信息
- * @param userId 用户ID
- * @return 返回对应的首次诊断信息对象,如果不存在则可能返回null
- */
+    /**
+     * 根据用户ID获取首次诊断信息
+     * @param userId 用户ID
+     * @return 返回对应的首次诊断信息对象,如果不存在则可能返回null
+     */
     FsFirstDiagnosis getByUserId(Long userId);
+
+
+    List<FsDiagnosisListDVO> getDiagnosisList(FsDiagnosisListDParam param);
+
+    R fill(FsDiagnosisFillDParam param);
+
+    List<FsFirstDiagnosisListUVO> selectFsFirstDiagnosisListUVO(FsFirstDiagnosisListUParam param);
+
+    FsFirstDiagnosisListUVO getInfo(Long id);
+
+    R confirm(Long id);
 }

+ 8 - 0
fs-service/src/main/java/com/fs/his/service/IFsInquiryPatientInfoService.java

@@ -1,8 +1,13 @@
 package com.fs.his.service;
 
+import com.fs.course.param.FsInquiryPatientInfoUParam;
+import com.fs.course.vo.FsInquiryPatientInfoListUVO;
 import com.fs.his.domain.FsInquiryPatientInfo;
 import com.fs.his.param.FsInquiryPatientParam;
 import com.fs.his.vo.FsInquiryPatientVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /**
  * 问诊患者信息Service接口
@@ -16,4 +21,7 @@ public interface IFsInquiryPatientInfoService {
 
     FsInquiryPatientVO selectFsInquiryPatientInfo(Long inquiryOrderId);
 
+    List<FsInquiryPatientInfoListUVO> selectFsInquiryPatientInfoListUVO(FsInquiryPatientInfoUParam param);
+
+    FsInquiryPatientInfoListUVO getInfo(Long id);
 }

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

@@ -12,6 +12,8 @@ import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
 import com.fs.course.vo.newfs.FsCourseAnalysisVO;
 import com.fs.his.domain.FsUser;
 import com.fs.his.domain.FsUserAddress;
+import com.fs.his.dto.FindUsersByDTO;
+import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserParam;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.FsUserExportListVO;
@@ -46,6 +48,12 @@ public interface IFsUserService
      */
     public FsUser selectFsUserByUserId(Long userId);
 
+    /**
+     * 通过参数查找用户
+     * @param param 参数
+     * @return 用户列表
+     */
+    public List<FindUsersByDTO> findUserByParam(FindUserByParam param);
     /**
      * 查询用户列表
      *

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

@@ -1,13 +1,28 @@
 package com.fs.his.service.impl;
 
+import java.util.Collections;
 import java.util.List;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.course.param.FsFirstDiagnosisListUParam;
+import com.fs.course.vo.FsFirstDiagnosisListUVO;
+import com.fs.his.domain.FsDoctor;
+import com.fs.his.param.FsDiagnosisFillDParam;
+import com.fs.his.param.FsDiagnosisListDParam;
+import com.fs.his.param.FsFirstDiagnosisParam;
+import com.fs.his.service.IFsDoctorService;
+import com.fs.his.vo.FsDiagnosisListDVO;
+import com.fs.his.vo.FsFirstDiagnosisVO;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.his.mapper.FsFirstDiagnosisMapper;
 import com.fs.his.domain.FsFirstDiagnosis;
 import com.fs.his.service.IFsFirstDiagnosisService;
+import org.springframework.util.CollectionUtils;
 
 /**
  * 初诊单Service业务层处理
@@ -18,6 +33,13 @@ import com.fs.his.service.IFsFirstDiagnosisService;
 @Service
 public class FsFirstDiagnosisServiceImpl extends ServiceImpl<FsFirstDiagnosisMapper, FsFirstDiagnosis> implements IFsFirstDiagnosisService {
 
+
+    @Autowired
+    private FsFirstDiagnosisMapper diagnosisMapper;
+
+    @Autowired
+    private IFsDoctorService doctorService;
+
     /**
      * 查询初诊单
      * 
@@ -42,6 +64,11 @@ public class FsFirstDiagnosisServiceImpl extends ServiceImpl<FsFirstDiagnosisMap
         return baseMapper.selectFsFirstDiagnosisList(fsFirstDiagnosis);
     }
 
+    @Override
+    public List<FsFirstDiagnosisVO> selectFsFirstDiagnosisVOList(FsFirstDiagnosisParam param) {
+        return diagnosisMapper.selectFsFirstDiagnosisVOList(param);
+    }
+
     /**
      * 新增初诊单
      * 
@@ -94,6 +121,88 @@ public class FsFirstDiagnosisServiceImpl extends ServiceImpl<FsFirstDiagnosisMap
 
     @Override
     public FsFirstDiagnosis getByUserId(Long userId) {
-        return baseMapper.getByUserId(userId);
+        FsFirstDiagnosis firstDiagnosis = baseMapper.getByUserId(userId);
+        if (firstDiagnosis == null) {
+            firstDiagnosis = new FsFirstDiagnosis();
+        }
+        return firstDiagnosis;
+    }
+
+    @Override
+    public List<FsDiagnosisListDVO> getDiagnosisList(FsDiagnosisListDParam param) {
+        return diagnosisMapper.selectFsDiagnosisListDVO(param);
+    }
+
+    @Override
+    public List<FsFirstDiagnosisListUVO> selectFsFirstDiagnosisListUVO(FsFirstDiagnosisListUParam param) {
+        List<FsFirstDiagnosisListUVO> listUVOS = diagnosisMapper.selectFsFirstDiagnosisListUVO(param);
+        if (!CollectionUtils.isEmpty(listUVOS)) {
+            listUVOS.forEach(listUVO -> {
+                if(listUVO.getDoctorStatus() != 1) {
+                    listUVO.setSignUrl(null);
+                }
+            });
+        }
+        return listUVOS;
+    }
+
+    @Override
+    public FsFirstDiagnosisListUVO getInfo(Long id) {
+        FsFirstDiagnosisListUVO info = diagnosisMapper.getInfo(id);
+        if (info != null && info.getDoctorStatus() != 1) {
+            info.setSignUrl(null);
+        }
+        return info;
+    }
+
+    @Override
+    public R confirm(Long id) {
+        FsFirstDiagnosis diagnosis = diagnosisMapper.selectById(id);
+        if (diagnosis == null) {
+            return R.error("初诊单不存在");
+        }
+        if (diagnosis.getUserStatus() != 0) {
+            return R.error("初诊单已确认");
+        }
+        if (diagnosis.getDoctorStatus() != 1) {
+            return R.error("医生未签字无法确认");
+        }
+        FsFirstDiagnosis map = new FsFirstDiagnosis();
+        map.setId(diagnosis.getId());
+        map.setUserStatus(1);
+        int i = diagnosisMapper.updateFsFirstDiagnosis(diagnosis);
+        if (i > 0) {
+            return R.ok();
+        }
+        return R.error();
+    }
+
+    @Override
+    public R fill(FsDiagnosisFillDParam param) {
+        if (param.getId() == null) {
+            return R.error("初诊单id不能为空");
+        }
+        FsFirstDiagnosis diagnosis = diagnosisMapper.selectById(param.getId());
+        FsFirstDiagnosis map = new FsFirstDiagnosis();
+        if (diagnosis == null) {
+            return R.error("初诊单不存在");
+        }
+        if (diagnosis.getDoctorId() == null){
+            FsDoctor doctor = doctorService.selectFsDoctorByDoctorId(param.getDoctorId());
+            if (doctor == null) {
+                return R.error("医生不存在");
+            }
+            map.setDoctorName(doctor.getDoctorName());
+            map.setDoctorDep(doctor.getPosition());
+            map.setDoctorCertificate(doctor.getCertificateCode());
+            map.setDoctorId(doctor.getDoctorId());
+        }
+        map.setId(diagnosis.getId());
+        map.setDoctorStatus(1);
+        int i = diagnosisMapper.updateFsFirstDiagnosis(diagnosis);
+        if (i > 0) {
+            return R.ok();
+        }
+        return R.error();
     }
 }

+ 17 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsInquiryPatientInfoServiceImpl.java

@@ -4,6 +4,8 @@ import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.BeanCopyUtils;
 import com.fs.common.exception.CustomException;
+import com.fs.course.param.FsInquiryPatientInfoUParam;
+import com.fs.course.vo.FsInquiryPatientInfoListUVO;
 import com.fs.his.domain.FsInquiryPatientInfo;
 import com.fs.his.mapper.FsInquiryOrderMapper;
 import com.fs.his.mapper.FsInquiryPatientInfoMapper;
@@ -17,6 +19,9 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.Collections;
+import java.util.List;
+
 /**
  * 问诊患者信息Service业务层处理
  */
@@ -55,7 +60,19 @@ public class FsInquiryPatientInfoServiceImpl implements IFsInquiryPatientInfoSer
         if (fsInquiryPatientInfo != null) {
             BeanCopyUtils.copy(fsInquiryPatientInfo, inquiryPatientVO);
             inquiryPatientVO.setDoctorAdviceJson(JSONUtil.toBean(fsInquiryPatientInfo.getDoctorAdviceJson(), DoctorAdviceVO.class));
+        } else {
+            inquiryPatientVO = new FsInquiryPatientVO();
         }
         return inquiryPatientVO;
     }
+
+    @Override
+    public List<FsInquiryPatientInfoListUVO> selectFsInquiryPatientInfoListUVO(FsInquiryPatientInfoUParam param) {
+        return fsInquiryPatientInfoMapper.selectFsInquiryPatientInfoListUVO(param);
+    }
+
+    @Override
+    public FsInquiryPatientInfoListUVO getInfo(Long id) {
+        return fsInquiryPatientInfoMapper.getInfo(id);
+    }
 }

+ 52 - 32
fs-service/src/main/java/com/fs/his/service/impl/FsPackageOrderServiceImpl.java

@@ -21,10 +21,7 @@ import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
-import com.fs.common.utils.DateUtils;
-import com.fs.common.utils.SecurityUtils;
-import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.*;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.Company;
@@ -165,6 +162,9 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
     @Autowired
     private FsUserWxMapper fsUserWxMapper;
 
+    @Autowired
+    private IFsUserWxService userWxService;
+
     /**
      * 查询套餐订单
      *
@@ -315,28 +315,26 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
             param.setUserCouponId(order.getUserCouponId());
         }
         FsUser user=userService.selectFsUserByUserId(order.getUserId());
+        if (Objects.isNull(user)){
+            return R.error("用户不存在");
+        }
+
         if (param.getType()==1) {
-            if(user!=null&& StringUtils.isNotEmpty(user.getMaOpenId())){
-                param.setCompanyId(order.getCompanyId());
-                param.setCompanyUserId(order.getUserId());
-                param.setStoreId(order.getStoreId());
-                Map<String,Object> moneys=computeOrderMoney(order.getTotalPrice(),param);
-                return R.ok().put("moneys",moneys);
-            }
-            else{
+            if (StringUtils.isBlank(user.getMaOpenId()) && !CloudHostUtils.isCloudHostName("弘德堂")) {
                 return R.error("用户OPENID不存在");
             }
+
+            param.setCompanyId(order.getCompanyId());
+            param.setCompanyUserId(order.getUserId());
+            param.setStoreId(order.getStoreId());
+            Map<String,Object> moneys=computeOrderMoney(order.getTotalPrice(),param);
+            return R.ok().put("moneys",moneys);
         }else if (param.getType()==2){
-            if(user!=null){
-                param.setCompanyId(order.getCompanyId());
-                param.setCompanyUserId(order.getUserId());
-                param.setStoreId(order.getStoreId());
-                Map<String,Object> moneys=computeOrderMoney(order.getTotalPrice(),param);
-                return R.ok().put("moneys",moneys);
-            }
-            else{
-                return R.error("用户不存在");
-            }
+            param.setCompanyId(order.getCompanyId());
+            param.setCompanyUserId(order.getUserId());
+            param.setStoreId(order.getStoreId());
+            Map<String,Object> moneys=computeOrderMoney(order.getTotalPrice(),param);
+            return R.ok().put("moneys",moneys);
         }else {
             return R.error("无效的类型参数");
         }
@@ -881,20 +879,42 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
             return R.error("非法操作");
         }
 
-        FsUser user=userService.selectFsUserByUserId(param.getUserId());
-
+//        FsUser user=userService.selectFsUserByUserId(param.getUserId());
+//
+//        String json = configService.selectConfigByKey("his.pay");
+//        PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
+//
+//        String openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+//        if (StringUtils.isBlank(openId)){
+//            Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+//                    .eq(FsUserWx::getFsUserId, param.getUserId())
+//                    .eq(FsUserWx::getAppId, payConfigDTO.getAppId());
+//            FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+//            if (Objects.nonNull(fsUserWx)){
+//                openId = fsUserWx.getOpenId();
+//            }
+//        }
+        FsUser user = userService.selectFsUserByUserId(param.getUserId());
+        //在线支付
         String json = configService.selectConfigByKey("his.pay");
         PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
-
-        String openId = Objects.isNull(user) ? "" : user.getMaOpenId();
-        if (StringUtils.isBlank(openId)){
-            Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
-                    .eq(FsUserWx::getFsUserId, param.getUserId())
-                    .eq(FsUserWx::getAppId, payConfigDTO.getAppId());
-            FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
-            if (Objects.nonNull(fsUserWx)){
+        String openId = "";
+        if (StringUtils.isNotEmpty(param.getAppId())) {
+            FsUserWx fsUserWx = userWxService.selectByAppIdAndUserId(param.getAppId(), param.getUserId(), 1);
+            if (fsUserWx != null) {
                 openId = fsUserWx.getOpenId();
             }
+        } else {
+            openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+            if (StringUtils.isBlank(openId)){
+                Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                        .eq(FsUserWx::getFsUserId, param.getUserId())
+                        .eq(FsUserWx::getAppId, payConfigDTO.getAppId());
+                FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+                if (Objects.nonNull(fsUserWx)){
+                    openId = fsUserWx.getOpenId();
+                }
+            }
         }
 
         if(user!=null&& StringUtils.isNotEmpty(openId)){

+ 123 - 49
fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java

@@ -38,9 +38,7 @@ import com.fs.his.config.FsSysConfig;
 import com.fs.his.config.StoreConfig;
 import com.fs.his.domain.*;
 import com.fs.his.dto.*;
-import com.fs.his.enums.FsStoreOrderLogEnum;
-import com.fs.his.enums.FsStoreOrderStatusEnum;
-import com.fs.his.enums.ShipperCodeEnum;
+import com.fs.his.enums.*;
 import com.fs.his.mapper.*;
 import com.fs.his.param.*;
 import com.fs.his.service.*;
@@ -82,7 +80,14 @@ import com.github.binarywang.wxpay.service.WxPayService;
 import com.google.gson.Gson;
 import lombok.Synchronized;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
 import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.aop.framework.AopContext;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -96,6 +101,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.interceptor.TransactionAspectSupport;
 import org.springframework.util.CollectionUtils;
 
+import java.io.IOException;
 import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -120,6 +126,7 @@ import static com.fs.his.utils.PhoneUtil.encryptPhone;
 @Slf4j
 @EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
 public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
+    Logger logger = LoggerFactory.getLogger(getClass());
     @Autowired
     private WxPayService wxPayService;
     @Autowired
@@ -270,9 +277,15 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
     @Autowired
     private FsUserWxMapper fsUserWxMapper;
 
+    @Autowired
+    private IFsUserWxService userWxService;
+
     @Value("${express.omsCode}")
     private String expressOmsCode;
 
+    @Autowired
+    private com.fs.gtPush.service.uniPush2Service uniPush2Service;
+
     /**
      * 查询订单
      *
@@ -1405,14 +1418,14 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
     @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
     public R payConfirm(String orderCode, String payCode, String tradeNo, String payType, Integer type) {
         try {
-            FsStoreOrderScrm order = null;
+            FsStoreOrder order = null;
             if (type.equals(1)) {
 
-                FsStorePaymentScrm storePayment = fsStorePaymentScrmMapper.selectFsStorePaymentByPaymentCode(payCode);
+                FsStorePayment storePayment = fsStorePaymentMapper.selectFsStorePaymentByPaymentCode(payCode);
                 if (storePayment != null) {
                     if (storePayment.getStatus().equals(0)) {
                         log.info(payCode + "待支付");
-                        FsStorePaymentScrm paymentMap = new FsStorePaymentScrm();
+                        FsStorePayment paymentMap = new FsStorePayment();
                         paymentMap.setPaymentId(storePayment.getPaymentId());
                         paymentMap.setStatus(1);
                         paymentMap.setPayTime(new Date());
@@ -1429,42 +1442,33 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                             paymentMap.setBankSerialNo(orderResult.getBankOrderId());
                             paymentMap.setBankTransactionId(orderResult.getBankTrxId());
                         }
-                        fsStorePaymentScrmMapper.updateFsStorePayment(paymentMap);
+                        fsStorePaymentMapper.updateFsStorePayment(paymentMap);
                         log.info(payCode + "已支付");
-                        order = fsStoreOrderScrmMapper.selectFsStoreOrderById(Long.parseLong(storePayment.getBusinessOrderId()));
+                        order = fsStoreOrderMapper.selectFsStoreOrderByOrderId(Long.parseLong(storePayment.getBusinessId()));
                     }
                 } else {
                     log.info(payCode + "支付单号不存在");
                     return R.error("支付单号不存在");
                 }
             } else if (type.equals(2)) {
-                order = fsStoreOrderScrmMapper.selectFsStoreOrderByOrderCode(orderCode);
+                order = fsStoreOrderMapper.selectFsStoreOrderByOrderCode(orderCode);
             }
-//            if (order != null && !order.getStatus().equals(FsStoreOrderStatusEnum.STATUS_1.getValue())) {
-//                log.info(payCode + "订单号不为待支付回退");
-//                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
-//                return R.error();
-//            }
-
-            //orderScrm迁移的表待支付状态码为:0
-            if (order != null && !order.getStatus().equals(0)) {//判断订单状态是否待支付
+            if (order != null && !order.getStatus().equals(FsStoreOrderStatusEnum.STATUS_1.getValue())) {
                 log.info(payCode + "订单号不为待支付回退");
                 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                 return R.error();
             }
-
-            if (order != null && !order.getPaid().equals(0)) {
+            if (order != null && !order.getIsPay().equals(0)) {
                 log.info(payCode + "订单号支付不为待支付回退");
                 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                 return R.error();
             }
-            fsStoreOrderLogsService.create(order.getId(), FsStoreOrderLogEnum.PAY_ORDER_SUCCESS.getValue(),
+            fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.PAY_ORDER_SUCCESS.getValue(),
                     FsStoreOrderLogEnum.PAY_ORDER_SUCCESS.getDesc());
-            FsStoreOrderScrm storeOrder = new FsStoreOrderScrm();
-            storeOrder.setId(order.getId());
-            storeOrder.setPaid(1);
-//            storeOrder.setStatus(2);
-            storeOrder.setStatus(1);//代发货
+            FsStoreOrder storeOrder = new FsStoreOrder();
+            storeOrder.setOrderId(order.getOrderId());
+            storeOrder.setIsPay(1);
+            storeOrder.setStatus(2);
             storeOrder.setPrescribePrice(order.getTotalPrice());
             SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.store");
             Map<String, Object> config = (Map<String, Object>) JSON.parse(sysConfig.getConfigValue());
@@ -1477,20 +1481,20 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
             } else {
                 storeOrder.setFollowDoctorId(iFsDoctorService.selectFollowDoctorDoctorByPackage());
             }
-            if (order.getCycle() != null && order.getCycle() >= followRate) {
+            if (order.getCycle() >= followRate) {
                 Calendar calendar = Calendar.getInstance();
                 calendar.setTime(new Date());
                 calendar.add(Calendar.DAY_OF_MONTH, followRate);
                 storeOrder.setFollowTime(calendar.getTime());
             }
             storeOrder.setPayTime(new Date());
-            fsStoreOrderScrmMapper.updateFsStoreOrder(storeOrder);
+            fsStoreOrderMapper.updateFsStoreOrder(storeOrder);
             //更新优惠券状态
-            if (order.getCouponId() != null && order.getCouponId() > 0) {
-                FsUserCoupon userCoupon = userCouponService.selectFsUserCouponById(order.getCouponId());
+            if (order.getUserCouponId() != null && order.getUserCouponId() > 0) {
+                FsUserCoupon userCoupon = userCouponService.selectFsUserCouponById(order.getUserCouponId());
                 if (userCoupon != null && userCoupon.getStatus().equals(0)) {
                     userCoupon.setUseTime(new Date());
-                    userCoupon.setBusinessId(order.getId());
+                    userCoupon.setBusinessId(order.getOrderId());
                     userCoupon.setBusinessType(2);
                     userCoupon.setStatus(1);
                     userCouponService.updateFsUserCoupon(userCoupon);
@@ -1895,7 +1899,7 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
     public ExpressResultDTO updateDeliveryItem(ExpressNotifyDTO notifyDTO) {
         String data = URLDecoder.decode(notifyDTO.getRequestData(), Charset.forName("UTF-8"));
         //ExpressInfoDTO
-        log.info("快递根踪回调: {}", data);
+        logger.info("快递根踪回调:" + data);
         FsSysConfig sysConfig = configUtil.getSysConfig();
         ExpressDataDTO expressDataDTO = JSONUtil.toBean(data, ExpressDataDTO.class);
         if (expressDataDTO != null && expressDataDTO.getData() != null) {
@@ -1903,8 +1907,8 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                 List<FsStoreOrder> orders = this.selectFsStoreOrderListByDeliveryId(dto.getLogisticCode());
                 if (orders != null) {
                     for (FsStoreOrder order : orders) {
-                        log.info("订单信息: {}", JSONUtil.toJsonStr(order));
-                        log.info("运单号: {}", dto.getLogisticCode());
+                        logger.info("订单信息:" + JSONUtil.toJsonStr(order));
+                        logger.info("运单号:" + dto.getLogisticCode());
                         if (order != null && (order.getDeliveryStatus() == null || order.getDeliveryStatus() != 3)) {
                             if (dto.getState() != null && dto.getStateEx() != null) {
                                 FsStoreOrder map = new FsStoreOrder();
@@ -1915,16 +1919,47 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                                 this.updateFsStoreOrder(map);
                                 //如果是正常签收,更新订单状态
                                 if (dto.getState().equals("3") && (dto.getStateEx().equals("301") || dto.getStateEx().equals("302") || dto.getStateEx().equals("304") || dto.getStateEx().equals("311"))) {
-                                    SysConfig storeConfig = sysConfigMapper.selectConfigByConfigKey("his.store");
-                                    Map<String, Object> config = (Map<String, Object>) JSON.parse(storeConfig.getConfigValue());
-                                    Object isUpdateOrder = config.get("isUpdateOrder");
-                                    if (isUpdateOrder == null || "1".equals(isUpdateOrder.toString())) {
-                                        this.getGoods(order.getOrderId());
+                                    this.getGoods(order.getOrderId());
+                                    //app订单签收通知
+                                    try {
+                                        uniPush2Service.pushOne(
+                                                order.getUserId(),
+                                                order.getOrderId(),
+                                                null,
+                                                "订单已签收",
+                                                "您的订单:" + order.getOrderCode() + "快递单号为:" + dto.getLogisticCode() + "已签收",
+                                                PushLogTypeEnum.ORDER_STORE.getValue(),
+                                                PushLogDesTypeEnum.ORDER_SIGN.getValue()
+                                        );
+                                    } catch (Exception e) {
+                                        log.error("app订单通知推送失败:{}", e.getMessage());
+                                    }
+                                }
+                                //app派件通知
+                                if (dto.getState().equals("2") && (dto.getStateEx().equals("211"))) {
+                                    //ai向客户发送发货物流信息
+                                    requestExpressInfo(order.getOrderId());
+                                }
+                                //app派件通知
+                                if (dto.getState().equals("2") && (dto.getStateEx().equals("202"))) {
+                                    //app订单签收通知
+                                    try {
+                                        uniPush2Service.pushOne(
+                                                order.getUserId(),
+                                                order.getOrderId(),
+                                                null,
+                                                "订单正在派送",
+                                                "您的订单:" + order.getOrderCode() + "快递单号为:" + dto.getLogisticCode() + "正在派送",
+                                                PushLogTypeEnum.ORDER_STORE.getValue(),
+                                                PushLogDesTypeEnum.ORDER_SIGN.getValue()
+                                        );
+                                    } catch (Exception e) {
+                                        log.error("app订单通知推送失败:{}", e.getMessage());
                                     }
                                 }
                             }
                             if (!dto.isSuccess()) {
-                                log.info("物流状态异常:{}", dto);
+                                logger.info("物流状态异常:{}" + dto);
                             }
                             if ((!dto.isSuccess() && dto.getReason() != null && dto.getReason().equals("三天无轨迹")) || (!dto.isSuccess() && dto.getReason() != null && dto.getReason().equals("七天内无轨迹变化"))) {
                                 //订阅物流回调
@@ -1936,7 +1971,7 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
                                     }
                                 }
                                 expressService.subscribeEspress(order.getOrderCode(), order.getDeliveryCode(), order.getDeliverySn(), lastFourNumber);
-                                log.info("物流重新订阅:{}", order.getDeliverySn());
+                                logger.info("物流重新订阅:{}", order.getDeliverySn());
                             }
 
                         }
@@ -1954,6 +1989,23 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         return fsStoreOrderMapper.selectFsStoreOrderListByDeliverySn(logisticCode);
     }
 
+    public static R requestExpressInfo(Long orderId){
+        String fileUrl = "http://ipad.cdwjyyh.com/msg/sendExpressInfo/" + orderId;
+
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            HttpGet httpGet = new HttpGet(fileUrl);
+            HttpResponse response = httpClient.execute(httpGet);
+
+            // 检查响应状态码
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                return R.ok();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
     public R syncExpress(Long id) {
@@ -2598,21 +2650,43 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
             return R.error("订单状态不正确");
         }
 
+//        FsUser user = userService.selectFsUserByUserId(param.getUserId());
+//
+//        //在线支付
+//        String json = configService.selectConfigByKey("his.pay");
+//        PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
+//
+//        String openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+//        if (StringUtils.isBlank(openId)){
+//            Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+//                    .eq(FsUserWx::getFsUserId, param.getUserId())
+//                    .eq(FsUserWx::getAppId, payConfigDTO.getAppId());
+//            FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+//            if (Objects.nonNull(fsUserWx)){
+//                openId = fsUserWx.getOpenId();
+//            }
+//        }
         FsUser user = userService.selectFsUserByUserId(param.getUserId());
-
         //在线支付
         String json = configService.selectConfigByKey("his.pay");
         PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
-
-        String openId = Objects.isNull(user) ? "" : user.getMaOpenId();
-        if (StringUtils.isBlank(openId)){
-            Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
-                    .eq(FsUserWx::getFsUserId, param.getUserId())
-                    .eq(FsUserWx::getAppId, payConfigDTO.getAppId());
-            FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
-            if (Objects.nonNull(fsUserWx)){
+        String openId = "";
+        if (StringUtils.isNotEmpty(param.getAppId())) {
+            FsUserWx fsUserWx = userWxService.selectByAppIdAndUserId(param.getAppId(), param.getUserId(), 1);
+            if (fsUserWx != null) {
                 openId = fsUserWx.getOpenId();
             }
+        } else {
+            openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+            if (StringUtils.isBlank(openId)){
+                Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                        .eq(FsUserWx::getFsUserId, param.getUserId())
+                        .eq(FsUserWx::getAppId, payConfigDTO.getAppId());
+                FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+                if (Objects.nonNull(fsUserWx)){
+                    openId = fsUserWx.getOpenId();
+                }
+            }
         }
 
         if (user != null && StringUtils.isNotEmpty(openId)) {

+ 7 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -40,8 +40,10 @@ import com.fs.his.config.IntegralConfig;
 import com.fs.his.domain.*;
 import com.fs.his.domain.FsUserAddress;
 import com.fs.his.domain.FsUserIntegralLogs;
+import com.fs.his.dto.FindUsersByDTO;
 import com.fs.his.enums.FsUserIntegralLogTypeEnum;
 import com.fs.his.mapper.*;
+import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserAddIntegralTemplateParam;
 import com.fs.his.param.FsUserParam;
 import com.fs.his.service.IFsUserIntegralLogsService;
@@ -173,6 +175,11 @@ public class FsUserServiceImpl implements IFsUserService
         return fsUserMapper.selectFsUserByUserId(userId);
     }
 
+    @Override
+    public List<FindUsersByDTO> findUserByParam(FindUserByParam param) {
+        return fsUserMapper.findUsersByParam(param);
+    }
+
     /**
      * 查询用户列表
      *

+ 33 - 0
fs-service/src/main/java/com/fs/his/vo/FsDiagnosisListDVO.java

@@ -0,0 +1,33 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class FsDiagnosisListDVO {
+
+    private Long id;
+
+    private Long doctorId;
+
+    private String patientName;
+
+    private Integer gender;
+
+    private String phone;
+
+    private String firstDiagnosis;
+
+    private String age;
+
+    private String physicalCondition;
+
+    /** 日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date dateTime;
+
+    private Integer doctorStatus;
+    private Integer userStatus;
+}

+ 2 - 0
fs-service/src/main/java/com/fs/his/vo/FsDoctorArticleUVO.java

@@ -32,5 +32,7 @@ public class FsDoctorArticleUVO implements Serializable {
     private Date createTime;
     private String videoUrl;
 
+    private String fileId; //vod文件id
+
 
 }

+ 81 - 0
fs-service/src/main/java/com/fs/his/vo/FsFirstDiagnosisVO.java

@@ -0,0 +1,81 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class FsFirstDiagnosisVO {
+    /** $column.columnComment */
+    private Long id;
+
+    /** 患者姓名 */
+    @Excel(name = "患者姓名")
+    private String patientName;
+
+    /** 年龄 */
+    @Excel(name = "年龄")
+    private String age;
+
+    /** 0-未知 1-男性 2-女性 */
+    @Excel(name = "性别",readConverterExp = "0=未知,1=男,2=女")
+    private Long gender;
+
+    /** 电话 */
+    @Excel(name = "电话")
+    private String phone;
+
+    /** 身体状况 */
+    @Excel(name = "身体状况")
+    private String physicalCondition;
+
+    /** 日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date dateTime;
+
+    /** 出版诊断 */
+    @Excel(name = "出版诊断")
+    private String firstDiagnosis;
+
+    /** 医生id */
+    @Excel(name = "医生id")
+    private Long doctorId;
+
+    /** 医生名称 */
+    @Excel(name = "医生名称")
+    private String doctorName;
+
+    /** 职称 */
+    @Excel(name = "职称")
+    private String doctorDep;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long userId;
+
+    /** 医生证号 */
+    @Excel(name = "医生证号")
+    private String doctorCertificate;
+
+    /** 医生是否填写:0-未填写 1-已填写 */
+    @Excel(name = "")
+    private Integer doctorStatus;
+
+    /** 用户是否答复:0-未答复 1-已答复 */
+    private Integer userStatus;
+
+    /**
+     * 销售id
+     */
+    private Long qwUserId;
+
+    /**
+     * 销售名称
+     */
+    private String qwUserName;
+
+    private String userName;
+}

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

@@ -756,7 +756,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService
             if (flag==0) {
                 return R.error("订单创建失败");
             }
-            if (!isPay){
+            if (!isPay && storeOrder.getCompanyId()!=null){
                 // 添加订单审核
                 addOrderAudit(storeOrder);
             }

+ 4 - 2
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -182,8 +182,10 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
                 String[] split = item.getCompanyIds().split(",");
 
                 for (String companyId : split) {
-                    String companyName = companyCacheService.selectCompanyNameById(Long.valueOf(companyId));
-                    companyNameList.add(companyName);
+                    if(StringUtils.isNotBlank(companyId)) {
+                        String companyName = companyCacheService.selectCompanyNameById(Long.valueOf(companyId));
+                        companyNameList.add(companyName);
+                    }
                 }
                 item.setCompanyName(String.join(",",companyNameList));
             }

+ 3 - 0
fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java

@@ -145,4 +145,7 @@ public class QwExternalContact extends BaseEntity
     // 是否已购0 否 1程序内下单 2程序外下单
     private Integer payOrder;
 
+    //用户是否回复  0未回复  1已回复
+    private Integer isReply;
+
 }

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

@@ -433,4 +433,8 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     int insertQwUserDelLossLog(@Param("param") QwUserDelLossLog qwUserDelLossLog);
 
     List<QwUserDelLossLogVO> selectQwUserDelLossList(@Param("param") QwUserDelLossLogParam param);
+
+    List<QwExternalContact> selectQwExternalContactByFsUserIdAndCompany(@Param("userId")Long userId,@Param("companyUserId") Long companyUserId);
+
+    void updateQwExternalContactIsRePlyById(@Param("id")Long id);
 }

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików