152 Incheckningar 0fc490c393 ... 939362d3d1

Upphovsman SHA1 Meddelande Datum
  sgw 939362d3d1 再次 补上 部门好友欢迎语查询的sql 6 månader sedan
  yfh b5a264235e Merge remote-tracking branch 'origin/master' 6 månader sedan
  yfh 5d959e3622 看课名称和id不一致问题 6 månader sedan
  caoliqin 47c09ae3a8 feat:九州app配置 6 månader sedan
  caoliqin 9b314c3a7a Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  caoliqin febbe2e756 feat:营期看课统计 6 månader sedan
  yfh fd9242e115 黑名单提示问题处理 6 månader sedan
  yfh 22ea1d7a14 Merge remote-tracking branch 'origin/master' 6 månader sedan
  yfh 3e47eff805 调整课程分类下拉选择 6 månader sedan
  ct d8cb7a8489 Merge remote-tracking branch 'origin/master' 6 månader sedan
  ct b5584a6b1d 还原配置 6 månader sedan
  ct 5e8b0e4775 金牛:售后部分商品退款 6 månader sedan
  yfh 78e161c611 Merge remote-tracking branch 'origin/master' 6 månader sedan
  yfh de5f13657d 1、看课分类限制,谁创建的只能看到自己的(需要打开看课配置中是否绑定销售信息) 6 månader sedan
  zyp 2d98fde767 zyp 6 månader sedan
  吴树波 f69dbbc171 Merge remote-tracking branch 'origin/master' 6 månader sedan
  吴树波 ef14a4502a 部门权限修改 6 månader sedan
  wjj a4e0a2bb7f fix:修复商城购物车,商品列表SQL 6 månader sedan
  liupeng 688fd8ae47 Merge remote-tracking branch 'origin/master' 6 månader sedan
  liupeng c72e0e189d 新增点播配置保存新字段createDeptId 6 månader sedan
  xdd e20550dd93 feat: 红包领取次数限制 6 månader sedan
  xdd 549d957952 feat: 红包领取次数限制 6 månader sedan
  xdd 4d6aa16d4d feat: 红包领取次数限制 6 månader sedan
  xdd 57fb01b384 fix: 红包领取次数限制 6 månader sedan
  zyp 32fdc40334 zyp 6 månader sedan
  yjwang 37d588a4e6 Merge remote-tracking branch 'origin/master' 6 månader sedan
  yjwang 18a0f9cad4 百年配置提交 6 månader sedan
  GHH 31f9efe03f fix:修改查寻sql 处方订单页面查重bug 6 månader sedan
  liupeng ff127387e4 Merge remote-tracking branch 'origin/master' 6 månader sedan
  liupeng 8bc409d669 修改内蒙古纯正堂配置文件iPad 6 månader sedan
  GHH cf0fc88244 fix:订单管理 套餐包订单,专家问诊订单,快速咨询订单,拉开药咨询订单,咨询报告订单 table表增加小程序名称属性,增加所属小程序选择框查询 6 månader sedan
  zyp f09bafd94f zyp 6 månader sedan
  liupeng 9196e241c4 新增内蒙古纯正堂配置文件 6 månader sedan
  jzp ca87dfea43 1.提交yml配置 6 månader sedan
  jzp d718814ba3 1.通话记录统计展示异常 6 månader sedan
  zyp 9d22a3cc4c Merge remote-tracking branch 'origin/master' 6 månader sedan
  zyp 1ee98e7000 zyp 6 månader sedan
  ct 27bade831d Merge remote-tracking branch 'origin/master' 6 månader sedan
  ct 30a729402f 金牛:售后部分商品退款 6 månader sedan
  三七 4d101770d8 迁移完课打备注 6 månader sedan
  jzp a9cd37212a 1.解决qw_voice循环依赖问题 6 månader sedan
  zyp fbaf14541a zyp 6 månader sedan
  ct 8bded3a9aa Merge remote-tracking branch 'origin/master' 6 månader sedan
  GHH d25a98a977 fix:处方订单页面 table表增加小程序名称属性,增加所属小程序选择框查询 6 månader sedan
  zyp 807d930290 zyp 6 månader sedan
  15376779826 7735a5db41 订单codeUtil更新 6 månader sedan
  liupeng 0d5e104eb2 新增内蒙古纯正堂配置文件 6 månader sedan
  yfh 93e96686b7 1、调整先导课显示 6 månader sedan
  ct 2fc31a2991 Merge remote-tracking branch 'origin/master' 6 månader sedan
  ct 7f8220da2d 金牛:修改物流代收金额 6 månader sedan
  xdd b7b6b77719 erp售后流程 6 månader sedan
  xdd d563a8fe9f erp售后流程 6 månader sedan
  Long 260ec3f855 去掉金牛、木易支付前的openId判断 6 månader sedan
  ct 4274c6b0cc Merge remote-tracking branch 'origin/master' 6 månader sedan
  ct 8440f4610c 金牛:添加包裹数量 6 månader sedan
  xdd 4d98d180f1 add 6 månader sedan
  xdd ff3807fcd6 add 6 månader sedan
  caoliqin c4f035e375 Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  caoliqin bc02d813b0 feat:鸿森堂配置 6 månader sedan
  jzp b8b66ea2ad Merge remote-tracking branch 'origin/master' 6 månader sedan
  吴树波 d4bbdbdf5a Merge remote-tracking branch 'origin/master' 6 månader sedan
  吴树波 4adbd0bd2c 医健宝部门数据 6 månader sedan
  jzp 9e0c9edc9d 1.套餐包增加订单包新增按钮 6 månader sedan
  GHH a621594a3b Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  GHH 83744e5359 fix:订单导出按照需求用户选则几个属性列表就导出几个属性列表 6 månader sedan
  jzp db4aec2944 1.益善缘取消店铺验证 6 månader sedan
  jzp 449971cecb 1.益善缘增加商品管理和套餐包一键复制 6 månader sedan
  ct ab2b917853 fix:金牛测试yml 6 månader sedan
  ct b14f44a73e Merge remote-tracking branch 'origin/master' 6 månader sedan
  ct fbbaf8dc2d fix:套餐区分区分appId查询 6 månader sedan
  yjwang 023f226eb1 Merge remote-tracking branch 'origin/master' 6 månader sedan
  yjwang 83320aec2e 济南联志健康配置提交2 6 månader sedan
  ct da93496d9a Merge remote-tracking branch 'origin/master' 6 månader sedan
  ct 0bf972b46c 金牛:待发货的订单,收到代服取消订单的通知,更改订单状态为待推送 6 månader sedan
  chenguo 5c441e2f11 Merge remote-tracking branch 'origin/master' 6 månader sedan
  chenguo d1ca8afcb3 多店铺开关 isStores 6 månader sedan
  yjwang 3a705e631c 济南联志健康配置提交 6 månader sedan
  jzp a55cb8b379 1.通话记录统计不展示标签问题 6 månader sedan
  GHH dc7ab8cdb0 fix:yml设置本地配置 6 månader sedan
  xdd 28d73b0f86 add 6 månader sedan
  ct 03ab37ab5f Merge remote-tracking branch 'origin/master' 6 månader sedan
  ct 15f90a6e39 套餐包区分appId显示: 6 månader sedan
  wjj b5779adf79 修改商城订单推送聚水潭订单备注业务 6 månader sedan
  三七 86933d527a 九州和同顺堂 润天老商户号配置 6 månader sedan
  15376779826 e053a7fe9c 新增公司码绑定销售 6 månader sedan
  15376779826 6128772d83 Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  15376779826 c6dee92a2d 新增公司码绑定销售 6 månader sedan
  wjj 8a21926813 1.更新权限问题 2.修改推送订单路由问题 6 månader sedan
  chenguo e653743ed8 Merge remote-tracking branch 'origin/master' 6 månader sedan
  Long 75085b1828 fs-qw-api-msg启动redis配置报错处理 6 månader sedan
  15376779826 9a4960cb32 Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  xdd 25b1b22a2a add 6 månader sedan
  xdd 2e70c31d98 add 6 månader sedan
  caoliqin 5d74e2b07b Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  caoliqin 2f6d3f987d feat:重新提交 6 månader sedan
  caoliqin e68753a051 Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  zyp 58590b5932 zyp 6 månader sedan
  ct 9da571fbc4 Merge remote-tracking branch 'origin/master' 6 månader sedan
  Long 1feedf8c02 红德堂-aiApi配置修改 6 månader sedan
  caoliqin 8967c33872 feat:被删除的代码还原 6 månader sedan
  ct 34e314e4a4 Merge remote-tracking branch 'origin/master' 6 månader sedan
  zx 5b039e14d6 add 6 månader sedan
  xdd f07cac6beb add 6 månader sedan
  xdd 41682314b6 add 6 månader sedan
  xdd f2a33bebe9 add 6 månader sedan
  xdd b79b528743 add 6 månader sedan
  xdd 187a8a16e3 add 6 månader sedan
  xdd ac0663fab7 add 6 månader sedan
  zyp dd6007c484 zyp 6 månader sedan
  zx d588bc29e0 add 6 månader sedan
  jzp 111b12ea51 1.修改通话记录统计展示 6 månader sedan
  ct 8ab786ca7a 空指针问题处理 6 månader sedan
  ct cc924e7b11 修改红包添加日志 6 månader sedan
  ct 7f0856ea34 多小程序支付 6 månader sedan
  ct d545422c3d 企微员工全模糊查询,通话记录按部门统计 6 månader sedan
  ct 413590bda2 多小程序所以汇付支付接口添加appid 6 månader sedan
  Long b548108b93 FIX: 会员训练营列表查询卡顿优化 6 månader sedan
  Long 16af405310 FIX: 项目会员列表查询条件还原 6 månader sedan
  yfh 79ca67506f 调整参数错误 6 månader sedan
  yfh fd530c7247 Merge remote-tracking branch 'origin/master' 6 månader sedan
  yfh 9fa1fce466 调整参数错误 6 månader sedan
  ct 6f509400ae Merge remote-tracking branch 'origin/master' 6 månader sedan
  xdd 768f07195a Merge branch 'refs/heads/master_feat_redisson_20250916' 6 månader sedan
  ct afd3508620 中康erp只推药品 6 månader sedan
  xdd 9fdb26229c fix: 润天公司账户充值 6 månader sedan
  caoliqin 958d45b71c Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  15376779826 64b47cf84e Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  15376779826 e50850e15e 木易问诊订单支付成功医生自动接单 6 månader sedan
  xdd e06b07c97d fix: 润天公司账户充值 6 månader sedan
  wjj 6b007ed34a 修改问诊单查询用户id问题 6 månader sedan
  caoliqin 873cf7035e Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  caoliqin cf80946b2c fix:商城小程序用户登录bug修复 6 månader sedan
  caoliqin 84886b8939 feat:鸿森堂配置文件 6 månader sedan
  xdd 74c9327688 fix: 代码优化 6 månader sedan
  xdd a0cff3d559 feat: 开启红包总账户扣款 6 månader sedan
  xdd f72625e842 feat: 红包性能优化 6 månader sedan
  15376779826 6c37f0a738 Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  zx b59076aa25 add 6 månader sedan
  zx 8cfbc98a7b Merge remote-tracking branch 'origin/master' 6 månader sedan
  zx 3053120a6a add 6 månader sedan
  yjwang 88bfbd2068 Merge remote-tracking branch 'origin/master' 6 månader sedan
  yjwang 0ebcdcc9f4 scrm小程序支付,适配多商城 6 månader sedan
  xdd c03b34a3c6 feat: 领红包添加公司余额 6 månader sedan
  xdd 69c0b5ef14 feat: 领红包添加公司余额 6 månader sedan
  15376779826 c9549a6595 补充缺少的接口 6 månader sedan
  三七 d484008d66 分支 6 månader sedan
  三七 1468784ad7 分支 6 månader sedan
  三七 1e7dd56df4 Merge branch 'refs/heads/master' into master_feat_redisson_20250916 6 månader sedan
  15376779826 7c34504fb1 Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  15376779826 3163559565 Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java 6 månader sedan
  15376779826 a33fd8d767 补充缺少的接口 6 månader sedan
  xdd 7c02abdc53 feat: 领红包添加公司余额 6 månader sedan
100 ändrade filer med 3135 tillägg och 493 borttagningar
  1. 37 0
      fs-ad-api/src/main/java/com/fs/framework/config/RedisConfig.java
  2. 5 5
      fs-admin/src/main/java/com/fs/company/controller/CompanyController.java
  3. 47 0
      fs-admin/src/main/java/com/fs/company/controller/CompanyStatisticsController.java
  4. 4 2
      fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java
  5. 44 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseCategoryController.java
  6. 1 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseVideoController.java
  7. 3 3
      fs-admin/src/main/java/com/fs/his/controller/FsCompanyController.java
  8. 12 0
      fs-admin/src/main/java/com/fs/his/controller/FsPackageController.java
  9. 60 12
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  10. 7 1
      fs-admin/src/main/java/com/fs/his/task/Task.java
  11. 13 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java
  12. 94 0
      fs-admin/src/main/java/com/fs/qw/FsCourseTask.java
  13. 2 2
      fs-admin/src/main/java/com/fs/qw/controller/QwCompanyController.java
  14. 2 2
      fs-admin/src/main/resources/application.yml
  15. 36 0
      fs-common-api/src/main/java/com/fs/framework/config/RedisConfig.java
  16. 10 1
      fs-common/src/main/java/com/fs/common/core/domain/entity/SysRole.java
  17. 61 0
      fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java
  18. 140 0
      fs-common/src/main/java/com/fs/common/utils/poi/ExcelUtil.java
  19. 40 0
      fs-company-app/src/main/java/com/fs/core/config/RedisConfig.java
  20. 14 5
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseAnswerLogsController.java
  21. 14 4
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java
  22. 2 0
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java
  23. 1 1
      fs-company/src/main/java/com/fs/company/controller/course/FsUserCourseTrainingCampController.java
  24. 1 1
      fs-company/src/main/java/com/fs/company/controller/course/FsUserOperationLogController.java
  25. 80 4
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserVoiceLogController.java
  26. 2 2
      fs-company/src/main/java/com/fs/company/controller/store/FsStoreOrderController.java
  27. 37 0
      fs-company/src/main/java/com/fs/framework/config/RedisConfig.java
  28. 39 0
      fs-doctor-app/src/main/java/com/fs/framework/config/RedisConfig.java
  29. 39 0
      fs-framework/src/main/java/com/fs/framework/config/RedisConfig.java
  30. 38 0
      fs-hospital/src/main/java/com/fs/framework/config/RedisConfig.java
  31. 40 0
      fs-ipad-task/src/main/java/com/fs/framework/config/RedisConfig.java
  32. 38 0
      fs-live-app/src/main/java/com/fs/framework/config/RedisConfig.java
  33. 39 0
      fs-qw-api-msg/src/main/java/com/fs/framework/config/RedisConfig.java
  34. 39 0
      fs-qw-api/src/main/java/com/fs/framework/config/RedisConfig.java
  35. 39 0
      fs-qw-mq/src/main/java/com/fs/framework/config/RedisConfig.java
  36. 40 0
      fs-qw-task/src/main/java/com/fs/framework/config/RedisConfig.java
  37. 42 8
      fs-qw-voice/src/main/java/com/fs/app/controller/CommonController.java
  38. 10 10
      fs-qw-voice/src/main/java/com/fs/app/exception/FSException.java
  39. 99 12
      fs-qw-voice/src/main/java/com/fs/app/mq/RocketMQConsumerService.java
  40. 10 10
      fs-qw-voice/src/main/java/com/fs/app/task/Task.java
  41. 13 3
      fs-qw-voice/src/main/java/com/fs/framework/config/DataSourceConfig.java
  42. 4 4
      fs-qw-voice/src/main/java/com/fs/framework/config/DruidConfig.java
  43. 37 0
      fs-qw-voice/src/main/java/com/fs/framework/config/RedisConfig.java
  44. 1 1
      fs-qw-voice/src/main/resources/application.yml
  45. 39 0
      fs-qwhook-msg/src/main/java/com/fs/framework/config/RedisConfig.java
  46. 38 0
      fs-qwhook-sop/src/main/java/com/fs/framework/config/RedisConfig.java
  47. 40 0
      fs-qwhook/src/main/java/com/fs/framework/config/RedisConfig.java
  48. 18 0
      fs-redis/src/main/java/com/fs/framework/config/RedisConfig.java
  49. 37 0
      fs-repeat-api/src/main/java/com/fs/framework/config/RedisConfig.java
  50. 59 0
      fs-service/src/main/java/com/fs/aiChat/domain/InterestAiChatMsg.java
  51. 73 0
      fs-service/src/main/java/com/fs/aiChat/domain/InterestAiSession.java
  52. 54 0
      fs-service/src/main/java/com/fs/aiChat/domain/SessionRoleInfo.java
  53. 59 0
      fs-service/src/main/java/com/fs/aiChat/mapper/InterestAiChatSessionMapper.java
  54. 15 0
      fs-service/src/main/java/com/fs/aiChat/param/InterestAiMessage.java
  55. 2 0
      fs-service/src/main/java/com/fs/company/service/ICompanyConfigService.java
  56. 2 0
      fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java
  57. 38 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyConfigServiceImpl.java
  58. 31 2
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  59. 9 0
      fs-service/src/main/java/com/fs/config/ai/AiHostProper.java
  60. 6 1
      fs-service/src/main/java/com/fs/core/config/RedissonConfig.java
  61. 15 24
      fs-service/src/main/java/com/fs/core/config/WxMaConfiguration.java
  62. 10 10
      fs-service/src/main/java/com/fs/core/utils/OrderCodeUtils.java
  63. 5 0
      fs-service/src/main/java/com/fs/course/domain/FsCoursePlaySourceConfig.java
  64. 9 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseRedPacketLog.java
  65. 2 0
      fs-service/src/main/java/com/fs/course/domain/FsUserCourseCategory.java
  66. 5 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  67. 3 0
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseCategoryMapper.java
  68. 4 0
      fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigCreateParam.java
  69. 4 0
      fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigEditParam.java
  70. 1 0
      fs-service/src/main/java/com/fs/course/param/FsUserCourseOrderDoPayParam.java
  71. 1 0
      fs-service/src/main/java/com/fs/course/param/FsUserVipOrderPayUParam.java
  72. 2 1
      fs-service/src/main/java/com/fs/course/service/IFsUserCompanyUserService.java
  73. 1 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseCategoryService.java
  74. 34 42
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseFinishTempServiceImpl.java
  75. 85 15
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseProductOrderServiceImpl.java
  76. 10 1
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCompanyUserServiceImpl.java
  77. 4 1
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseCategoryServiceImpl.java
  78. 63 6
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseOrderServiceImpl.java
  79. 5 5
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java
  80. 302 124
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  81. 63 6
      fs-service/src/main/java/com/fs/course/service/impl/FsUserVipOrderServiceImpl.java
  82. 16 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserWatchCourseStatisticsServiceImpl.java
  83. 6 0
      fs-service/src/main/java/com/fs/course/vo/FsCoursePlaySourceConfigVO.java
  84. 4 0
      fs-service/src/main/java/com/fs/erp/dto/sdk/wangdian/api/WdtClient.java
  85. 5 5
      fs-service/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java
  86. 41 35
      fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java
  87. 29 80
      fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java
  88. 23 7
      fs-service/src/main/java/com/fs/erp/service/impl/K9OrderScrmServiceImpl.java
  89. 2 2
      fs-service/src/main/java/com/fs/erp/service/impl/WdtErpGoodsServiceImpl.java
  90. 314 29
      fs-service/src/main/java/com/fs/fastgptApi/util/AudioUtils.java
  91. 4 0
      fs-service/src/main/java/com/fs/fastgptApi/vo/AudioVO.java
  92. 71 0
      fs-service/src/main/java/com/fs/his/domain/FsInterestAiMsg.java
  93. 56 0
      fs-service/src/main/java/com/fs/his/domain/FsInterestAiRole.java
  94. 50 0
      fs-service/src/main/java/com/fs/his/domain/FsInterestAiSession.java
  95. 1 0
      fs-service/src/main/java/com/fs/his/domain/FsPackage.java
  96. 4 0
      fs-service/src/main/java/com/fs/his/domain/FsStoreOrderDf.java
  97. 14 0
      fs-service/src/main/java/com/fs/his/domain/FsStorePayment.java
  98. 1 0
      fs-service/src/main/java/com/fs/his/enums/FsStoreOrderLogEnum.java
  99. 5 1
      fs-service/src/main/java/com/fs/his/mapper/FsInquiryOrderMapper.java
  100. 5 1
      fs-service/src/main/java/com/fs/his/mapper/FsInquiryOrderReportMapper.java

+ 37 - 0
fs-ad-api/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -65,6 +67,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
 
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
@@ -90,7 +110,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 5 - 5
fs-admin/src/main/java/com/fs/company/controller/CompanyController.java

@@ -71,7 +71,7 @@ public class CompanyController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             param.setDeptId(loginUser.getDeptId());
         }
         List<CompanyVO> list = companyService.selectCompanyVOList(param);
@@ -89,7 +89,7 @@ public class CompanyController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             company.setDeptId(loginUser.getDeptId());
         }
         List<CompanyVO> list = companyService.selectCompanyVOList(company);
@@ -178,7 +178,7 @@ public class CompanyController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             map.setDeptId(loginUser.getDeptId());
         }
         List<Company> list = companyService.selectCompanyList(map);
@@ -194,7 +194,7 @@ public class CompanyController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             param.setDeptId(loginUser.getDeptId());
         }
         List<CompanyCrmVO> list = companyService.selectCompanyCrmDayCountList(param);
@@ -276,7 +276,7 @@ public class CompanyController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             deptId = loginUser.getDeptId();
         }
         List<OptionsVO> list = companyService.selectAllCompanyList(deptId);

+ 47 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyStatisticsController.java

@@ -19,6 +19,7 @@ import com.fs.crm.service.ICrmCustomerService;
 import com.fs.crm.service.ICrmCustomerVisitService;
 import com.fs.crm.vo.CrmCustomerStatisticsVO;
 import com.fs.crm.vo.CrmCustomerVisitStatisticsVO;
+import com.fs.his.service.IFsStoreAfterSalesService;
 import com.fs.his.service.IFsStoreOrderService;
 import com.fs.his.service.IFsStorePaymentService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -49,6 +50,10 @@ public class CompanyStatisticsController extends BaseController
 
     @Autowired
     private IFsStoreOrderService storeOrderService;
+
+    @Autowired
+    private IFsStoreAfterSalesService storeAfterSalesService;
+
     @Autowired
     private IFsStorePaymentService storePaymentService;
     @Autowired
@@ -146,6 +151,48 @@ public class CompanyStatisticsController extends BaseController
         }
     }
 
+    @GetMapping("/afterSalesOrder")
+    public R afterSalesOrder(FsStoreStatisticsParam param)
+    {
+        if(StringUtils.isNotEmpty(param.getUserIds())){
+            String[] userIds=param.getUserIds().split(",");
+            Long[] ids=new Long[userIds.length];
+            for(int i=0;i<ids.length; i++){
+                ids[i]=Long.parseLong(userIds[i]);
+            }
+            param.setUsers(ids);
+        }
+        else{
+            //获取部门下的所有用户
+            CompanyUser usermap=new CompanyUser();
+            usermap.setDeptId(param.getDeptId());
+            List<CompanyUser> users = userService.getUserListByDeptId(usermap);
+            List<Long> userIds = users.stream().map(element -> element.getUserId()).collect(Collectors.toList());
+            param.setUsers(userIds.toArray(new Long[userIds.size()]));
+        }
+        if(param.getUsers()!=null&&param.getUsers().length>0){
+            List<FsStoreOrderStatisticsVO> list= storeAfterSalesService.selectFsStoreAfterSalesServiceStatisticsList(param);
+
+            TimeUtils.TimeEntity timeEntity= TimeUtils.parseTime(param.getType().toString(),param.getStartTime(),param.getEndTime());
+            timeEntity.setUserIds(param.getUsers());
+            Integer cycleNum = timeEntity.getCycleNum();
+            Integer beginTime = timeEntity.getBeginTime();
+            List<Integer> timeList = new ArrayList<>();
+            for (int i = 1; i <= cycleNum; i++) {
+                timeList.add(beginTime);
+                beginTime = TimeUtils.formatTime(beginTime);
+            }
+            List<JSONObject> jsonObjectList = storeOrderService.selectFsPackageOrderCounts(timeEntity.toMap());
+            List<String> dates = jsonObjectList.stream().map(jsonObject -> jsonObject.getString("type")).collect(Collectors.toList());
+            List<Integer> orderCount = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("orderCount")).collect(Collectors.toList());
+            List<Integer> payPrice = jsonObjectList.stream().map(jsonObject -> jsonObject.getInteger("payPrice")).collect(Collectors.toList());
+            return R.ok().put("list",list).put("dates",dates).put("orderCount",orderCount).put("payPrice",payPrice);
+        }
+        else {
+            return R.ok("未查找到数据");
+        }
+    }
+
 
     @GetMapping("/inquiryOrder")
     public R storeInquiryOrder(FsStoreStatisticsParam param)

+ 4 - 2
fs-admin/src/main/java/com/fs/course/controller/FsCoursePlaySourceConfigController.java

@@ -46,17 +46,19 @@ public class FsCoursePlaySourceConfigController extends BaseController {
     @GetMapping("/list")
     public TableDataInfo list(@RequestParam(required = false) String name,
                               @RequestParam(required = false) String appid,
+                              @RequestParam(required = false) Integer isMall,
                               @RequestParam(required = false, defaultValue = "1") Integer pageNum,
                               @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
         Map<String, Object> params = new HashMap<>();
         params.put("name", name);
         params.put("appid", appid);
+        params.put("isMall", isMall);
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
         Long userId = null;
         Long deptId = null;
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             deptId = loginUser.getDeptId();
             if(config.getDept() == null || !config.getDept()){
                 userId = loginUser.getUserId();
@@ -146,7 +148,7 @@ public class FsCoursePlaySourceConfigController extends BaseController {
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
         QueryWrapper<FsCoursePlaySourceConfig> queryWrapper = new QueryWrapper<FsCoursePlaySourceConfig>().eq("is_del", 0);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             queryWrapper.eq("create_user_id", loginUser.getUserId()).eq(config.getDept() == null || !config.getDept(), "create_dept_id", loginUser.getDeptId());
         }
         if(companyId != null){

+ 44 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseCategoryController.java

@@ -3,9 +3,18 @@ package com.fs.course.controller;
 import java.util.List;
 
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.utils.ServletUtils;
+import com.fs.framework.web.service.TokenService;
 import com.fs.his.domain.FsStoreProductCategory;
 import com.fs.his.vo.FsStoreProductCategoryVO;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
+import com.fs.course.config.CourseConfig;
+
 import com.fs.his.vo.OptionsVO;
+import com.fs.system.service.ISysConfigService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -38,6 +47,12 @@ public class FsUserCourseCategoryController extends BaseController
     @Autowired
     private IFsUserCourseCategoryService fsUserCourseCategoryService;
 
+    @Autowired
+    private TokenService tokenService;
+
+    @Autowired
+    private ISysConfigService configService;
+
     /**
      * 查询课堂分类列表
      */
@@ -45,6 +60,13 @@ public class FsUserCourseCategoryController extends BaseController
     @GetMapping("/list")
     public AjaxResult list(FsUserCourseCategory fsUserCourseCategory)
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
+            fsUserCourseCategory.setUserId(userId);
+        }
         List<FsUserCourseCategory> list = fsUserCourseCategoryService.selectFsUserCourseCategoryList(fsUserCourseCategory);
         return AjaxResult.success(list);
     }
@@ -57,6 +79,13 @@ public class FsUserCourseCategoryController extends BaseController
     @GetMapping("/export")
     public AjaxResult export(FsUserCourseCategory fsUserCourseCategory)
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
+            fsUserCourseCategory.setUserId(userId);
+        }
         List<FsUserCourseCategory> list = fsUserCourseCategoryService.selectFsUserCourseCategoryList(fsUserCourseCategory);
         ExcelUtil<FsUserCourseCategory> util = new ExcelUtil<FsUserCourseCategory>(FsUserCourseCategory.class);
         return util.exportExcel(list, "课堂分类数据");
@@ -80,6 +109,13 @@ public class FsUserCourseCategoryController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody FsUserCourseCategory fsUserCourseCategory)
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
+            fsUserCourseCategory.setUserId(userId);
+        }
         return toAjax(fsUserCourseCategoryService.insertFsUserCourseCategory(fsUserCourseCategory));
     }
 
@@ -119,6 +155,14 @@ public class FsUserCourseCategoryController extends BaseController
     @GetMapping("/getCatePidList")
     public R getCatePidList()
     {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
+            List<OptionsVO> list = fsUserCourseCategoryService.selectFsUserCourseCategoryPidList(userId);
+            return R.ok().put("data", list);
+        }
         List<OptionsVO> list = fsUserCourseCategoryService.selectFsUserCourseCategoryPidList();
         return R.ok().put("data", list);
     }

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

@@ -202,6 +202,7 @@ public class FsUserCourseVideoController extends BaseController
         return R.ok();
     }
     @PostMapping("/batchUpdateRed")
+    @Log(title = "按课程批量保存设置红包金额", businessType = BusinessType.UPDATE)
     public R batchUpdateRed(@RequestBody List<BatchRedUpdate> list){
         fsUserCourseVideoService.batchUpdateRed(list);
         return R.ok();

+ 3 - 3
fs-admin/src/main/java/com/fs/his/controller/FsCompanyController.java

@@ -81,7 +81,7 @@ public class FsCompanyController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             company.setDeptId(loginUser.getDeptId());
         }
         List<CompanyVO> list = companyService.selectCompanyListVO(company);
@@ -96,7 +96,7 @@ public class FsCompanyController extends BaseController
         Long depId = null;
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             depId = loginUser.getDeptId();
         }
         List<OptionsVO> list = companyService.selectAllCompanyList(depId);
@@ -152,7 +152,7 @@ public class FsCompanyController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             company.setDeptId(loginUser.getDeptId());
         }
         return companyService.insertCompany(company);

+ 12 - 0
fs-admin/src/main/java/com/fs/his/controller/FsPackageController.java

@@ -147,6 +147,18 @@ public class FsPackageController extends BaseController
         return toAjax(fsPackageService.deleteFsPackageByPackageIds(packageIds));
     }
 
+
+    /**
+     * 批量复制套餐包
+     */
+    @PreAuthorize("@ss.hasPermi('his:package:bulkCopy')")
+    @Log(title = "套餐包", businessType = BusinessType.DELETE)
+    @GetMapping("/bulkCopy/{packageIds}")
+    public AjaxResult bulkCopy(@PathVariable Long[] packageIds)
+    {
+        return toAjax(fsPackageService.bulkCopyFsPackageByPackage(packageIds));
+    }
+
     /**
      * 查询套餐包列表
      */

+ 60 - 12
fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java

@@ -2,10 +2,7 @@ package com.fs.his.controller;
 
 import java.math.BigDecimal;
 import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import cn.hutool.core.util.StrUtil;
@@ -14,10 +11,7 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysRole;
 import com.fs.common.core.domain.entity.SysUser;
 import com.fs.common.core.domain.model.LoginUser;
-import com.fs.common.utils.ParseUtils;
-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.company.param.CompanyStoreOrderMoneyLogsListParam;
 import com.fs.company.service.ICompanyMoneyLogsService;
 import com.fs.company.vo.CompanyStoreOrderMoneyLogsVO;
@@ -38,6 +32,7 @@ import com.fs.his.dto.ExpressInfoDTO;
 import com.fs.his.dto.StoreOrderExpressExportDTO;
 import com.fs.his.dto.TracesDTO;
 import com.fs.his.enums.FsStoreOrderLogEnum;
+import com.fs.his.enums.FsStoreOrderStatusEnum;
 import com.fs.his.enums.ShipperCodeEnum;
 import com.fs.his.param.FsFollowMsgParam;
 import com.fs.his.param.FsStoreOrderParam;
@@ -224,11 +219,19 @@ public class FsStoreOrderController extends BaseController
     @PreAuthorize("@ss.hasPermi('store:storeOrder:export')")
     @Log(title = "导出订单", businessType = BusinessType.EXPORT)
     @GetMapping("/orderExport")
-    public AjaxResult orderExport(FsStoreOrderParam param) {
+    public AjaxResult orderExport(FsStoreOrderParam param,String filter) {
         Integer exportType1 = exportTaskService.isExportType1(SecurityUtils.getUserId());
 //        if (exportType1>0){
 //            return AjaxResult.error("你已经有正在导出的任务");
 //        }
+        // 1. 处理filter参数:将逗号分隔的字符串拆分为ArrayList<String>
+        ArrayList<String> filterList = new ArrayList<>();
+        if (StringUtils.isNotBlank(filter)) {
+            // 按逗号拆分,同时去除可能的空格(如filter传"orderId, orderCode"时兼容)
+            String[] filterArr = filter.split("\\s*,\\s*");
+            filterList.addAll(Arrays.asList(filterArr));
+        }
+
         if (fsStoreOrderService.isEntityNull(param)){
             return AjaxResult.error("请筛选数据导出");
         }
@@ -255,7 +258,7 @@ public class FsStoreOrderController extends BaseController
         exportTaskService.insertFsExportTask(task);
         param.setTaskId(task.getTaskId());
         boolean checkPhone = isCheckPhone();
-        exportTaskService.exportStore1Data(param,checkPhone);
+        exportTaskService.exportStore1Data(param,checkPhone, filterList);
 
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
 
@@ -359,7 +362,11 @@ public class FsStoreOrderController extends BaseController
             moneyLogsMap.setBusinessId(order.getOrderId());
             tuiMoneyLogs=moneyLogsService.selectCompanyStoreOrderMoneyLogsList(moneyLogsMap);
         }
-        return R.ok().put("data",order).put("tuiMoneyLogs",tuiMoneyLogs);
+        if ((CloudHostUtils.hasCloudHostName("金牛明医"))){
+            return R.ok().put("data",order).put("tuiMoneyLogs",tuiMoneyLogs).put("isUpdateRefund",1).put("isUpdatePayRemain",1);
+        } else {
+            return R.ok().put("data",order).put("tuiMoneyLogs",tuiMoneyLogs).put("isUpdateRefund",0).put("isUpdatePayRemain",0);
+        }
     }
 
     @GetMapping(value = "/queryPhone/{orderId}")
@@ -415,9 +422,40 @@ public class FsStoreOrderController extends BaseController
     @PutMapping
     public AjaxResult edit(@RequestBody FsStoreOrder fsStoreOrder)
     {
+        AjaxResult error = moneyCheck(fsStoreOrder);
+        if (error != null) return error;
         return toAjax(fsStoreOrderService.updateFsStoreOrder(fsStoreOrder));
     }
 
+    private AjaxResult moneyCheck(FsStoreOrder fsStoreOrder) {
+        BigDecimal payRemain = fsStoreOrder.getPayRemain();
+        BigDecimal payPrice = fsStoreOrder.getPayPrice();
+        if (payRemain != null && payPrice == null){
+            return AjaxResult.error("订单应收金额不正确!");
+        }
+        if (payRemain == null && payPrice != null){
+            return AjaxResult.error("订单物流代收金额不正确!");
+        }
+        if (payRemain != null && payPrice != null){
+            FsStoreOrder temp = fsStoreOrderService.selectFsStoreOrderByOrderId(fsStoreOrder.getOrderId());
+            if (!((Objects.equals(temp.getStatus(), FsStoreOrderStatusEnum.STATUS_2.getValue())
+                    || (Objects.equals(temp.getStatus(), FsStoreOrderStatusEnum.STATUS_1.getValue())))
+                    || (StringUtils.isNotBlank(temp.getExtendOrderId())))
+            ){
+
+                BigDecimal payMoney = temp.getPayMoney();
+                if (fsStoreOrder.getPayMoney()!=null){
+                    fsStoreOrder.setPayMoney(payMoney);
+                }
+                BigDecimal tempPayPrice = payMoney.add(payRemain);
+                if(0 != tempPayPrice.compareTo(fsStoreOrder.getPayPrice())){
+                    return AjaxResult.error("订单金额不正确!");
+                }
+            }
+        }
+        return null;
+    }
+
     /**
      * 修改订单
      */
@@ -578,7 +616,15 @@ public class FsStoreOrderController extends BaseController
     {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         fsStoreOrder.setOperator(loginUser.getUser().getNickName());
-        return toAjax(fsStoreOrderService.afterSales(fsStoreOrder));
+        if (fsStoreOrder.getRefundAmount() != null){
+            if (fsStoreOrder.getRefundList()!=null && !fsStoreOrder.getRefundList().isEmpty()){
+                return toAjax(fsStoreOrderService.afterSalesByProduct(fsStoreOrder));
+            } else {
+                return AjaxResult.error("没有选择需要退款的商品!");
+            }
+        } else {
+            return toAjax(fsStoreOrderService.afterSales(fsStoreOrder));
+        }
     }
 
     /**
@@ -658,6 +704,7 @@ public class FsStoreOrderController extends BaseController
                 df.setOrderId(orderId);
                 FsStoreOrderDf temp = fsStoreOrderDfService.selectFsStoreOrderDfByOrderId(df.getOrderId());
                 if (temp == null){
+                    df.setParcelQuantity(param.getParcelQuantity()); //设置包裹数量
                     fsStoreOrderDfService.insertFsStoreOrderDf(df);
                     fsStoreOrderLogsService.create(orderId, FsStoreOrderLogEnum.SET_PUSH_ACCOUNT.getValue(),
                             nickName + " " +FsStoreOrderLogEnum.SET_PUSH_ACCOUNT.getDesc() + ":" + df.getLoginAccount());
@@ -702,6 +749,7 @@ public class FsStoreOrderController extends BaseController
         orderIds.forEach(orderId->{
             df.setOrderId(orderId);
             FsStoreOrderDf temp = fsStoreOrderDfService.selectFsStoreOrderDfByOrderId(df.getOrderId());
+            df.setParcelQuantity(param.getParcelQuantity());
             if (temp != null){
                 df.setUpdateTime(new Date());
                 fsStoreOrderDfService.updateFsStoreOrderDf(df);

+ 7 - 1
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -62,6 +62,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
 
 @Slf4j
 @Component("task")
@@ -555,9 +556,14 @@ public class Task {
         if (erpOrderService !=null && erpOrderService == dfOrderService){
             orders = fsStoreOrderMapper.selectShippedOrder();
             if(orders!=null&& !orders.isEmpty()){
+                List<CompletableFuture<Void>> futures = new ArrayList<>();
                 for(FsStoreOrder order:orders){
-                    erpOrderService.getOrderDeliveryStatus(order);
+                    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                        erpOrderService.getOrderDeliveryStatus(order);
+                    });
+                    futures.add(future);
                 }
+                CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
             }
         }
     }

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

@@ -179,6 +179,19 @@ public class FsStoreProductScrmController extends BaseController
         return toAjax(fsStoreProductService.deleteFsStoreProductByIds(productIds));
     }
 
+
+    /**
+     * 批量复制商品
+     */
+    @PreAuthorize("@ss.hasPermi('store:storeProduct:bulkCopy')")
+    @Log(title = "商品管理", businessType = BusinessType.UPDATE,isStoreLog = true,
+            logParamExpression ="#p0.length>1?new String[]{'商品','批量复制商品信息'}: new String[]{'商品','复制商品信息'}" )
+    @GetMapping("/bulkCopy/{productIds}")
+    public R bulkCopy(@PathVariable Long[] productIds)
+    {
+        return fsStoreProductService.bulkCopyFsStoreProductByIds(productIds);
+    }
+
     @ApiOperation(value = "生成属性")
     @PostMapping(value = "/genFormatAttr/{productId}")
     public ResponseEntity genFormatAttr(@PathVariable Long productId, @RequestBody String jsonStr,Long[] stores){

+ 94 - 0
fs-admin/src/main/java/com/fs/qw/FsCourseTask.java

@@ -1,11 +1,27 @@
 package com.fs.qw;
 
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.qw.service.IQwWorkTaskService;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import io.jsonwebtoken.lang.Assert;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.http.util.Asserts;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
 /**
  * 后台统计相关 定时任务
  */
@@ -16,7 +32,85 @@ public class FsCourseTask {
     private IFsCourseWatchLogService fsCourseWatchLogService;
     @Autowired
     private IQwWorkTaskService qwWorkTaskService;
+    @Autowired
+    private RedisCache redisCache;
+    private static final String REDPACKET_COMPANY_MONEY_CHANGE = "redpacket_money_CHANGE";
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+    private static final String REDPACKET_POOL_LOCK = "redpacket_pool_lock";
+    private static final String REDPACKET_COMPANY_MONEY = "redpacket_money";
+
+    @Autowired
+    private RedissonClient redissonClient;
+
+    /**
+     * 润天公司账户充值
+     * @param chargeMoney 充值金额
+     */
+    public void rechargeRedpacketMoney(String chargeMoney){
+        Assert.notNull(chargeMoney,"充值金额不能为空!");
+
+        log.info("润天公司账户充值 充值金额: {}",chargeMoney);
+
+        RLock lock = redissonClient.getLock(REDPACKET_POOL_LOCK);
+        try{
+            boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
+
+            if (!locked) {
+                log.error("获取锁失败...");
+                return;
+            }
+
+            BigDecimal redPacketCompanyMoney = redisCache.getCacheObject(REDPACKET_COMPANY_MONEY);
+            if(ObjectUtils.isNull(redPacketCompanyMoney)){
+                redPacketCompanyMoney = BigDecimal.ZERO;
+            }
+            BigDecimal value = redPacketCompanyMoney.add(new BigDecimal(chargeMoney));
+
+            log.info("润天公司账户充值成功 目前余额: {}",value);
 
+            redisCache.setCacheObject(REDPACKET_COMPANY_MONEY,value);
+
+
+            // 保存到数据库
+            SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("company.money");
+
+            sysConfig.setConfigValue(value.setScale(4, RoundingMode.HALF_UP).toPlainString());
+            sysConfig.setConfigKey("company.money");
+            sysConfigMapper.updateConfig(sysConfig);
+
+        }catch (Exception e){
+            log.error("充值失败 原因:{}", ExceptionUtils.getFullStackTrace(e),e);
+            throw new RuntimeException(e);
+        }finally {
+            if (lock.isHeldByCurrentThread()) {
+                lock.unlock();
+            }
+        }
+    }
+    /**
+     * 公司红包金额变更
+     */
+    public void redpacketCompanyMoneyChange(){
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("company.money");
+        Asserts.notNull(sysConfig,"公司账户配置不能为空!");
+
+        String configValue = sysConfig.getConfigValue();
+        Asserts.notNull(configValue,"公司账户余额不能为空");
+
+        BigDecimal oldBigDecimal = new BigDecimal(configValue);
+
+        BigDecimal redPacketCompanyMoney = redisCache.getCacheObject(REDPACKET_COMPANY_MONEY);
+
+        log.info("公司账户红包余额变更 {} -> {}",oldBigDecimal,redPacketCompanyMoney);
+
+        // 如果两者不一致才同步到数据库
+        if(oldBigDecimal.compareTo(redPacketCompanyMoney) != 0){
+            sysConfig.setConfigValue(redPacketCompanyMoney.setScale(4, RoundingMode.HALF_UP).toPlainString());
+            sysConfig.setConfigKey("company.money");
+            sysConfigMapper.updateConfig(sysConfig);
+        }
+    }
     /**
      * 添加企微观看日志
      * @throws Exception

+ 2 - 2
fs-admin/src/main/java/com/fs/qw/controller/QwCompanyController.java

@@ -57,7 +57,7 @@ public class QwCompanyController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             qwCompany.setCreateDeptId(loginUser.getDeptId());
             qwCompany.setCreateUserId(loginUser.getUserId());
         }
@@ -74,7 +74,7 @@ public class QwCompanyController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if(!loginUser.isAdmin() && config.getIsBound() != null && config.getIsBound()){
+        if(!loginUser.isAdmin() && config.getDept() != null && config.getDept()){
             deptId = loginUser.getDeptId();
             if(config.getDept() == null || !config.getDept()){
                 userId = loginUser.getUserId();

+ 2 - 2
fs-admin/src/main/resources/application.yml

@@ -4,11 +4,11 @@ server:
 # Spring配置
 spring:
   profiles:
-#    active: druid-myhk-test
+    active: druid-jnmy-test
 #    active: druid-hdt
 #    active: druid-yzt
 #    active: druid-sxjz
 #    active: druid-sft
 #    active: druid-fby
-    active: dev-yjb
+#    active: dev
 

+ 36 - 0
fs-common-api/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -65,7 +67,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {
@@ -90,7 +109,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 10 - 1
fs-common/src/main/java/com/fs/common/core/domain/entity/SysRole.java

@@ -61,12 +61,21 @@ public class SysRole extends BaseEntity
 
     /** 是否可以查看手机全号 0否 1是 */
     private Integer isCheckPhone;
-
+    /** 是否可以查看地址全号 0否 1是 */
+    private Integer isCheckAddress;
     public SysRole()
     {
 
     }
 
+    public Integer getIsCheckAddress() {
+        return isCheckAddress;
+    }
+
+    public void setIsCheckAddress(Integer isCheckAddress) {
+        this.isCheckAddress = isCheckAddress;
+    }
+
     public SysRole(Long roleId)
     {
         this.roleId = roleId;

+ 61 - 0
fs-common/src/main/java/com/fs/common/core/redis/RedisCache.java

@@ -240,4 +240,65 @@ public class RedisCache
     public Long incr(final String key,final Long delta) {
         return redisTemplate.opsForValue().increment(key, delta);
     }
+
+    /**
+     * 获得list对象的value
+     *
+     * @param key 缓存的键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> T getVoiceAllList(final String key)
+    {
+        return (T) redisTemplate.opsForList().range(key,0,-1);
+    }
+    /**
+     * 获得缓存的对象id
+     *
+     * @param key 缓存的键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> T popVoiceKey(final String key)
+    {
+        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
+            return (T) redisTemplate.opsForList().leftPop(key);
+        }
+        return null;
+    }
+    /**
+     * 删除缓存Map
+     *
+     * @param key
+     * @param hKey
+     */
+    public <T> void deleteCacheMap(final String key, final String hKey)
+    {
+        if (hKey != null) {
+            redisTemplate.opsForHash().delete(key, hKey);
+        }
+    }
+
+    /**
+     * 添加List消息数据
+     *
+     * @param key 缓存的键值
+     * @param dataList 待缓存的List消息数据
+     * @return 缓存的对象
+     */
+    public <T> long setVoiceList(final String key, final List<T> dataList)
+    {
+        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
+        return count == null ? 0 : count;
+    }
+
+    /**
+     * 添加List消息数据的key
+     *
+     * @param key 缓存的键值
+     * @param value 待缓存的值
+     * @return 缓存的对象
+     */
+    public <T> void setVoice(final String key, final T value)
+    {
+        redisTemplate.opsForList().rightPush(key, value);
+    }
 }

+ 140 - 0
fs-common/src/main/java/com/fs/common/utils/poi/ExcelUtil.java

@@ -1274,4 +1274,144 @@ public class ExcelUtil<T>
         }
         return sheetIndexPicMap;
     }
+    /**
+     * 对list数据源将其里面的数据导入到excel表单(只导出选中的列)
+     *
+     * @param list 导出数据集合
+     * @param sheetName 工作表的名称
+     * @param selectedFields 选中的字段列表
+     * @return 结果
+     */
+    public AjaxResult exportExcelSelectedColumns(List<T> list, String sheetName, List<String> selectedFields) {
+        this.init(list, sheetName, Type.EXPORT);
+        return exportExcelSelectedColumns(selectedFields);
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单(只导出选中的列)
+     *
+     * @param response 返回数据
+     * @param list 导出数据集合
+     * @param sheetName 工作表的名称
+     * @param selectedFields 选中的字段列表
+     * @return 结果
+     * @throws IOException
+     */
+    public void exportExcelSelectedColumns(HttpServletResponse response, List<T> list, String sheetName, List<String> selectedFields) throws IOException {
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        this.init(list, sheetName, Type.EXPORT);
+        exportExcelSelectedColumns(response.getOutputStream(), selectedFields);
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单(只导出选中的列)
+     *
+     * @param selectedFields 选中的字段列表
+     * @return 结果
+     */
+    public void exportExcelSelectedColumns(OutputStream out, List<String> selectedFields) {
+        try {
+            writeSelectedSheet(selectedFields);
+            wb.write(out);
+        } catch (Exception e) {
+            log.error("导出Excel异常{}", e.getMessage());
+        } finally {
+            IOUtils.closeQuietly(wb);
+            IOUtils.closeQuietly(out);
+        }
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单(只导出选中的列)
+     *
+     * @param selectedFields 选中的字段列表
+     * @return 结果
+     */
+    public AjaxResult exportExcelSelectedColumns(List<String> selectedFields) {
+        OutputStream out = null;
+        try {
+            writeSelectedSheet(selectedFields);
+            String filename = encodingFilename(sheetName);
+            out = new FileOutputStream(getAbsoluteFile(filename));
+            wb.write(out);
+            return AjaxResult.success(filename);
+        } catch (Exception e) {
+            log.error("导出Excel异常{}", e.getMessage());
+            throw new UtilException("导出Excel失败,请联系网站管理员!");
+        } finally {
+            IOUtils.closeQuietly(wb);
+            IOUtils.closeQuietly(out);
+        }
+    }
+
+    /**
+     * 创建写入数据到Sheet(只写入选中的列)
+     */
+    public void writeSelectedSheet(List<String> selectedFields) {
+        // 筛选出选中的字段
+        List<Object[]> selectedFieldList = filterSelectedFields(selectedFields);
+
+        // 取出一共有多少个sheet.
+        double sheetNo = Math.ceil(list.size() / sheetSize);
+        for (int index = 0; index <= sheetNo; index++) {
+            createSheet(sheetNo, index);
+
+            // 产生一行
+            Row row = sheet.createRow(0);
+            int column = 0;
+            // 写入各个字段的列头名称(只写入选中的字段)
+            for (Object[] os : selectedFieldList) {
+                Excel excel = (Excel) os[1];
+                this.createCell(excel, row, column++);
+            }
+            if (Type.EXPORT.equals(type)) {
+                fillSelectedExcelData(index, row, selectedFieldList);
+                addStatisticsRow();
+            }
+        }
+    }
+
+    /**
+     * 筛选出选中的字段
+     */
+    private List<Object[]> filterSelectedFields(List<String> selectedFields) {
+        if (selectedFields == null || selectedFields.isEmpty()) {
+            return this.fields; // 如果没有选择字段,返回所有字段
+        }
+
+        List<Object[]> selectedFieldList = new ArrayList<>();
+        for (Object[] fieldInfo : this.fields) {
+            Field field = (Field) fieldInfo[0];
+            if (selectedFields.contains(field.getName())) {
+                selectedFieldList.add(fieldInfo);
+            }
+        }
+        return selectedFieldList;
+    }
+
+    /**
+     * 填充excel数据(只填充选中的列)
+     *
+     * @param index 序号
+     * @param row 单元格行
+     * @param selectedFieldList 选中的字段列表
+     */
+    public void fillSelectedExcelData(int index, Row row, List<Object[]> selectedFieldList) {
+        int startNo = index * sheetSize;
+        int endNo = Math.min(startNo + sheetSize, list.size());
+        for (int i = startNo; i < endNo; i++) {
+            row = sheet.createRow(i + 1 - startNo);
+            // 得到导出对象.
+            T vo = (T) list.get(i);
+            int column = 0;
+            for (Object[] os : selectedFieldList) {
+                Field field = (Field) os[0];
+                Excel excel = (Excel) os[1];
+                // 设置实体类私有属性可访问
+                field.setAccessible(true);
+                this.addCell(excel, row, vo, field, column++);
+            }
+        }
+    }
 }

+ 40 - 0
fs-company-app/src/main/java/com/fs/core/config/RedisConfig.java

@@ -12,6 +12,8 @@ import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -62,6 +64,44 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {

+ 14 - 5
fs-company/src/main/java/com/fs/company/controller/course/FsCourseAnswerLogsController.java

@@ -1,5 +1,7 @@
 package com.fs.company.controller.course;
 
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
 import com.fs.common.annotation.Log;
 import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.controller.BaseController;
@@ -8,11 +10,13 @@ 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.course.config.CourseConfig;
 import com.fs.course.param.FsCourseAnswerLogsParam;
 import com.fs.course.service.IFsCourseAnswerLogsService;
 import com.fs.course.vo.FsCourseAnswerLogsListVO;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
+import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -38,6 +42,9 @@ public class FsCourseAnswerLogsController extends BaseController
 
     @Autowired
     private TokenService tokenService;
+
+    @Autowired
+    private ISysConfigService configService;
     /**
      * 查询答题日志列表
      */
@@ -48,11 +55,13 @@ public class FsCourseAnswerLogsController extends BaseController
         if (param.getPhoneMk() != null && param.getPhoneMk() != "") {
             param.setPhone(param.getPhoneMk());
         }
-
-//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        // 如果他可以看两个销售公司就会查不出来
-//        param.setCompanyId( loginUser.getCompany().getCompanyId());
-
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
+            param.setCompanyId( loginUser.getCompany().getCompanyId());
+        }
         List<FsCourseAnswerLogsListVO> list = fsCourseAnswerLogsService.selectFsCourseAnswerLogsListVONew(param);
         TableDataInfo rspData = new TableDataInfo();
         rspData.setCode(HttpStatus.SUCCESS);

+ 14 - 4
fs-company/src/main/java/com/fs/company/controller/course/FsCourseRedPacketLogController.java

@@ -1,5 +1,7 @@
 package com.fs.company.controller.course;
 
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.controller.BaseController;
@@ -9,6 +11,7 @@ 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.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseRedPacketLog;
 import com.fs.course.mapper.FsUserCourseMapper;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
@@ -19,6 +22,7 @@ import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 import com.fs.his.utils.PhoneUtil;
 import com.fs.his.vo.OptionsVO;
+import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -45,7 +49,8 @@ public class FsCourseRedPacketLogController extends BaseController
     FsUserCourseVideoMapper fsUserCourseVideoMapper;
     @Autowired
     private TokenService tokenService;
-
+    @Autowired
+    private ISysConfigService configService;
     /**
      * 查询短链课程看课记录列表
      */
@@ -57,9 +62,14 @@ public class FsCourseRedPacketLogController extends BaseController
         if (fsCourseRedPacketLog.getPhoneMk() != null && fsCourseRedPacketLog.getPhoneMk() != "") {
             fsCourseRedPacketLog.setPhone(encryptPhone(fsCourseRedPacketLog.getPhoneMk()));
         }
-//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-        // 如果他可以看两个销售公司就会查不出来
-//        fsCourseRedPacketLog.setCompanyId( loginUser.getCompany().getCompanyId());
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long userId = loginUser.getUser().getUserId();
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (ObjectUtil.isNotEmpty(config.getIsBound())&&config.getIsBound()){
+            fsCourseRedPacketLog.setCompanyId( loginUser.getCompany().getCompanyId());
+        }
 
         List<FsCourseRedPacketLogListPVO> list = fsCourseRedPacketLogService.selectFsCourseRedPacketLogListVO(fsCourseRedPacketLog);
         for (FsCourseRedPacketLogListPVO fsCourseRedPacketLogListPVO : list) {

+ 2 - 0
fs-company/src/main/java/com/fs/company/controller/course/FsUserCoursePeriodController.java

@@ -191,6 +191,7 @@ public class FsUserCoursePeriodController extends BaseController {
 
     @ApiOperation("按课程批量保存设置红包金额")
     @PostMapping("/batchRedPacket")
+    @Log(title = "按课程批量保存设置红包金额", businessType = BusinessType.UPDATE)
     public R batchRedPacketMoney(@RequestBody List<FsUserCourseVideoRedPackage> videoRedPackageList) {
         try {
             fsUserCourseVideoRedPackageService.batchSaveCompanyRedPackage(videoRedPackageList);
@@ -203,6 +204,7 @@ public class FsUserCoursePeriodController extends BaseController {
 
     @ApiOperation("按营期批量保存设置红包金额")
     @PostMapping("/batchRedPacket/byPeriod")
+    @Log(title = "按营期批量保存设置红包金额", businessType = BusinessType.UPDATE)
     public R batchRedPacketByPeriod(@RequestBody List<FsBatchPeriodRedPackageParam> periodRedPackageList) {
         try {
             fsUserCourseVideoRedPackageService.batchRedPacketByPeriod(periodRedPackageList);

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

@@ -36,7 +36,7 @@ public class FsUserCourseTrainingCampController {
     /**
      * 查询训练营列表
      */
-    @PreAuthorize("@ss.hasPermi('course:trainingCamp:list')")
+    //@PreAuthorize("@ss.hasPermi('course:trainingCamp:list')")
     @GetMapping("/list")
     public AjaxResult list(@RequestParam(required = false) String trainingCampName,
                            @RequestParam(required = false) String userId,

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

@@ -25,7 +25,7 @@ public class FsUserOperationLogController extends BaseController {
     @Autowired
     private IFsUserOperationLogService fsUserOperationLogService;
 
-    @PreAuthorize("@ss.hasPermi('his:userOperationLog:list')")
+    //@PreAuthorize("@ss.hasPermi('his:userOperationLog:list')")
     @GetMapping("/list")
     public TableDataInfo list(FsUserOperationLog fsUserOperationLog)
     {

+ 80 - 4
fs-company/src/main/java/com/fs/company/controller/qw/QwUserVoiceLogController.java

@@ -1,12 +1,18 @@
 package com.fs.company.controller.qw;
 
 import com.fs.common.annotation.Log;
+import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.PageDomain;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.page.TableSupport;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.service.ICompanyUserService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
 import com.fs.qw.domain.QwUserVoiceLog;
@@ -22,8 +28,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * 企微用户通话记录Controller
@@ -44,6 +52,9 @@ public class QwUserVoiceLogController extends BaseController
     @Autowired
     private IQwTagService iQwTagService;
 
+    @Autowired
+    private ICompanyUserService userService;
+
     /**
      * 查询企微用户通话记录列表
      */
@@ -137,23 +148,88 @@ public class QwUserVoiceLogController extends BaseController
     @GetMapping("/totalList")
     public TableDataInfo totalList(QwUserVoiceLogTotalVo qwUserVoiceLog)
     {
-        startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
         qwUserVoiceLog.setQwUserId(1L);
         List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
-        return getDataTable(list);
+        list.forEach(item->{
+            if(item.getQwExternalContact() != null){
+                if (item.getQwExternalContact().getTagIds() != null && !Objects.equals(item.getQwExternalContact().getTagIds(), "[]")) {
+                    QwTagSearchParam param = new QwTagSearchParam();
+                    Gson gson = new Gson();
+                    List<String> tagIds = gson.fromJson(
+                            item.getQwExternalContact().getTagIds(),
+                            new TypeToken<List<String>>() {
+                            }.getType()
+                    );
+                    param.setTagIds(tagIds);
+                    item.setTagIdsName(iQwTagService.selectQwTagListByTagIds(param));
+                }
+            }
+        });
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
     }
 
 
     @GetMapping("/sellTotalList")
     public TableDataInfo sellTotalList(QwUserVoiceLogTotalVo qwUserVoiceLog)
     {
-        startPage();
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         qwUserVoiceLog.setCompanyId(loginUser.getCompany().getCompanyId());
         List<QwUserVoiceLogTotalVo> list = qwUserVoiceLogService.selectQwUserVoiceLogTotalList(qwUserVoiceLog);
-        return getDataTable(list);
+
+        // 获取分页参数
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+
+        int total = list.size();
+        // 在内存中进行分页处理
+        if (pageNum != null && pageSize != null) {
+            int fromIndex = (pageNum - 1) * pageSize;
+            int toIndex = Math.min(fromIndex + pageSize, total);
+
+            // 确保索引不越界
+            if (fromIndex < total) {
+                list = list.subList(fromIndex, toIndex);
+            } else {
+                list = new ArrayList<>(); // 返回空列表
+            }
+        }
+
+        // 构造返回结果
+        TableDataInfo rspData = new TableDataInfo();
+        rspData.setCode(HttpStatus.SUCCESS);
+        rspData.setMsg("查询成功");
+        rspData.setRows(list);
+        rspData.setTotal(total);
+        return rspData;
     }
 
     @PreAuthorize("@ss.hasPermi('qw:qwUserVoiceLog:sellTotalExport')")

+ 2 - 2
fs-company/src/main/java/com/fs/company/controller/store/FsStoreOrderController.java

@@ -130,7 +130,7 @@ public class FsStoreOrderController extends BaseController
         task.setCompanyUserId(userId);
         exportTaskService.insertFsExportTask(task);
         param.setTaskId(task.getTaskId());
-        exportTaskService.exportStore1Data(param,false);
+        exportTaskService.exportStore1Data(param,false, null);
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
 
 
@@ -172,7 +172,7 @@ public class FsStoreOrderController extends BaseController
         task.setCompanyUserId(userId);
         exportTaskService.insertFsExportTask(task);
         param.setTaskId(task.getTaskId());
-        exportTaskService.exportStore1Data(param,false);
+        exportTaskService.exportStore1Data(param,false, null);
         return new AjaxResult(200,"后台正在导出,请等待...任务ID:"+task.getTaskId(),task.getTaskId());
     }
     /**

+ 37 - 0
fs-company/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -19,6 +19,7 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
 import java.util.Arrays;
 
 /**
@@ -55,6 +56,25 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public RedisTemplate<String, Boolean> redisTemplateForBoolean(RedisConnectionFactory connectionFactory) {
         RedisTemplate<String, Boolean> template = new RedisTemplate<>();
@@ -96,7 +116,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 39 - 0
fs-doctor-app/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -66,6 +68,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
 
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
@@ -92,6 +112,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 39 - 0
fs-framework/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -66,6 +68,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
 
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
@@ -92,6 +112,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 38 - 0
fs-hospital/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -16,6 +16,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -67,7 +69,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {
@@ -93,6 +112,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 40 - 0
fs-ipad-task/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -66,6 +68,24 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {
@@ -91,6 +111,26 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 38 - 0
fs-live-app/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -65,6 +67,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
 
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
@@ -91,6 +111,24 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 39 - 0
fs-qw-api-msg/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -66,6 +68,43 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {

+ 39 - 0
fs-qw-api/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -90,6 +92,43 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
 
     @Bean
     public DefaultRedisScript<Long> limitScript()

+ 39 - 0
fs-qw-mq/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -66,6 +68,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {
@@ -90,6 +111,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
 
     @Bean
     public DefaultRedisScript<Long> limitScript()

+ 40 - 0
fs-qw-task/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -66,6 +68,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {
@@ -91,6 +112,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 42 - 8
fs-qw-voice/src/main/java/com/fs/app/controller/CommonController.java

@@ -1,20 +1,24 @@
 package com.fs.app.controller;
 
 
-import com.fs.ad.enums.AdUploadType;
-import com.fs.ad.service.IAdHtmlClickLogService;
+import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.R;
-import com.fs.company.service.ICompanyWxChatService;
+import com.fs.common.core.page.TableDataInfo;
 import com.fs.fastgptApi.util.AudioUtils;
 import com.fs.fastgptApi.vo.AudioVO;
-import com.fs.wxUser.service.ICompanyWxUserService;
+import com.fs.sop.domain.QwSopTempVoice;
+import com.fs.sop.service.IQwSopTempVoiceService;
 import io.swagger.annotations.Api;
 import jdk.nashorn.internal.ir.annotations.Ignore;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
 
 
 @Slf4j
@@ -23,7 +27,8 @@ import org.springframework.web.bind.annotation.RestController;
 @AllArgsConstructor
 @Ignore
 @RequestMapping(value="/app/common")
-public class CommonController {
+public class CommonController extends BaseController {
+
 
     @GetMapping("/test")
     public R testSend(String voice, Long id){
@@ -31,4 +36,33 @@ public class CommonController {
         return R.ok().put("data", audioVO);
     }
 
+    @GetMapping("/voice")
+    public R voice(String voice, Long id){
+        AudioVO audioVO = AudioUtils.transferAudioSilkFromText(voice,id,  false);
+        return R.ok().put("data", audioVO);
+    }
+
+
+    /**
+     * 当只有模板文字text时,生成表中对应条的voice_url和user_voice_url
+     * @param id            qw_sop_temp_voice的id
+     * @return
+     */
+    @GetMapping("/createUserUrlAndUrl")
+    public R createUserUrlAndUrl(@Param("id") Long id,@Param("voiceTxt") String voiceTxt){
+        AudioVO audioVO = AudioUtils.transferCompanyIdAudioSilkFromText(voiceTxt,id,false);
+        return R.ok().put("data", audioVO);
+    }
+
+    /**
+     * 当只有user_voice_url时,生成表中对应条的voice_url
+     * @param userVoiceUrl  wav格式的语音文件
+     * @param id            qw_sop_temp_voice的id
+     * @return
+     */
+    @GetMapping("/createVoiceUrl")
+    public R createVoiceUrl( @RequestParam("id") Long id,@RequestParam("userVoiceUrl") String userVoiceUrl){
+        AudioVO audioVO = AudioUtils.transferAudioSilkFromUrl(userVoiceUrl,  false);
+        return R.ok().put("data", audioVO);
+    }
 }

+ 10 - 10
fs-qw-voice/src/main/java/com/fs/app/exception/FSException.java

@@ -5,26 +5,26 @@ package com.fs.app.exception;
  */
 public class FSException extends RuntimeException {
 	private static final long serialVersionUID = 1L;
-	
-    private String msg;
-    private int code = 500;
-    
-    public FSException(String msg) {
+
+	private String msg;
+	private int code = 500;
+
+	public FSException(String msg) {
 		super(msg);
 		this.msg = msg;
 	}
-	
+
 	public FSException(String msg, Throwable e) {
 		super(msg, e);
 		this.msg = msg;
 	}
-	
+
 	public FSException(String msg, int code) {
 		super(msg);
 		this.msg = msg;
 		this.code = code;
 	}
-	
+
 	public FSException(String msg, int code, Throwable e) {
 		super(msg, e);
 		this.msg = msg;
@@ -46,6 +46,6 @@ public class FSException extends RuntimeException {
 	public void setCode(int code) {
 		this.code = code;
 	}
-	
-	
+
+
 }

+ 99 - 12
fs-qw-voice/src/main/java/com/fs/app/mq/RocketMQConsumerService.java

@@ -1,29 +1,25 @@
 package com.fs.app.mq;
 
 import com.alibaba.fastjson.JSON;
-import com.fs.ad.service.IAdHtmlClickLogService;
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.annotation.DataSource;
 import com.fs.common.core.redis.RedisCacheT;
 import com.fs.common.enums.DataSourceType;
-import com.fs.common.utils.PubFun;
-import com.fs.qw.result.QwFilterSopCustomersResult;
-import com.fs.qw.vo.AdUploadVo;
+import com.fs.qw.service.IQwUserService;
+import com.fs.qw.vo.QwUserVO;
 import com.fs.sop.domain.QwSop;
 import com.fs.sop.domain.QwSopTempContent;
-import com.fs.sop.domain.QwSopTempDay;
-import com.fs.sop.mapper.QwSopMapper;
-import com.fs.sop.mapper.QwSopTempContentMapper;
-import com.fs.sop.params.QwSopTagsParam;
+import com.fs.sop.domain.QwSopTempVoice;
+import com.fs.sop.service.IQwSopService;
+import com.fs.sop.service.IQwSopTempContentService;
 import com.fs.sop.service.IQwSopTempDayService;
 import com.fs.sop.service.IQwSopTempVoiceService;
 import com.fs.sop.vo.VoiceVo;
-import com.fs.voice.utils.StringUtil;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.commons.beanutils.ConvertUtils;
 import org.apache.rocketmq.spring.core.RocketMQListener;
 import org.springframework.stereotype.Service;
-import org.springframework.web.bind.annotation.GetMapping;
 
 import java.util.Arrays;
 import java.util.List;
@@ -32,10 +28,14 @@ import java.util.stream.Collectors;
 @Slf4j
 @Service
 @AllArgsConstructor
-@RocketMQMessageListener(topic = "${rocketmq.consumer.topic}", consumerGroup = "${rocketmq.consumer.group}")
+//@RocketMQMessageListener(topic = "${rocketmq.consumer.topic}", consumerGroup = "${rocketmq.consumer.group}")
 public class RocketMQConsumerService implements RocketMQListener<String> {
     private final RedisCacheT<VoiceVo> redisCache;
     public final static String VOICE_CACHE_KEY = "voice:cache:";
+    private final IQwSopTempVoiceService qwSopTempVoiceService;
+    private final IQwSopTempContentService qwSopTempContentService;
+    private final IQwUserService qwUserService;
+    private final IQwSopService qwSopService;
 
     @Override
     @DataSource(DataSourceType.SOP)
@@ -44,6 +44,93 @@ public class RocketMQConsumerService implements RocketMQListener<String> {
         VoiceVo vo = JSON.parseObject(message, VoiceVo.class);
         vo.setGenerated(0);
         redisCache.setCacheObject(VOICE_CACHE_KEY + vo.getType() + ":" + vo.getId(), vo);
+
+        /*VoiceVo vo = JSON.parseObject(message, VoiceVo.class);
+        if(vo.getId() != null){
+            List<QwSopTempContent> contentList = qwSopTempContentService.selectQwSopTempContentByTempId(vo.getId());
+            if(contentList != null && !contentList.isEmpty()){
+                updateTempVoiceInfo(contentList);
+            }
+        }*/
     }
 
+    private void updateTempVoiceInfo(List<QwSopTempContent> voiceList) {
+        for (QwSopTempContent qwSopTempContent : voiceList) {
+            String tempId = qwSopTempContent.getTempId();
+            String content = qwSopTempContent.getContent();
+            JSONObject jsonObject = JSONObject.parseObject(content);
+            String text = jsonObject.getString("value");
+            List<QwSop> qwSopList = qwSopService.selectQwSopByTempId(tempId);//通过tempId查询出所有sop
+            if(qwSopList != null && !qwSopList.isEmpty()){
+                for (QwSop qwSop : qwSopList) {
+                    if(qwSop != null && qwSop.getQwUserIds() != null){
+                        //查询出所有的tempContent来筛选文字
+                        List<QwSopTempContent> qwSopTempContentList = qwSopTempContentService.selectQwSopTempContentByTempId(tempId);
+                        if(qwSopTempContentList != null && !qwSopTempContentList.isEmpty()){
+                            for (QwSopTempContent qwSopTemp : qwSopTempContentList) {
+                                if(qwSopTemp != null && qwSopTemp.getContentType() == 7){
+                                    String[] split = qwSop.getQwUserIds().split(",");
+                                    Long[] qwUserIds = (Long[]) ConvertUtils.convert(split, Long.class);
+                                    List<QwUserVO> qwUserVOS = qwUserService.selectQwUserVOByIds(qwUserIds);
+                                    if(qwUserVOS != null){
+                                        for (QwUserVO qwUserVO : qwUserVOS) {
+                                            Long companyUserId = qwUserVO.getCompanyUserId();
+                                            QwSopTempVoice qwSopTempVoice = qwSopTempVoiceService.selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(companyUserId,text);
+                                            if(qwSopTempVoice == null){
+                                                QwSopTempVoice sopTempVoice = new QwSopTempVoice();
+                                                sopTempVoice.setCompanyUserId(companyUserId);
+                                                sopTempVoice.setVoiceTxt(text);
+                                                sopTempVoice.setTempId(tempId);
+                                                sopTempVoice.setRecordType(0);
+                                                qwSopTempVoiceService.insertQwSopTempVoice(sopTempVoice);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        /*if(qwSop != null && qwSop.getQwUserIds() != null){
+            String tempId = qwSop.getTempId();
+            List<QwSopTempContent> voiceList = qwSopTempContentService.selectQwSopTempContentByTempId(tempId);
+            if(voiceList != null && !voiceList.isEmpty()){
+                for (QwSopTempContent qwSopTempContent : voiceList) {
+                    String content = qwSopTempContent.getContent();
+                    JSONObject jsonObject = JSONObject.parseObject(content);
+                    String text = jsonObject.getString("value");
+                    //查询出所有的tempContent来筛选文字
+                    List<QwSopTempContent> qwSopTempContentList = qwSopTempContentService.selectQwSopTempContentByTempId(tempId);
+                    if(qwSopTempContentList != null && !qwSopTempContentList.isEmpty()){
+                        for (QwSopTempContent qwSopTemp : qwSopTempContentList) {
+                            if(qwSopTemp != null && qwSopTemp.getContentType() == 7){
+                                String[] split = qwSop.getQwUserIds().split(",");
+                                Long[] qwUserIds = Arrays.stream(split)
+                                        .filter(s -> !s.isEmpty())
+                                        .map(Long::valueOf)
+                                        .toArray(Long[]::new);
+                                List<QwUserVO> qwUserVOS = qwUserService.selectQwUserVOByIds(qwUserIds);
+                                if(qwUserVOS != null){
+                                    for (QwUserVO qwUserVO : qwUserVOS) {
+                                        Long companyUserId = qwUserVO.getCompanyUserId();
+                                        QwSopTempVoice qwSopTempVoice = qwSopTempVoiceService.selectQwSopTempVoiceByCompanyUserIdAndVoiceTxt(companyUserId,text);
+                                        if(qwSopTempVoice == null){
+                                            QwSopTempVoice sopTempVoice = new QwSopTempVoice();
+                                            sopTempVoice.setCompanyUserId(companyUserId);
+                                            sopTempVoice.setVoiceTxt(text);
+                                            sopTempVoice.setTempId(tempId);
+                                            sopTempVoice.setRecordType(0);
+                                            qwSopTempVoiceService.insertQwSopTempVoice(sopTempVoice);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }*/
+    }
 }

+ 10 - 10
fs-qw-voice/src/main/java/com/fs/app/task/Task.java

@@ -38,14 +38,14 @@ public class Task {
 
     @Scheduled(cron = "0 * * * * *")
     @DataSource(DataSourceType.SOP)
-    public void scheduled(){
+    public void scheduled() {
         List<VoiceVo> list = redisCache.getCacheListByPattern(VOICE_CACHE_KEY + "*");
-        if(list.isEmpty()) return;
+        if (list.isEmpty()) return;
         VoiceVo vo = list.get(0);
-        if(vo.getType() == 0){
+        if (vo.getType() == 0) {
             synchronousTemp(Long.parseLong(vo.getId()));
         }
-        if(vo.getType() == 1){
+        if (vo.getType() == 1) {
             synchronousSop(vo.getId());
         }
         redisCache.deleteObject(VOICE_CACHE_KEY + vo.getType() + ":" + vo.getId());
@@ -53,19 +53,19 @@ public class Task {
 
 
     @DataSource(DataSourceType.SOP)
-    public void synchronousTemp(Long dayId){
+    public void synchronousTemp(Long dayId) {
         QwSopTempDay day = qwSopTempDayService.getById(dayId);
         List<QwSopTempContent> contents = qwSopTempContentMapper.listByTempAndDay(day.getTempId(), dayId);
         qwSopTempVoiceService.synchronousTemp(contents, day);
     }
 
     @DataSource(DataSourceType.SOP)
-    public void synchronousSop(String sopId){
+    public void synchronousSop(String sopId) {
         QwSop qwSop = qwSopMapper.selectQwSopById(sopId);
         QwSopTagsParam qwSopTagsParam = new QwSopTagsParam();
         //成员筛选
-        if (!StringUtil.strIsNullOrEmpty(qwSop.getQwUserIds())){
-            qwSopTagsParam.setUserIdsSelectList(Arrays.asList( qwSop.getQwUserIds().split(",")));
+        if (!StringUtil.strIsNullOrEmpty(qwSop.getQwUserIds())) {
+            qwSopTagsParam.setUserIdsSelectList(Arrays.asList(qwSop.getQwUserIds().split(",")));
         }
 
         //标过滤类型
@@ -73,13 +73,13 @@ public class Task {
 
 
         //标签
-        if (!StringUtil.strIsNullOrEmpty(qwSop.getTags())){
+        if (!StringUtil.strIsNullOrEmpty(qwSop.getTags())) {
             qwSopTagsParam.setTagsIdsSelectList(Arrays.asList(qwSop.getTags().split(",")));
         }
 
         //排除标签
         String excludeTags = qwSop.getExcludeTags();
-        if (!StringUtil.strIsNullOrEmpty(excludeTags)){
+        if (!StringUtil.strIsNullOrEmpty(excludeTags)) {
             qwSopTagsParam.setOutTagsIdsSelectList(Arrays.asList(excludeTags.split(",")));
         }
         qwSopTagsParam.setCropId(qwSop.getCorpId());

+ 13 - 3
fs-qw-voice/src/main/java/com/fs/framework/config/DataSourceConfig.java

@@ -21,7 +21,6 @@ import java.util.Map;
 
 @Configuration
 public class DataSourceConfig {
-
     @Bean
     @ConfigurationProperties(prefix = "spring.datasource.sop.druid.master")
     public DataSource sopDataSource() {
@@ -34,12 +33,23 @@ public class DataSourceConfig {
         return new DruidDataSource();
     }
 
+    @Bean
+    @ConfigurationProperties(prefix = "spring.datasource.mysql.druid.slave")
+    public DataSource slaveDataSource() {
+        return new DruidDataSource();
+    }
+
 
 
     @Bean
     @Primary
-    public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("sopDataSource") DataSource sopDataSource) {
+    public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
+                                        @Qualifier("sopDataSource") DataSource sopDataSource,
+                                        @Qualifier("slaveDataSource") DataSource slaveDataSource) {
         Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
+
+        targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource);
         targetDataSources.put(DataSourceType.SOP.name(), sopDataSource);
         return new DynamicDataSource(masterDataSource, targetDataSources);
     }
@@ -49,7 +59,7 @@ public class DataSourceConfig {
      */
     @SuppressWarnings({ "rawtypes", "unchecked" })
     @Bean
-    @ConditionalOnProperty(name = "spring.datasource.mysql.druid.statViewServlet.enabled", havingValue = "true")
+    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
     public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
     {
         // 获取web监控页面的参数

+ 4 - 4
fs-qw-voice/src/main/java/com/fs/framework/config/DruidConfig.java

@@ -30,7 +30,7 @@ package com.fs.framework.config;//package com.fs.framework.config;
 //public class DruidConfig
 //{
 //    @Bean
-//    @ConfigurationProperties("spring.datasource.mysql.druid.master")
+//    @ConfigurationProperties("spring.datasource.druid.master")
 //    public DataSource masterDataSource(DruidProperties druidProperties)
 //    {
 //        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
@@ -38,8 +38,8 @@ package com.fs.framework.config;//package com.fs.framework.config;
 //    }
 //
 //    @Bean
-//    @ConfigurationProperties("spring.datasource.mysql.druid.slave")
-//    @ConditionalOnProperty(prefix = "spring.datasource.mysql.druid.slave", name = "enabled", havingValue = "true")
+//    @ConfigurationProperties("spring.datasource.druid.slave")
+//    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
 //    public DataSource slaveDataSource(DruidProperties druidProperties)
 //    {
 //        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
@@ -80,7 +80,7 @@ package com.fs.framework.config;//package com.fs.framework.config;
 //     */
 //    @SuppressWarnings({ "rawtypes", "unchecked" })
 //    @Bean
-//    @ConditionalOnProperty(name = "spring.datasource.mysql.druid.statViewServlet.enabled", havingValue = "true")
+//    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
 //    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
 //    {
 //        // 获取web监控页面的参数

+ 37 - 0
fs-qw-voice/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -65,6 +67,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
 
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
@@ -90,7 +110,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 1 - 1
fs-qw-voice/src/main/resources/application.yml

@@ -6,4 +6,4 @@ server:
 # Spring配置
 spring:
   profiles:
-    active: dev
+    active: druid-jnmy-test

+ 39 - 0
fs-qwhook-msg/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -66,6 +68,24 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {
@@ -91,6 +111,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 38 - 0
fs-qwhook-sop/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -65,6 +67,24 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
 
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
@@ -91,6 +111,24 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 40 - 0
fs-qwhook/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -66,6 +68,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {
@@ -91,6 +112,25 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 18 - 0
fs-redis/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -68,6 +68,24 @@ public class RedisConfig extends CachingConfigurerSupport
         return template;
     }
 
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     @SuppressWarnings(value = { "unchecked", "rawtypes" })
     public RedisTemplate<String, Object> redisTemplateForObject(RedisConnectionFactory connectionFactory) {

+ 37 - 0
fs-repeat-api/src/main/java/com/fs/framework/config/RedisConfig.java

@@ -15,6 +15,8 @@ import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.data.redis.serializer.GenericToStringSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
+import java.math.BigDecimal;
+
 /**
  * redis配置
  *
@@ -90,7 +92,42 @@ public class RedisConfig extends CachingConfigurerSupport
         template.afterPropertiesSet();
         return template;
     }
+    @Bean
+    public RedisTemplate<String, Integer> redisTemplateForInteger(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Integer> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(Integer.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
+    @Bean
+    public RedisTemplate<String, BigDecimal> redisTemplateForBigDecimal(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, BigDecimal> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+
+        // 使用GenericToStringSerializer保证BigDecimal精度不丢失
+        template.setValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
 
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(new GenericToStringSerializer<>(BigDecimal.class));
+
+        template.afterPropertiesSet();
+        return template;
+    }
     @Bean
     public DefaultRedisScript<Long> limitScript()
     {

+ 59 - 0
fs-service/src/main/java/com/fs/aiChat/domain/InterestAiChatMsg.java

@@ -0,0 +1,59 @@
+package com.fs.aiChat.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+@Data
+public class InterestAiChatMsg extends BaseEntity {
+    /** $column.columnComment */
+    private Long msgId;
+
+    /** 消息id */
+    @Excel(name = "消息id")
+    private Long sessionId;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private String userId;
+
+    /** 消息内容 */
+    @Excel(name = "消息内容")
+    private String content;
+
+    /** 发送类型 1用户发送 2ai发送 */
+    @Excel(name = "发送类型 1用户发送 2ai发送")
+    private Integer sendType;
+
+    /** 角色ID */
+    @Excel(name = "角色ID")
+    private Long roleId;
+
+    /** 角色名称 */
+    @Excel(name = "角色名称")
+    private String roleName;
+
+    /** 昵称 */
+    @Excel(name = "昵称")
+    private String nickName;
+
+    /** 头像 */
+    @Excel(name = "头像")
+    private String avatar;
+
+    /** 用户输入的令牌数量 */
+    @Excel(name = "用户输入的令牌数量")
+    private Long promptTokens;
+
+    /** 生成的回复中使用的令牌 */
+    @Excel(name = "生成的回复中使用的令牌")
+    private Long completionTokens;
+
+    /** 总令牌数量 */
+    @Excel(name = "总令牌数量")
+    private Long totalTokens;
+
+    /** 角色头像 */
+    @Excel(name = "角色头像")
+    private String roleAvatar;
+}

+ 73 - 0
fs-service/src/main/java/com/fs/aiChat/domain/InterestAiSession.java

@@ -0,0 +1,73 @@
+package com.fs.aiChat.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class InterestAiSession extends BaseEntity {
+
+
+    /** 会话ID */
+    private Long sessionId;
+
+    /** 会话标识 */
+    @Excel(name = "会话标识")
+    private String chatId;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long userId;
+
+    /** 客服ID */
+    @Excel(name = "客服ID")
+    private Long roleId;
+
+    /** 客服名称 */
+    @Excel(name = "客服名称")
+    private String roleName;
+
+    /** 状态 1会话中 2已结束 */
+    @Excel(name = "状态 1会话中 2已结束")
+    private Integer status;
+
+    /** 客户昵称 */
+    @Excel(name = "客户昵称")
+    private String nickName;
+
+    /** 头像 */
+    @Excel(name = "头像")
+    private String avatar;
+
+    /**
+     * 当前会话最后一条消息记录
+     */
+    @Excel(name = "最后一条消息记录")
+    private String lastContent;
+
+    /**
+     * 角色头像
+     */
+    @Excel(name = "角色头像")
+    private String roleAvatar;
+
+    /**
+     * 标签
+     */
+    @Excel(name = "标签")
+    private String roleTag;
+
+    @Excel(name = "欢迎语")
+    private String welcomeMessage;
+
+    @Excel(name = "文本说明")
+    private String textDescription;
+
+    @Excel(name = "最后回复时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date lastCreateTime;
+
+}

+ 54 - 0
fs-service/src/main/java/com/fs/aiChat/domain/SessionRoleInfo.java

@@ -0,0 +1,54 @@
+package com.fs.aiChat.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+@Data
+public class SessionRoleInfo extends BaseEntity {
+
+    /** ID */
+    private Long roleId;
+
+    /** 角色名称 */
+    @Excel(name = "角色名称")
+    private String roleName;
+
+    /** 标签 */
+    @Excel(name = "标签")
+    private String roleTag;
+
+    /** 模型key */
+    @Excel(name = "模型key")
+    private String appKey;
+
+    /** 客服头像 */
+    @Excel(name = "客服头像")
+    private String avatar;
+
+    /** 欢迎语 */
+    @Excel(name = "欢迎语")
+    private String welcomeMessage;
+
+    @Excel(name = "封面图片")
+    private String imageUrl;
+
+    @Excel(name = "文本描述")
+    private String textDescription;
+
+    /** 提示词列表 */
+    @Excel(name = "提示词列表")
+    private String wordList;
+
+    @Excel(name = "标题")
+    private String title;
+
+    @Excel(name = "是否推荐")
+    private int isRecommend;
+
+    @Excel(name = "推荐图")
+    private String recommendImgUrl;
+
+    @Excel(name = "会话ID")
+    private Long sessionId;
+}

+ 59 - 0
fs-service/src/main/java/com/fs/aiChat/mapper/InterestAiChatSessionMapper.java

@@ -0,0 +1,59 @@
+package com.fs.aiChat.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.aiChat.domain.DoctorAiChatLog;
+import com.fs.aiChat.domain.InterestAiChatMsg;
+import com.fs.aiChat.domain.InterestAiSession;
+import com.fs.aiChat.domain.SessionRoleInfo;
+import com.fs.his.domain.FsInterestAiMsg;
+import com.fs.his.domain.FsInterestAiRole;
+import com.fs.his.domain.FsInterestAiSession;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+
+public interface InterestAiChatSessionMapper extends BaseMapper<DoctorAiChatLog> {
+
+    /**
+     * 查询用户对话列表
+     * @param userId
+     * @return
+     */
+    List<InterestAiSession> getSessionListByUserId(Long userId);
+
+    /**
+     * 根据roleid获取会话角色信息
+     * @param roleId
+     * @return
+     */
+    SessionRoleInfo getSessionRoleInfoByRoleId(Long roleId);
+
+    /**
+     * 获取聊天记录
+     * @param sessionId
+     * @return
+     */
+    List<InterestAiChatMsg> getSessionChatList(String sessionId);
+
+    /**
+     * 获取会话
+     * @param roleId
+     * @param userId
+     * @return
+     */
+    @Select("  select session_id,chat_id,user_id,role_id,role_name, status, nick_name,avatar,create_time,update_time\n" +
+            "            from fs_interest_ai_session where role_id = #{roleId} and user_id = #{userId} limit 1")
+    FsInterestAiSession getInterestAiSessionByRoleIdAndUserId(@Param(value =  "roleId")Long roleId,@Param(value = "userId") Long userId);
+
+
+    int insertFsInterestAiSession(FsInterestAiSession fsInterestAiSession);
+
+    int insertFsInterestAiMsg(FsInterestAiMsg fsInterestAiMsg);
+
+    List<SessionRoleInfo> getRecommendRoleList(String userId);
+
+
+    FsInterestAiRole selectFsInterestAiRoleByRoleId(Long roleId);
+}

+ 15 - 0
fs-service/src/main/java/com/fs/aiChat/param/InterestAiMessage.java

@@ -0,0 +1,15 @@
+package com.fs.aiChat.param;
+
+import lombok.Data;
+
+@Data
+public class InterestAiMessage {
+    Long userId;
+    String nickName;
+    String avatar;
+    Long roleId;
+    String roleName;
+    String message;
+    Long sessionId;
+//    Integer isWelcome;
+}

+ 2 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyConfigService.java

@@ -67,4 +67,6 @@ public interface ICompanyConfigService
     String selectConfigByKey(String configKey);
 
     CompanyConfig selectCompanyConfigByServerKey(String key);
+
+    String selectRedPacketConfigByKey(Long companyId);
 }

+ 2 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java

@@ -236,4 +236,6 @@ public interface ICompanyUserService {
     R bindDoctor(CompanyUser param);
 
     R unBindDoctor(Long userId);
+
+    R getBindInfo(Long companyUserId);
 }

+ 38 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyConfigServiceImpl.java

@@ -8,10 +8,15 @@ import com.fs.company.domain.CompanyConfig;
 import com.fs.company.mapper.CompanyConfigMapper;
 import com.fs.company.service.ICompanyConfigService;
 import com.fs.system.domain.SysConfig;
+import org.apache.http.util.Asserts;
+import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 
 import static com.fs.common.utils.DictUtils.getCacheKey;
 
@@ -28,6 +33,10 @@ public class CompanyConfigServiceImpl implements ICompanyConfigService
     private RedisCache redisCache;
     @Autowired
     private CompanyConfigMapper companyConfigMapper;
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate; // 注入RedisTemplate
+    private static final String REDIS_KEY_PREFIX = "red_packet_config:";
+    private static final long CACHE_TIMEOUT = 24 * 60 * 60;
 
     /**
      * 查询参数配置
@@ -133,4 +142,33 @@ public class CompanyConfigServiceImpl implements ICompanyConfigService
     public CompanyConfig selectCompanyConfigByServerKey(String key) {
         return companyConfigMapper.selectCompanyConfigByServerKey(key);
     }
+
+    @Override
+    public String selectRedPacketConfigByKey(Long companyId) {
+        Asserts.notNull(companyId,"公司id不能为空!");
+        String redisKey = REDIS_KEY_PREFIX + companyId;
+        String cachedConfig = redisTemplate.opsForValue().get(redisKey);
+        if (cachedConfig != null) {
+            return cachedConfig;
+        }
+        synchronized (getSynchronizationObject(companyId)) {
+            cachedConfig = redisTemplate.opsForValue().get(redisKey);
+
+            if (cachedConfig != null) {
+                return cachedConfig;
+            }
+            String configFromDb = companyConfigMapper.selectRedPacketConfigByKey(companyId);
+            if (configFromDb != null) {
+                redisTemplate.opsForValue().set(redisKey, configFromDb, CACHE_TIMEOUT, TimeUnit.SECONDS);
+            } else {
+                redisTemplate.opsForValue().set(redisKey, "", 5 * 60, TimeUnit.SECONDS);
+            }
+            return configFromDb;
+        }
+    }
+
+    private static final ConcurrentHashMap<Long, Object> LOCKS = new ConcurrentHashMap<>();
+    private static Object getSynchronizationObject(Long companyId) {
+        return LOCKS.computeIfAbsent(companyId, k -> new Object());
+    }
 }

+ 31 - 2
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.BeanCopyUtils;
+import com.fs.common.QRutils;
 import com.fs.common.annotation.DataScope;
 import com.fs.common.constant.UserConstants;
 import com.fs.common.core.domain.AjaxResult;
@@ -54,6 +55,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.math.BigDecimal;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
@@ -106,8 +108,6 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     @Autowired
     private ICompanyService companyService;
 
-    @Autowired
-    private IQwExternalContactService qwExternalContactService;
 
     @Autowired
     private IQwUserService qwUserService;
@@ -977,4 +977,33 @@ public class CompanyUserServiceImpl implements ICompanyUserService
             return R.error();
         }
     }
+
+
+    private final static String bindBaseUrl = "https://userapp.jnmyunl.com/bindcompanyuser?companyUserId=";
+    /**
+     * @param companyUserId
+     * @return
+     */
+    @Override
+    public R getBindInfo(Long companyUserId) {
+        //链接
+        String url = bindBaseUrl + companyUserId;
+        //查询码是否存在
+        CompanyUser companyUser = selectCompanyUserById(companyUserId);
+        if (companyUser != null) {
+            if (StringUtils.isBlank(companyUser.getBindCode())) {
+                //生成二维码
+                InputStream qrCodeStream = QRutils.getQRCodeImageInputStream(url, 250, 250);
+                CloudStorageService storage = OSSFactory.build();
+                if (storage != null) {
+                    String link = storage.uploadSuffix(qrCodeStream, ".jpg");
+                    companyUser.setBindCode(link);
+                    updateCompanyUser(companyUser);
+
+                }
+            }
+            return R.ok().put("url", url).put("imageUrl", companyUser.getBindCode());
+        }
+        return R.error();
+    }
 }

+ 9 - 0
fs-service/src/main/java/com/fs/config/ai/AiHostProper.java

@@ -13,4 +13,13 @@ public class AiHostProper {
     @Value("${ipad.aiApi}")
     private String aiApi;
 
+    @Value("${ipad.voiceApi}")
+    private String voiceApi;
+
+
+    @Value("${ipad.commonApi}")
+    private String commonApi;
+
+
+
 }

+ 6 - 1
fs-service/src/main/java/com/fs/core/config/RedissonConfig.java

@@ -19,6 +19,9 @@ public class RedissonConfig {
     @Value("${spring.redis.host}")
     private String host;
 
+    @Value("${spring.redis.database}")
+    private int database;
+
     @Bean(destroyMethod = "shutdown")
     public RedissonClient redissonClient() {
         String address = "redis://" + host + ":" + port;
@@ -26,7 +29,9 @@ public class RedissonConfig {
         config.useSingleServer()
                 .setAddress(address) // 设置连接地址
                 .setConnectionMinimumIdleSize(10) // 最小空闲连接数
-                .setConnectionPoolSize(64); // 最大连接数
+                .setConnectionPoolSize(64) // 最大连接数
+                .setDatabase(database); //数据库
+
         if (null!=password&&!"".equals(password)) {
             config.useSingleServer().setPassword(password);
         }

+ 15 - 24
fs-service/src/main/java/com/fs/core/config/WxMaConfiguration.java

@@ -35,8 +35,10 @@ import org.yaml.snakeyaml.events.Event;
 import javax.annotation.PostConstruct;
 import java.io.File;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -105,20 +107,7 @@ public class WxMaConfiguration {
     }
 
     public static WxMaService getMaService(String appid) {
-        // 从缓存获取
-        WxMaService wxService = maServices.get(appid);
-        if (wxService != null) {
-            return wxService;
-        }
-
-        // 缓存未命中,查询数据库
-        synchronized (WxMaConfiguration.class) {
-            // 双重检查
-            wxService = maServices.get(appid);
-            if (wxService != null) {
-                return wxService;
-            }
-
+        return maServices.computeIfAbsent(appid,e->{
             // 查询数据库
             FsCoursePlaySourceConfigMapper configMapper = SpringUtils.getBean(FsCoursePlaySourceConfigMapper.class);
             Wrapper<FsCoursePlaySourceConfig> queryWrapper = Wrappers.<FsCoursePlaySourceConfig>lambdaQuery()
@@ -129,11 +118,8 @@ public class WxMaConfiguration {
                 throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
             }
 
-            WxMaService service = getWxMaService(config.getAppid(), config.getSecret(), config.getToken(), config.getAesKey(), config.getMsgDataFormat());
-            maServices.put(appid, service);
-            log.info("Initialized WxMaService for appid: {}", appid);
-            return service;
-        }
+            return getWxMaService(config.getAppid(), config.getSecret(), config.getToken(), config.getAesKey(), config.getMsgDataFormat());
+        });
     }
 
     /**
@@ -164,11 +150,16 @@ public class WxMaConfiguration {
         }
 
         maServices = configs.stream()
-            .map(a -> {
-                WxMaService service = getWxMaService(a.getAppid(), a.getSecret(), a.getToken(), a.getAesKey(), a.getMsgDataFormat());
-                routers.put(a.getAppid(), this.newRouter(service));
-                return service;
-            }).collect(Collectors.toMap(s -> s.getWxMaConfig().getAppid(), a -> a));
+                .map(a -> {
+                    WxMaService service = getWxMaService(a.getAppid(), a.getSecret(), a.getToken(), a.getAesKey(), a.getMsgDataFormat());
+                    routers.put(a.getAppid(), this.newRouter(service));
+                    return service;
+                }).collect(Collectors.toMap(
+                        s -> s.getWxMaConfig().getAppid(),
+                        a -> a,
+                        (existing, replacement) -> replacement,
+                        ConcurrentHashMap::new
+                ));
     }
 
     private WxMaMessageRouter newRouter(WxMaService service) {

+ 10 - 10
fs-service/src/main/java/com/fs/core/utils/OrderCodeUtils.java

@@ -44,16 +44,16 @@ public class OrderCodeUtils {
 
     }
     public static String getOrderSn(){
-//        String url= FSConfig.getCommonApi()+ "/app/common/genOrderCode";
-////        String url= "42.194.245.189:8010/app/common/genOrderCode";
-//        String json = HttpRequest.get(url)
-//                .execute().body();
-//        OrderCodeVO vo= JSONUtil.toBean(json, OrderCodeVO.class);
-//        if(vo.getCode()==200){
-//            return vo.getOrderCode();
-//        }
-//        else return null;
-        return OrderCodeUtils.genOrderSn();
+        String url= FSConfig.getCommonApi()+ "/app/common/genOrderCode";
+//        String url= "42.194.245.189:8010/app/common/genOrderCode";
+        String json = HttpRequest.get(url)
+                .execute().body();
+        OrderCodeVO vo= JSONUtil.toBean(json, OrderCodeVO.class);
+        if(vo.getCode()==200){
+            return vo.getOrderCode();
+        }
+        else return null;
+        //return OrderCodeUtils.genOrderSn();
 
     }
 

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

@@ -90,4 +90,9 @@ public class FsCoursePlaySourceConfig {
     private Long createUserId;
     // 创建部门
     private Long createDeptId;
+
+    /**
+     * 是否是互医/商城小程序
+     */
+    private Integer isMall;
 }

+ 9 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseRedPacketLog.java

@@ -64,4 +64,13 @@ public class FsCourseRedPacketLog extends BaseEntity
 
     private String appId;//小程序appId
 
+    /**
+     * 账户余额扣减前
+     */
+    private BigDecimal accBalanceBefore;
+    /**
+     * 账户余额扣减后
+     */
+    private BigDecimal accBalanceAfter;
+
 }

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

@@ -40,4 +40,6 @@ public class FsUserCourseCategory extends BaseEntity
 
     private String pic;
 
+
+    private Long userId;
 }

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

@@ -519,4 +519,9 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
     Long selectByWatchlxDay(@Param("userId") Long userId,@Param("projectId")  Long projectId);
 
     List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVOexport(@Param("maps") FsCourseWatchLogListParam param);
+
+    /**
+     * 查询训练营看课人数
+     */
+    Integer getUserCountByCampId(@Param("trainingCampId") Long trainingCampId);
 }

+ 3 - 0
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseCategoryMapper.java

@@ -71,6 +71,9 @@ public interface FsUserCourseCategoryMapper
     @Select("select cate_id dict_value, cate_name dict_label  from fs_user_course_category WHERE pid = 0 and is_del=0 ")
     List<OptionsVO> selectFsUserCourseCategoryPidList();
 
+    @Select("select cate_id dict_value, cate_name dict_label  from fs_user_course_category WHERE pid = 0 and is_del=0 and user_id = #{userId}")
+    List<OptionsVO> selectFsUserCourseCategoryPidListByUserId(Long userId);
+
     @Select("select cate_id dict_value, cate_name dict_label  from fs_user_course_category WHERE pid =#{pid} and is_del=0 ")
     List<OptionsVO> selectCateListByPid(Long pid);
 

+ 4 - 0
fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigCreateParam.java

@@ -45,4 +45,8 @@ public class FsCoursePlaySourceConfigCreateParam {
 
     @ApiModelProperty("所属公司")
     private Long companyId;
+
+    @ApiModelProperty("是否是互医/商城小程序")
+    private Integer isMall;
+    private Long createDeptId;
 }

+ 4 - 0
fs-service/src/main/java/com/fs/course/param/FsCoursePlaySourceConfigEditParam.java

@@ -42,4 +42,8 @@ public class FsCoursePlaySourceConfigEditParam {
 
     @ApiModelProperty("所属公司")
     private Long companyId;
+
+    @ApiModelProperty("是否是互医/商城小程序")
+    private Integer isMall;
+    private Long createDeptId;
 }

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

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

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

@@ -11,4 +11,5 @@ public class FsUserVipOrderPayUParam implements Serializable {
    private Long userId;
    @NotNull(message = "orderId不能为空")
    private Long orderId;
+   private String appId;
 }

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

@@ -9,7 +9,7 @@ import java.util.Map;
 
 /**
  * 微信用户和销售关系Service接口
- * 
+ *
  * @author fs
  * @date 2025-05-09
  */
@@ -75,6 +75,7 @@ public interface IFsUserCompanyUserService extends IService<FsUserCompanyUser>{
      * @param projectId   项目ID
      * @return FsUserCompanyUser
      */
+    FsUserCompanyUser selectByUserIdAndProjectId(Long userId, Long projectId,Long companyUserId);
     FsUserCompanyUser selectByUserIdAndProjectId(Long userId, Long projectId);
 
     /**

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

@@ -64,6 +64,7 @@ public interface IFsUserCourseCategoryService
     List<OptionsVO> selectFsUserCoursePidList();
 
     List<OptionsVO> selectFsUserCourseCategoryPidList();
+    List<OptionsVO> selectFsUserCourseCategoryPidList(Long userId);
 
     List<OptionsVO> selectCateListByPid(Long pid);
 }

+ 34 - 42
fs-service/src/main/java/com/fs/course/service/impl/FsCourseFinishTempServiceImpl.java

@@ -259,50 +259,15 @@ public class FsCourseFinishTempServiceImpl implements IFsCourseFinishTempService
 
             // 3. 检查是否需要更新
             boolean shouldUpdate = true;
-            int currentYear = Calendar.getInstance().get(Calendar.YEAR);
-
-            // Convert input dates to full YYYYMMDD format
-            String fullMonthDay = String.valueOf(currentYear) + monthDay; // becomes YYYYMMDD
-            String fullDayOfMonth = String.valueOf(currentYear) +
-                    String.format("%02d", Calendar.getInstance().get(Calendar.MONTH) + 1) +
-                    String.format("%02d", Integer.parseInt(dayOfMonth));
 
             for (String mark : allOldMarks) {
-                Matcher numMatcher = Pattern.compile("\\*(\\d{1,4})完").matcher(mark);
-                if (numMatcher.find()) {
-                    String numStr = numMatcher.group(1);
-                    String fullOldDate;
-
-                    if (numStr.length() <= 2) { // 处理 "*D完" 格式
-
-                        int day = Integer.parseInt(numStr);
-                        Calendar cal = Calendar.getInstance();
-                        int currentMonth = cal.get(Calendar.MONTH) + 1;
-                        int currentDay = cal.get(Calendar.DAY_OF_MONTH);
-                        int year = cal.get(Calendar.YEAR);
-
-                        // 获取上个月的最大天数
-                        int prevMonth = (currentMonth == 1) ? 12 : currentMonth - 1;
-                        int prevYear = (currentMonth == 1) ? year - 1 : year;
-                        cal.set(prevYear, prevMonth - 1, 1); // Calendar 月份是 0-based
-                        int maxDaysInPrevMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
-
-                        // 如果 day > 上个月的天数,说明是上个月的日期
-                        if (day > maxDaysInPrevMonth || day > currentDay + 7) {
-                            currentMonth = prevMonth;
-                            year = prevYear;
-                        }
-
-                        fullOldDate = String.format("%04d%02d%02d", year, currentMonth, day);
-
-                        // 比较日期
-                        if (Integer.parseInt(fullOldDate) >= Integer.parseInt(fullMonthDay) ||
-                                Integer.parseInt(fullOldDate) >= Integer.parseInt(fullDayOfMonth)) {
-                            shouldUpdate = false;
-                            break;
-                        }
-                    }
 
+                String normalizedOldMark = normalizeMarkFormat(mark);
+                // 直接比较字符串是否相等
+                if (normalizedOldMark.equals(newNotes) ||
+                        normalizedOldMark.equals(newNotesDay)) {
+                    shouldUpdate = false;
+                    break;
                 }
             }
 
@@ -349,7 +314,7 @@ public class FsCourseFinishTempServiceImpl implements IFsCourseFinishTempService
 
                         break;
                     } else {
-                        if (attempt==2 && (qwResult.getErrcode() == 45033 || qwResult.getErrcode()== -1 || qwResult.getErrcode()== 60020)) {
+                        if (attempt==2 && (qwResult.getErrcode() == 45033 || qwResult.getErrcode()== -1 || qwResult.getErrcode()== 60020 )) {
                             QwCourseFinishRemarkRty remarkRty=new QwCourseFinishRemarkRty();
                             remarkRty.setQwUserId(externalContact.getUserId());
                             remarkRty.setCorpId(externalContact.getCorpId());
@@ -385,6 +350,33 @@ public class FsCourseFinishTempServiceImpl implements IFsCourseFinishTempService
 
     }
 
+    /**
+     * 标准化标记格式,将不同格式统一为标准格式
+     * 例如: "*5完" -> "*05完", "*305完" -> "*0305完" (如果是3位数字)
+     */
+    private String normalizeMarkFormat(String mark) {
+        Matcher matcher = Pattern.compile("\\*(\\d{1,4})完").matcher(mark);
+        if (matcher.find()) {
+            String digits = matcher.group(1);
+
+            // 根据数字长度进行标准化
+            switch (digits.length()) {
+                case 1: // "*D完" -> "*0D完"
+                    return "*0" + digits + "完";
+                case 2: // "*DD完" -> 保持不变
+                    return mark;
+                case 3: // "*DDD完" -> 可能是 "*MDD完" 或 "*DDM完",统一为4位
+                    // 假设是月日格式,补零到4位 "*0MDD完"
+                    return "*0" + digits + "完";
+                case 4: // "*MMDD完" -> 保持不变
+                    return mark;
+                default:
+                    return mark;
+            }
+        }
+        return mark;
+    }
+
 //    @Override
 //    public void updateFsCourseFinishTempByCompanyUserId() {
 //        List<FsCourseFinishTemp> fsCourseFinishTemps = fsCourseFinishTempMapper.selectFsCourseFinishTempByCompanyList();

+ 85 - 15
fs-service/src/main/java/com/fs/course/service/impl/FsCourseProductOrderServiceImpl.java

@@ -7,6 +7,8 @@ import java.util.*;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
@@ -27,6 +29,7 @@ import com.fs.his.domain.*;
 import com.fs.his.dto.PayConfigDTO;
 import com.fs.his.enums.FsCourseProductOrderStatusEnum;
 import com.fs.his.mapper.FsStorePaymentMapper;
+import com.fs.his.mapper.FsUserWxMapper;
 import com.fs.his.param.ApplyCourseProductOrderRefundParam;
 import com.fs.his.param.FsCourseProductOrderComputeParam;
 import com.fs.his.param.FsCourseProductOrderDoPayParam;
@@ -125,8 +128,11 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
 
     private static final int CODE_LENGTH = 12;
 
+//    @Autowired
+//    private IFsUserWxService userWxService;
+
     @Autowired
-    private IFsUserWxService userWxService;
+    private FsUserWxMapper fsUserWxMapper;
 
 
     /**
@@ -271,9 +277,40 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
             return R.error("订单状态不正确");
         }
         FsUser user = userService.selectFsUserByUserId(courseProductOrder.getUserId());
-        FsUserWx fsUserWx = userWxService.selectByAppIdAndUserId(param.getAppId(), user.getUserId(), 1);
-        logger.info("用户微信信息==============={}",fsUserWx);
-        if (user != null && fsUserWx != null && StringUtils.isNotEmpty(fsUserWx.getOpenId())) {
+
+        String json = configService.selectConfigByKey("his.pay");
+        PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
+        String openId = null;
+        String appId = param.getAppId();
+        if (StringUtils.isNotBlank(appId)) {
+            //查询fs_user_wx的openId
+            Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                    .eq(FsUserWx::getFsUserId, user.getUserId())
+                    .eq(FsUserWx::getAppId, appId);
+            FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+            logger.info("用户微信信息==============={}",fsUserWx);
+            if (fsUserWx != null) {
+                openId = fsUserWx.getOpenId();
+            }
+        } else {
+            appId = payConfigDTO.getAppId();
+            openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+            if (StringUtils.isBlank(openId)){
+                Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                        .eq(FsUserWx::getFsUserId, user.getUserId())
+                        .eq(FsUserWx::getAppId, appId);
+                FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+                logger.info("用户微信信息==============={}",fsUserWx);
+                if (Objects.nonNull(fsUserWx)){
+                    openId = fsUserWx.getOpenId();
+                }
+            }
+        }
+
+//        FsUserWx fsUserWx = userWxService.selectByAppIdAndUserId(param.getAppId(), user.getUserId(), 1);
+//        logger.info("用户微信信息==============={}",fsUserWx);
+//        if (user != null && fsUserWx != null && StringUtils.isNotEmpty(fsUserWx.getOpenId())) {
+        if (user != null && StringUtils.isNotEmpty(openId)) {
             Map<String,Object> moneys= Maps.newHashMap();
             FsCourseProduct courseProduct = JSONUtil.toBean(courseProductOrder.getProductJson(), FsCourseProduct.class);
             moneys.put("payPrice",courseProduct.getProductPrice());
@@ -316,12 +353,43 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
         }
         FsUser user = userService.selectFsUserByUserId(param.getUserId());
         logger.info("用户信息==============={}",user);
-        FsUserWx fsUserWx = userWxService.selectByAppIdAndUserId(param.getAppId(), user.getUserId(), 1);
-        logger.info("用户微信信息==============={}",fsUserWx);
-        if (user != null && fsUserWx != null && StringUtils.isNotEmpty(fsUserWx.getOpenId())) {
+
+        String json = configService.selectConfigByKey("his.pay");
+        PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
+        String openId = null;
+        String appId = param.getAppId();
+        if (StringUtils.isNotBlank(appId)) {
+            //查询fs_user_wx的openId
+            Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                    .eq(FsUserWx::getFsUserId, user.getUserId())
+                    .eq(FsUserWx::getAppId, appId);
+            FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+            logger.info("用户微信信息==============={}",fsUserWx);
+            if (fsUserWx != null) {
+                openId = fsUserWx.getOpenId();
+            }
+        } else {
+            appId = payConfigDTO.getAppId();
+            openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+            if (StringUtils.isBlank(openId)){
+                Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                        .eq(FsUserWx::getFsUserId, user.getUserId())
+                        .eq(FsUserWx::getAppId, appId);
+                FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+                logger.info("用户微信信息==============={}",fsUserWx);
+                if (Objects.nonNull(fsUserWx)){
+                    openId = fsUserWx.getOpenId();
+                }
+            }
+        }
+
+//        FsUserWx fsUserWx = userWxService.selectByAppIdAndUserId(param.getAppId(), user.getUserId(), 1);
+//        logger.info("用户微信信息==============={}",fsUserWx);
+//        if (user != null && fsUserWx != null && StringUtils.isNotEmpty(fsUserWx.getOpenId())) {
+        if (user != null && StringUtils.isNotEmpty(openId)) {
             if (courseProduct.getProductPrice().compareTo(new BigDecimal(0))==1) {
-                String json = configService.selectConfigByKey("his.pay");
-                PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
+//                String json = configService.selectConfigByKey("his.pay");
+//                PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
                 String payCode =  OrderCodeUtils.getOrderSn();
                 if(StringUtils.isEmpty(payCode)){
                     return R.error("订单生成失败,请重试");
@@ -338,7 +406,7 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
                 storePayment.setCompanyId(courseProductOrder.getCompanyId());
                 storePayment.setCompanyUserId(courseProductOrder.getCompanyUserId());
                 storePayment.setRemark("拍商品订单支付");
-                storePayment.setOpenId(fsUserWx.getOpenId());
+                storePayment.setOpenId(openId);
                 storePayment.setUserId(user.getUserId());
                 storePayment.setBusinessId(courseProductOrder.getCourseOrderId().toString());
                 if (storePaymentService.insertFsStorePayment(storePayment) > 0) {
@@ -347,7 +415,7 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
                         WxPayConfig payConfig = new WxPayConfig();
                         SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("his.pay");
                         FsPayConfig fsPayConfig = new Gson().fromJson(sysConfig.getConfigValue(), FsPayConfig.class);
-                        payConfig.setAppId(fsPayConfig.getAppId());
+                        payConfig.setAppId(appId);
                         payConfig.setMchId(fsPayConfig.getWxMchId());
                         payConfig.setMchKey(fsPayConfig.getWxMchKey());
                         payConfig.setSubAppId(org.apache.commons.lang3.StringUtils.trimToNull(null));
@@ -356,7 +424,7 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
                         payConfig.setNotifyUrl(wxPayProperties.getNotifyUrl());
                         wxPayService.setConfig(payConfig);
                         WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
-                        orderRequest.setOpenid(fsUserWx.getOpenId());//公众号支付提供用户openid
+                        orderRequest.setOpenid(openId);//公众号支付提供用户openid
                         orderRequest.setBody("拍商品订单支付");
                         orderRequest.setOutTradeNo("product-" + storePayment.getPayCode());
                         orderRequest.setTotalFee(WxPayUnifiedOrderRequest.yuanToFen(storePayment.getPayMoney().toString()));//测试
@@ -378,7 +446,7 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
                         p.setLowOrderId("product-" + storePayment.getPayCode());
                         p.setBody("拍商品订单支付");
                         p.setIsMinipg("1");
-                        p.setOpenId(fsUserWx.getOpenId());
+                        p.setOpenId(openId);
                         p.setAttach("");
                         p.setStoreid("0");
                         CreateWxOrderResult wxOrder = payService.createWxOrder(p);
@@ -408,7 +476,7 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
                         }
                         o.setPayerName("微信用户"+phone);
                         o.setGoodsInfo("拍商品订单支付"); // 订单信息
-                        o.setOpenId(fsUserWx.getOpenId());
+                        o.setOpenId(openId);
                         o.setOrderType(3);
                         o.setOrderId(courseProductOrder.getCourseOrderId().toString());
                         TzBankResult<PayCreateOrderResult> result = tzBankService.createOrder(o);
@@ -421,7 +489,8 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
                         logger.info("创建汇付订单");
                         HuiFuCreateOrder o = new HuiFuCreateOrder();
                         o.setTradeType("T_MINIAPP");
-                        o.setOpenid(fsUserWx.getOpenId());
+                        o.setOpenid(openId);
+                        o.setAppId(appId);
                         o.setReqSeqId("product-"+storePayment.getPayCode());
                         o.setTransAmt(storePayment.getPayMoney().toString());
                         o.setGoodsDesc("拍商品订单支付");
@@ -430,6 +499,7 @@ public class FsCourseProductOrderServiceImpl extends ServiceImpl<FsCourseProduct
                         FsStorePayment mt=new FsStorePayment();
                         mt.setPaymentId(storePayment.getPaymentId());
                         mt.setTradeNo(result.getHf_seq_id());
+                        mt.setAppId(appId);
                         storePaymentService.updateFsStorePayment(mt);
                         return R.ok().put("isPay", 0).put("data", result).put("type", "hf");
                     }

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

@@ -17,7 +17,7 @@ import java.util.Objects;
 
 /**
  * 微信用户和销售关系Service业务层处理
- * 
+ *
  * @author fs
  * @date 2025-05-09
  */
@@ -111,6 +111,15 @@ public class FsUserCompanyUserServiceImpl extends ServiceImpl<FsUserCompanyUserM
      * @param projectId   项目ID
      * @return FsUserCompanyUser
      */
+    @Override
+    public FsUserCompanyUser selectByUserIdAndProjectId(Long userId, Long projectId,Long companyUserId) {
+        LambdaQueryWrapper<FsUserCompanyUser> queryWrapper = Wrappers.<FsUserCompanyUser>lambdaQuery()
+                .eq(FsUserCompanyUser::getUserId, userId)
+                .eq(FsUserCompanyUser::getProjectId, projectId)
+                .eq(FsUserCompanyUser::getCompanyUserId, companyUserId);
+        return getOne(queryWrapper);
+    }
+
     @Override
     public FsUserCompanyUser selectByUserIdAndProjectId(Long userId, Long projectId) {
         LambdaQueryWrapper<FsUserCompanyUser> queryWrapper = Wrappers.<FsUserCompanyUser>lambdaQuery()

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

@@ -105,7 +105,10 @@ public class FsUserCourseCategoryServiceImpl implements IFsUserCourseCategorySer
         return fsUserCourseCategoryMapper.selectFsUserCourseCategoryPidList();
     }
 
-
+    @Override
+    public List<OptionsVO> selectFsUserCourseCategoryPidList(Long userId) {
+        return fsUserCourseCategoryMapper.selectFsUserCourseCategoryPidListByUserId(userId);
+    }
     @Override
     public List<OptionsVO> selectCateListByPid(Long pid) {
         return fsUserCourseCategoryMapper.selectCateListByPid(pid);

+ 63 - 6
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseOrderServiceImpl.java

@@ -377,15 +377,40 @@ public class FsUserCourseOrderServiceImpl implements IFsUserCourseOrderService
         String json = configService.selectConfigByKey("his.pay");
         PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
 
-        String openId = Objects.isNull(user) ? "" : user.getMaOpenId();
-        if (StringUtils.isBlank(openId)){
+//        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();
+//            }
+//        }
+
+        String openId = null;
+        String appId = param.getAppId();
+        if (StringUtils.isNotBlank(appId)) {
+            //查询fs_user_wx的openId
             Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
                     .eq(FsUserWx::getFsUserId, param.getUserId())
-                    .eq(FsUserWx::getAppId, payConfigDTO.getAppId());
+                    .eq(FsUserWx::getAppId, appId);
             FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
-            if (Objects.nonNull(fsUserWx)){
+            if (fsUserWx != null) {
                 openId = fsUserWx.getOpenId();
             }
+        } else {
+            appId = payConfigDTO.getAppId();
+            openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+            if (StringUtils.isBlank(openId)){
+                Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                        .eq(FsUserWx::getFsUserId, param.getUserId())
+                        .eq(FsUserWx::getAppId, appId);
+                FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+                if (Objects.nonNull(fsUserWx)){
+                    openId = fsUserWx.getOpenId();
+                }
+            }
         }
 
         if(user!=null&& StringUtils.isNotEmpty(openId)) {
@@ -484,6 +509,7 @@ public class FsUserCourseOrderServiceImpl implements IFsUserCourseOrderService
                         HuiFuCreateOrder o = new HuiFuCreateOrder();
                         o.setTradeType("T_MINIAPP");
                         o.setOpenid(openId);
+                        o.setAppId(appId);
                         o.setReqSeqId("course-" + storePayment.getPayCode());
                         o.setTransAmt(storePayment.getPayMoney().toString());
                         o.setGoodsDesc("课程订单支付");
@@ -491,6 +517,7 @@ public class FsUserCourseOrderServiceImpl implements IFsUserCourseOrderService
                         FsStorePayment mt = new FsStorePayment();
                         mt.setPaymentId(storePayment.getPaymentId());
                         mt.setTradeNo(result.getHf_seq_id());
+                        mt.setAppId(appId);
                         storePaymentService.updateFsStorePayment(mt);
                         return R.ok().put("isPay", 0).put("data", result).put("type", "hf");
 
@@ -521,14 +548,42 @@ public class FsUserCourseOrderServiceImpl implements IFsUserCourseOrderService
 //            }
 //        }
         FsUser user=fsUserMapper.selectFsUserByUserId(param.getUserId());
+
+        String json = configService.selectConfigByKey("his.pay");
+        PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
+        String openId = null;
+        String appId = param.getAppId();
+        if (StringUtils.isNotBlank(appId)) {
+            //查询fs_user_wx的openId
+            Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                    .eq(FsUserWx::getFsUserId, param.getUserId())
+                    .eq(FsUserWx::getAppId, appId);
+            FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+            if (fsUserWx != null) {
+                openId = fsUserWx.getOpenId();
+            }
+        } else {
+            appId = payConfigDTO.getAppId();
+            openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+            if (StringUtils.isBlank(openId)){
+                Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                        .eq(FsUserWx::getFsUserId, param.getUserId())
+                        .eq(FsUserWx::getAppId, appId);
+                FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+                if (Objects.nonNull(fsUserWx)){
+                    openId = fsUserWx.getOpenId();
+                }
+            }
+        }
+
         if(user!=null){
             if(order.getPayMoney().compareTo(new BigDecimal(0))==1){
                 String payCode =  OrderCodeUtils.getOrderSn();
                 if(StringUtils.isEmpty(payCode)){
                     return R.error("订单生成失败,请重试");
                 }
-                String json = configService.selectConfigByKey("his.pay");
-                PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
+//                String json = configService.selectConfigByKey("his.pay");
+//                PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
                 FsStorePayment storePayment=new FsStorePayment();
                 storePayment.setStatus(0);
                 storePayment.setPayMode(payConfigDTO.getType());
@@ -574,10 +629,12 @@ public class FsUserCourseOrderServiceImpl implements IFsUserCourseOrderService
                         o.setReqSeqId("course-"+storePayment.getPayCode());
                         o.setTransAmt(storePayment.getPayMoney().toString());
                         o.setGoodsDesc("课程订单支付");
+                        o.setAppId(appId);
                         HuifuCreateOrderResult result = huiFuService.createOrder(o);
                         FsStorePayment mt=new FsStorePayment();
                         mt.setPaymentId(storePayment.getPaymentId());
                         mt.setTradeNo(result.getHf_seq_id());
+                        mt.setAppId(appId);
                         storePaymentService.updateFsStorePayment(mt);
                         return R.ok().put("isPay", 0).put("data", result).put("type", "hf");
                     }

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

@@ -11,10 +11,7 @@ import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.domain.FsUserCoursePeriodDays;
 import com.fs.course.domain.FsUserCourseTrainingCamp;
 import com.fs.course.dto.FsUserCourseTrainingCampDTO;
-import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
-import com.fs.course.mapper.FsUserCoursePeriodMapper;
-import com.fs.course.mapper.FsUserCourseTrainingCampMapper;
-import com.fs.course.mapper.FsUserCourseVideoRedPackageMapper;
+import com.fs.course.mapper.*;
 import com.fs.course.service.IFsUserCoursePeriodDaysService;
 import com.fs.course.service.IFsUserCourseTrainingCampService;
 import com.fs.course.vo.FsUserCourseTrainingCampVO;
@@ -46,6 +43,7 @@ public class FsUserCourseTrainingCampServiceImpl extends ServiceImpl<FsUserCours
     private final FsUserCoursePeriodDaysMapper fsUserCoursePeriodDaysMapper;
 
     private final IFsUserCoursePeriodDaysService fsUserCoursePeriodDaysService;
+    private final FsCourseWatchLogMapper fsCourseWatchLogMapper;
 
     /**
      * 查询训练营列表
@@ -54,7 +52,9 @@ public class FsUserCourseTrainingCampServiceImpl extends ServiceImpl<FsUserCours
      */
     @Override
     public List<FsUserCourseTrainingCampVO> selectFsUserCourseTrainingCampVOListByMap(Map<String, Object> params) {
-        return baseMapper.selectFsUserCourseTrainingCampVOListByMap(params);
+        List<FsUserCourseTrainingCampVO> trainingCampVOS = baseMapper.selectFsUserCourseTrainingCampVOListByMap(params);
+        trainingCampVOS.forEach(camp -> camp.setVipCount(fsCourseWatchLogMapper.getUserCountByCampId(camp.getTrainingCampId())));
+        return trainingCampVOS;
     }
 
     /**

+ 302 - 124
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.BeanCopyUtils;
 import com.fs.common.core.domain.R;
@@ -62,28 +63,37 @@ import com.fs.qwApi.service.QwApiService;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.SopUserLogsInfoMapper;
 import com.fs.sop.service.ISopUserLogsInfoService;
+import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysDictDataMapper;
 import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
 import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
+import com.google.common.collect.Sets;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
+import org.jetbrains.annotations.NotNull;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.redisson.client.RedisClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.scheduling.annotation.Async;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Collectors;
@@ -106,6 +116,31 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     private Boolean isNewWxMerchant;
     private static final Logger logger = LoggerFactory.getLogger(FsUserCourseVideoServiceImpl.class);
 
+    /**
+     * 红包账户锁
+     */
+    private static final String REDPACKET_POOL_LOCK = "redpacket_pool_lock";
+
+    /**
+     * 公司红包金额
+     */
+    private static final String REDPACKET_COMPANY_MONEY = "redpacket_money";
+
+    /**
+     * 用户领取红包限制
+     */
+    private static final String REDPACKET_USER_LIMIT = "redpacket_user_limit:%s:%d";
+
+    /**
+     * 红包改变记录
+     */
+    private static final String REDPACKET_COMPANY_MONEY_CHANGE = "redpacket_money_change";
+
+    /**
+     * 是否开启红包账户扣减
+     */
+    @Value("${enableRedPackAccount:0}")
+    private String ENABLE_RED_PACK_ACCOUNT;
 
     private static final String miniappRealLink = "/pages_course/video.html?course=";
     private static final String REAL_LINK_PREFIX = "/courseH5/pages/course/learning?course=";
@@ -141,6 +176,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     private AsyncIsAddKfXfkService xfkService;
 
 
+    @Autowired
+    private RedissonClient redissonClient;
     @Autowired
     private QwExternalContactMapper qwExternalContactMapper;
     @Autowired
@@ -221,6 +258,17 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     @Autowired
     ConfigUtil configUtil;
 
+    @Autowired
+    private RedisTemplate<String,BigDecimal> redisTemplate;
+
+    @Autowired
+    private RedisTemplate<String,Integer> redisTemplateInteger;
+
+    /**
+     * 红包领取数量限制 默认一个用户当天最多只能领取10个
+     */
+    @Value("${RED_PACKET_LIMIT_COUNT:10}")
+    private Integer RED_PACKET_LIMIT_COUNT;
 
     /**
      * 查询课堂视频
@@ -417,7 +465,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
     @Override
     public R isAddKf(FsUserCourseVideoAddKfUParam param) {
-        logger.info("zyp \n【判断添加客服】:{}",param);
+        logger.info("【判断添加客服】:{}",param);
         //查询用户
         FsUser fsUser = fsUserMapper.selectFsUserByUserId(param.getUserId());
         //用户不存在唤起重新授权
@@ -597,7 +645,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         log.setQwUserId(Long.valueOf(param.getQwUserId()));
         log.setCreateTime(new Date());
         log.setLogType(3);
-        logger.info("zyp \n【群聊生成看课记录】:{}",param);
+        logger.info("【群聊生成看课记录】:{}",param);
         courseWatchLogMapper.insertFsCourseWatchLog(log);
     }
 
@@ -792,7 +840,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     public R getInternetTraffic(FsUserCourseVideoFinishUParam param) {
         try {
             if (param.getBufferRate()==null){
-                logger.error("zyp \n【缓冲值空】参数: {}",param);
+                logger.error("【缓冲值空】参数: {}",param);
                 return R.error("缓冲值空");
             }
             FsCourseTrafficLog trafficLog = new FsCourseTrafficLog();
@@ -829,7 +877,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         } catch (Exception e) {
             e.printStackTrace();
             // 打印参数param和异常信息
-            logger.error("zyp \n【插入或更新流量失败】参数: {}, 错误信息:{}", param, e.getMessage(), e);
+            logger.error("【插入或更新流量失败】参数: {}, 错误信息:{}", param, e.getMessage(), e);
             return R.error();
         }
         return R.ok();
@@ -990,6 +1038,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         if (log == null) {
             return R.error("无记录");
         }
+        if (log.getLogType() != 2) {
+            return R.error("未完课");
+        }
         if (log.getRewardType() != null) {
             FsCourseRedPacketLog packetLog = redPacketLogMapper.selectFsCourseRedPacketLogByTemporary(param.getVideoId(), param.getUserId());
             if(packetLog != null && packetLog.getStatus() == 1) {
@@ -1134,13 +1185,12 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
                 try {
                     handleFsUserWx(user,param.getAppId());
                 }catch (Exception e){
-                    logger.error("zyp \n 【更新或插入用户与小程序的绑定关系失败】:{}", user.getUserId());
+                    logger.error("【更新或插入用户与小程序的绑定关系失败】:{}", user.getUserId());
                 }
             }else {
                 packetParam.setOpenId(fsUserWx.getOpenId());
             }
             //查出公司绑定openid并赋值
-
         }
 
         //判断服务号配置是否存在
@@ -1154,104 +1204,160 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         packetParam.setAppId(param.getAppId());
         packetParam.setUser(user);
 
-        System.out.println("红包金额"+amount);
-        System.out.println("红包商户号"+packetParam);
+        logger.info("红包金额 {},红包商户号 {}",amount,packetParam);
         //2025.6.19 红包金额为0的时候
         if (amount.compareTo(BigDecimal.ZERO)>0){
 
-//            Company company = companyMapper.selectCompanyByIdForUpdate(param.getCompanyId());
-           // BigDecimal money = company.getMoney();
-         //   BigDecimal subtract = money.subtract(amount);
-//            if (subtract.compareTo(BigDecimal.ZERO)<0){
-//                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
-//                redPacketLog.setCourseId(param.getCourseId());
-//                redPacketLog.setCompanyId(param.getCompanyId());
-//                redPacketLog.setUserId(param.getUserId());
-//                redPacketLog.setVideoId(param.getVideoId());
-//                redPacketLog.setStatus(2);
-//                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-//                redPacketLog.setCompanyUserId(param.getCompanyUserId());
-//                redPacketLog.setCreateTime(new Date());
-//                redPacketLog.setAmount(amount);
-//                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-//                redPacketLog.setPeriodId(param.getPeriodId());
-//                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
-//                return R.error("余额不足请稍等");
-//            }
-            // 发送红包
-            R sendRedPacket = paymentService.sendRedPacket(packetParam);
-            if (sendRedPacket.get("code").equals(200)) {
-                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
-                TransferBillsResult transferBillsResult;
-                if (sendRedPacket.get("isNew").equals(1)){
-                    transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
-                    redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
-                    redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
-                }else {
-                    redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+            //---------------发红包前先判断润天账户余额是否足够---------
+            RLock lock = redissonClient.getLock(REDPACKET_POOL_LOCK);
+            try{
+                boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
+
+                if (!locked) {
+                    logger.error("获取锁失败");
+                    return R.error("[红包领取] 系统繁忙,请重试!");
+                }
+
+                // 发送红包
+                return sendRedPacketRewardToUser(param, log, config, packetParam, amount);
+
+            }catch (Exception e){
+                logger.error("领取红包失败原因:{}", ExceptionUtils.getFullStackTrace(e),e);
+                throw new RuntimeException(e);
+            }finally {
+                if (lock.isHeldByCurrentThread()) {
+                    lock.unlock();
                 }
-                // 添加红包记录
-                redPacketLog.setCourseId(param.getCourseId());
-//            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
-                redPacketLog.setCompanyId(param.getCompanyId());
-                redPacketLog.setUserId(param.getUserId());
-                redPacketLog.setVideoId(param.getVideoId());
-                redPacketLog.setStatus(0);
-                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-                redPacketLog.setCompanyUserId(param.getCompanyUserId());
-                redPacketLog.setCreateTime(new Date());
-                redPacketLog.setAmount(amount);
-                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-                redPacketLog.setPeriodId(param.getPeriodId());
-                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
-
-                // 更新观看记录的奖励类型
-                log.setRewardType(config.getRewardType());
-                courseWatchLogMapper.updateFsCourseWatchLog(log);
-//
-//                company.setMoney(subtract);
-//                companyMapper.updateCompany(company);
-//
-//                CompanyMoneyLogs logs=new CompanyMoneyLogs();
-//                logs.setCompanyId(company.getCompanyId());
-//                logs.setRemark("扣除红包金额");
-//                logs.setMoney(amount.multiply(new BigDecimal(-1)));
-//                logs.setLogsType(15);
-//                logs.setBalance(company.getMoney());
-//                logs.setCreateTime(new Date());
-//                moneyLogsMapper.insertCompanyMoneyLogs(logs);
-
-                return sendRedPacket;
-            } else {
-                return R.error("奖励发送失败,请联系客服");
             }
         } else {
             FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
             // 添加红包记录
             redPacketLog.setCourseId(param.getCourseId());
-//            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+
             redPacketLog.setCompanyId(param.getCompanyId());
             redPacketLog.setUserId(param.getUserId());
             redPacketLog.setVideoId(param.getVideoId());
             redPacketLog.setStatus(0);
-            redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+            redPacketLog.setQwUserId(param.getQwUserId());
             redPacketLog.setCompanyUserId(param.getCompanyUserId());
             redPacketLog.setCreateTime(new Date());
             redPacketLog.setAmount(BigDecimal.ZERO);
-            redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+            redPacketLog.setWatchLogId(log.getLogId());
             redPacketLog.setPeriodId(param.getPeriodId());
             redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
 
             // 更新观看记录的奖励类型
-//            if (param.getLinkType() == null || param.getLinkType() == 0) {
             log.setRewardType(config.getRewardType());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
-//            }
             return R.ok("红包发送成功");
         }
 
     }
 
+    private R sendRedPacketRewardToUser(FsCourseSendRewardUParam param, FsCourseWatchLog log, CourseConfig config, WxSendRedPacketParam packetParam, BigDecimal amount) {
+
+        // 判断当前用户是否限流
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+        String today = sdf.format(new Date());
+        String userLimitKey = String.format(REDPACKET_USER_LIMIT, today, param.getUserId());
+        Integer userCount =  redisTemplateInteger.opsForValue().get(userLimitKey);
+
+        // 首次领取
+        if(userCount == null) {
+            userCount = 0;
+            long expireSeconds = getExpireSeconds();
+            redisTemplateInteger.opsForValue().set(userLimitKey, userCount, expireSeconds, TimeUnit.SECONDS);
+        }
+
+        if(userCount >= RED_PACKET_LIMIT_COUNT){
+            logger.info("[红包领取] 用户{} 领取红包已经达到最大限制!",param.getUserId());
+            return R.error("[红包领取] 当前用户当前已经领取红包已经达到限制!");
+        }
+
+
+        BigDecimal companyMoney = null;
+        if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
+            companyMoney = redisTemplate.opsForValue().get(REDPACKET_COMPANY_MONEY);
+
+            if(ObjectUtils.isNull(companyMoney)){
+                SysConfig sysConfig = sysConfigService.selectConfigByConfigKey("company.money");
+                if(ObjectUtils.isNull(sysConfig)){
+                    throw new IllegalArgumentException("润天公司账户余额不能为空!请检查配置!");
+                }
+                String configValue = sysConfig.getConfigValue();
+                companyMoney = new BigDecimal(configValue);
+                logger.info("缓存公司余额为空,从数据库读取 companyMoney: {}",companyMoney);
+            }
+
+            if (companyMoney.compareTo(BigDecimal.ZERO) <= 0) {
+                logger.info("润天账户余额: {} 不足!", companyMoney);
+                return R.error("[红包领取] 账户余额不足,请联系管理员!");
+            }
+        }
+
+        // 发送红包
+        R sendRedPacket = paymentService.sendRedPacket(packetParam);
+        if (sendRedPacket.get("code").equals(200)) {
+            FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+            TransferBillsResult transferBillsResult;
+            if (sendRedPacket.get("isNew").equals(1)){
+                transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
+                redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
+                redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+            }else {
+                redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+            }
+            // 添加红包记录
+            redPacketLog.setCourseId(param.getCourseId());
+            redPacketLog.setCompanyId(param.getCompanyId());
+            redPacketLog.setUserId(param.getUserId());
+            redPacketLog.setVideoId(param.getVideoId());
+            redPacketLog.setStatus(0);
+            redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+            redPacketLog.setCompanyUserId(param.getCompanyUserId());
+            redPacketLog.setCreateTime(new Date());
+            redPacketLog.setAmount(amount);
+            redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+            redPacketLog.setPeriodId(param.getPeriodId());
+            if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
+                redPacketLog.setAccBalanceBefore(companyMoney);
+                redPacketLog.setAccBalanceAfter(companyMoney.subtract(amount));
+            }
+
+            redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+            // 更新观看记录的奖励类型
+            log.setRewardType(config.getRewardType());
+            courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+            if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
+                // 更新账户余额
+                logger.info("[更新账户余额] 当前余额{} 更新后余额{}",companyMoney.toPlainString(),companyMoney.subtract(amount).toPlainString());
+
+                companyMoney = companyMoney.subtract(amount);
+                redisTemplate.opsForValue().set(REDPACKET_COMPANY_MONEY,companyMoney);
+            }
+
+            // 用户领取红包次数+1
+            redisTemplateInteger.opsForValue().increment(userLimitKey, 1);
+
+            return sendRedPacket;
+        } else {
+            return R.error("奖励发送失败,请联系客服");
+        }
+    }
+
+    private static long getExpireSeconds() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.add(Calendar.DAY_OF_YEAR, 1);
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        // 计算从现在到明天凌晨的秒数
+        long expireSeconds = (calendar.getTimeInMillis() - System.currentTimeMillis()) / 1000;
+        return expireSeconds;
+    }
+
 
     private void handleFsUserWx(FsUser user, String appId) {
         FsUserWx fsUserWx = new FsUserWx();
@@ -1264,7 +1370,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         fsUserWx.setUpdateTime(new Date());
         fsUserWxService.saveOrUpdateByUniqueKey(fsUserWx);
 
-        logger.info("zyp \n 【更新或插入用户与小程序{}的绑定关系】:{}", appId, user.getUserId());
+        logger.info("【更新或插入用户与小程序{}的绑定关系】:{}", appId, user.getUserId());
     }
 
     /**
@@ -1319,14 +1425,14 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
                 FsUserWx fsUserWx = fsUserWxService.selectByAppIdAndUserId(param.getAppId(),user.getUserId(),1);
                 if (fsUserWx ==null){
                     if (user.getCourseMaOpenId()==null){
-                        logger.error("zyp \n 【转账openId参数错误】:{}", user.getUserId());
+                        logger.error(" 【转账openId参数错误】:{}", user.getUserId());
                         return R.error("openId参数错误,请清理缓存后重新授权!");
                     }
                     packetParam.setOpenId(user.getCourseMaOpenId());
                     try {
                         handleFsUserWx(user,param.getAppId());
                     }catch (Exception e){
-                        logger.error("zyp \n 【更新或插入用户与小程序的绑定关系失败】:{}", user.getUserId(),e);
+                        logger.error(" 【更新或插入用户与小程序的绑定关系失败】:{}", user.getUserId(),e);
                     }
 
                 }else {
@@ -1346,11 +1452,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         packetParam.setAppId(param.getAppId());
         packetParam.setUser(user);
 
-        System.out.println("红包金额"+amount);
-        System.out.println("红包商户号"+packetParam);
-//        if (ObjectUtils.isNotEmpty(config.getIsNegative())&&config.getIsNegative() == 1) {
-//            return processRedPacket(config, packetParam, param, amount, log);
-//        }
+        logger.info("红包金额 {},红包商户号 {}",amount,packetParam);
+
         //2025.6.19 红包金额为0的时候
         if (amount.compareTo(BigDecimal.ZERO)>0){
 
@@ -1359,42 +1462,113 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             if (money.compareTo(BigDecimal.ZERO)<0) {
                 return R.error("服务商余额不足,请联系群主服务器充值!");
             }
-            // 发送红包
-            R sendRedPacket = paymentService.sendRedPacket(packetParam);
-            if (sendRedPacket.get("code").equals(200)) {
-                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
-                TransferBillsResult transferBillsResult;
-                if (sendRedPacket.get("isNew").equals(1)){
-                    transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
-                    redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
-                    redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
-                }else {
-                    redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
-                    redPacketLog.setBatchId(sendRedPacket.get("batchId").toString());
+
+            //---------------发红包前先判断润天账户余额是否足够---------
+            RLock lock = redissonClient.getLock(REDPACKET_POOL_LOCK);
+            try{
+                boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
+
+                if (!locked) {
+                    logger.error("获取锁失败");
+                    return R.error("[红包领取] 系统繁忙,请重试!");
+                }
+
+                // 判断当前用户是否限流
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+                String today = sdf.format(new Date());
+                String userLimitKey = String.format(REDPACKET_USER_LIMIT, today, param.getUserId());
+                Integer userCount =  redisTemplateInteger.opsForValue().get(userLimitKey);
+
+                // 首次领取
+                if(userCount == null) {
+                    userCount = 0;
+                    long expireSeconds = getExpireSeconds();
+                    redisTemplateInteger.opsForValue().set(userLimitKey, userCount, expireSeconds, TimeUnit.SECONDS);
+                }
+
+                if(userCount >= RED_PACKET_LIMIT_COUNT){
+                    logger.info("[红包领取] 用户{} 领取红包已经达到最大限制!",param.getUserId());
+                    return R.error("[红包领取] 当前用户当前已经领取红包已经达到限制!");
+                }
+
+                BigDecimal companyMoney = null;
+                if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
+                    companyMoney = redisTemplate.opsForValue().get(REDPACKET_COMPANY_MONEY);
+                    if(ObjectUtils.isNull(companyMoney)){
+                        SysConfig sysConfig = sysConfigService.selectConfigByConfigKey("company.money");
+                        if(ObjectUtils.isNull(sysConfig)){
+                            throw new IllegalArgumentException("润天公司账户余额不能为空!请检查配置!");
+                        }
+                        String configValue = sysConfig.getConfigValue();
+                        companyMoney = new BigDecimal(configValue);
+                        logger.info("缓存公司余额为空,从数据库读取 companyMoney: {}",companyMoney);
+                    }
+
+                    if (companyMoney.compareTo(BigDecimal.ZERO) <= 0) {
+                        logger.info("润天账户余额: {} 不足!", companyMoney);
+                        return R.error("[红包领取] 账户余额不足,请联系管理员!");
+                    }
+                }
+                // 发送红包
+                R sendRedPacket = paymentService.sendRedPacket(packetParam);
+                if (sendRedPacket.get("code").equals(200)) {
+                    FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                    TransferBillsResult transferBillsResult;
+                    if (sendRedPacket.get("isNew").equals(1)){
+                        transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
+                        redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
+                        redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                    }else {
+                        redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                        redPacketLog.setBatchId(sendRedPacket.get("batchId").toString());
+                    }
+                    // 添加红包记录
+                    redPacketLog.setCourseId(param.getCourseId());
+                    redPacketLog.setCompanyId(param.getCompanyId());
+                    redPacketLog.setUserId(param.getUserId());
+                    redPacketLog.setVideoId(param.getVideoId());
+                    redPacketLog.setStatus(0);
+                    redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                    redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                    redPacketLog.setCreateTime(new Date());
+                    redPacketLog.setAmount(amount);
+                    redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                    redPacketLog.setPeriodId(param.getPeriodId());
+                    redPacketLog.setAppId(param.getAppId());
+                    if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
+                        redPacketLog.setAccBalanceBefore(companyMoney);
+                        redPacketLog.setAccBalanceAfter(companyMoney.subtract(amount));
+                    }
+                    redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+
+                    // 更新观看记录的奖励类型
+                    log.setRewardType(config.getRewardType());
+                    courseWatchLogMapper.updateFsCourseWatchLog(log);
+
+
+                    if(StringUtils.equals(ENABLE_RED_PACK_ACCOUNT,"1")) {
+                        // 更新账户余额
+                        logger.info("[更新账户余额] 当前余额{} 更新后余额{}",companyMoney.toPlainString(),companyMoney.subtract(amount).toPlainString());
+
+                        companyMoney = companyMoney.subtract(amount);
+                        redisTemplate.opsForValue().set(REDPACKET_COMPANY_MONEY,companyMoney);
+                    }
+
+                    // 用户领取红包次数+1
+                    redisTemplateInteger.opsForValue().increment(userLimitKey, 1);
+                    return sendRedPacket;
+                } else {
+                    return R.error("奖励发送失败,请联系客服");
+                }
+            }catch (Exception e){
+                logger.error("领取红包失败原因:{}", ExceptionUtils.getFullStackTrace(e),e);
+                throw new RuntimeException(e);
+            }finally {
+                if (lock.isHeldByCurrentThread()) {
+                    lock.unlock();
                 }
-                // 添加红包记录
-                redPacketLog.setCourseId(param.getCourseId());
-                redPacketLog.setCompanyId(param.getCompanyId());
-                redPacketLog.setUserId(param.getUserId());
-                redPacketLog.setVideoId(param.getVideoId());
-                redPacketLog.setStatus(0);
-                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-                redPacketLog.setCompanyUserId(param.getCompanyUserId());
-                redPacketLog.setCreateTime(new Date());
-                redPacketLog.setAmount(amount);
-                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-                redPacketLog.setPeriodId(param.getPeriodId());
-                redPacketLog.setAppId(param.getAppId());
-                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
-
-                // 更新观看记录的奖励类型
-                log.setRewardType(config.getRewardType());
-                courseWatchLogMapper.updateFsCourseWatchLog(log);
-
-                return sendRedPacket;
-            } else {
-                return R.error("奖励发送失败,请联系客服");
             }
+
         } else {
             FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
             // 添加红包记录
@@ -1694,7 +1868,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         }
 
         // 查询【用户-项目】关系
-        FsUserCompanyUser userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(fsUser.getUserId(), courseProject);
+        FsUserCompanyUser userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(fsUser.getUserId(), courseProject,companyUser.getUserId());
 
         // 添加逻辑:如果存在fs_user表数据,但是又不存在fs_user_company_user表,则表示是以前企微看课的,需要手动绑定
         if(Objects.isNull(userCompanyUser)) {
@@ -2252,6 +2426,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         }
         link.setProjectCode(cloudHostProper.getProjectCode());
 
+        link.setProjectCode(cloudHostProper.getProjectCode());
+
         String randomString = generateRandomStringWithLock();
         if (StringUtil.strIsNullOrEmpty(randomString)){
             link.setLink(UUID.randomUUID().toString().replace("-", ""));
@@ -2260,6 +2436,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         }
 
         link.setCreateTime(sendTime);
+        link.setProjectCode(cloudHostProper.getProjectCode());
 
         FsCourseRealLink courseMap = new FsCourseRealLink();
         BeanUtils.copyProperties(link,courseMap);
@@ -2697,7 +2874,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     public R getInternetTrafficIsOpen(FsUserCourseVideoFinishUParam param) {
         try {
             if (param.getBufferRate()==null){
-                logger.error("zyp \n【缓冲值空】参数: {}",param);
+                logger.error("【缓冲值空】参数: {}",param);
                 return R.error("缓冲值空");
             }
             FsCourseTrafficLog trafficLog = new FsCourseTrafficLog();
@@ -2726,7 +2903,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         } catch (Exception e) {
             e.printStackTrace();
             // 打印参数param和异常信息
-            logger.error("zyp \n【插入或更新流量失败】参数: {}, 错误信息:{}", param, e.getMessage(), e);
+            logger.error("【插入或更新流量失败】参数: {}, 错误信息:{}", param, e.getMessage(), e);
             return R.error();
         }
         return R.ok();
@@ -2742,7 +2919,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             log.setDuration(0L);
             log.setCreateTime(new Date());
             log.setLogType(3);
-            logger.info("zyp \n【群聊生成看课记录】:{}",param);
+            logger.info("【群聊生成看课记录】:{}",param);
             courseWatchLogMapper.insertFsCourseWatchLog(log);
         } catch (BeansException e) {
             return R.error("群聊生成看课记录失败!");
@@ -2751,3 +2928,4 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
     }
 
 }
+

+ 63 - 6
fs-service/src/main/java/com/fs/course/service/impl/FsUserVipOrderServiceImpl.java

@@ -236,15 +236,39 @@ public class FsUserVipOrderServiceImpl implements IFsUserVipOrderService
         String json = configService.selectConfigByKey("his.pay");
         PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
 
-        String openId = Objects.isNull(user) ? "" : user.getMaOpenId();
-        if (StringUtils.isBlank(openId)){
+//        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();
+//            }
+//        }
+        String openId = null;
+        String appId = param.getAppId();
+        if (StringUtils.isNotBlank(appId)) {
+            //查询fs_user_wx的openId
             Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
                     .eq(FsUserWx::getFsUserId, param.getUserId())
-                    .eq(FsUserWx::getAppId, payConfigDTO.getAppId());
+                    .eq(FsUserWx::getAppId, appId);
             FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
-            if (Objects.nonNull(fsUserWx)){
+            if (fsUserWx != null) {
                 openId = fsUserWx.getOpenId();
             }
+        } else {
+            appId = payConfigDTO.getAppId();
+            openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+            if (StringUtils.isBlank(openId)){
+                Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                        .eq(FsUserWx::getFsUserId, param.getUserId())
+                        .eq(FsUserWx::getAppId, appId);
+                FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+                if (Objects.nonNull(fsUserWx)){
+                    openId = fsUserWx.getOpenId();
+                }
+            }
         }
 
         if(user!=null&& StringUtils.isNotEmpty(openId)) {
@@ -343,6 +367,7 @@ public class FsUserVipOrderServiceImpl implements IFsUserVipOrderService
                         HuiFuCreateOrder o = new HuiFuCreateOrder();
                         o.setTradeType("T_MINIAPP");
                         o.setOpenid(openId);
+                        o.setAppId(appId);
                         o.setReqSeqId("appvip-" + storePayment.getPayCode());
                         o.setTransAmt(storePayment.getPayMoney().toString());
                         o.setGoodsDesc("会员开通订单支付");
@@ -350,6 +375,7 @@ public class FsUserVipOrderServiceImpl implements IFsUserVipOrderService
                         FsStorePayment mt = new FsStorePayment();
                         mt.setPaymentId(storePayment.getPaymentId());
                         mt.setTradeNo(result.getHf_seq_id());
+                        mt.setAppId(appId);
                         storePaymentService.updateFsStorePayment(mt);
                         return R.ok().put("isPay", 0).put("data", result).put("type", "hf");
                     }
@@ -371,14 +397,43 @@ public class FsUserVipOrderServiceImpl implements IFsUserVipOrderService
             return R.error("非法操作");
         }
         FsUser user = userMapper.selectFsUserByUserId(param.getUserId());
+
+        String json = configService.selectConfigByKey("his.pay");
+        PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
+
+        String openId = null;
+        String appId = param.getAppId();
+        if (StringUtils.isNotBlank(appId)) {
+            //查询fs_user_wx的openId
+            Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                    .eq(FsUserWx::getFsUserId, param.getUserId())
+                    .eq(FsUserWx::getAppId, appId);
+            FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+            if (fsUserWx != null) {
+                openId = fsUserWx.getOpenId();
+            }
+        } else {
+            appId = payConfigDTO.getAppId();
+            openId = Objects.isNull(user) ? "" : user.getMaOpenId();
+            if (StringUtils.isBlank(openId)){
+                Wrapper<FsUserWx> queryWrapper = Wrappers.<FsUserWx>lambdaQuery()
+                        .eq(FsUserWx::getFsUserId, param.getUserId())
+                        .eq(FsUserWx::getAppId, appId);
+                FsUserWx fsUserWx = fsUserWxMapper.selectOne(queryWrapper);
+                if (Objects.nonNull(fsUserWx)){
+                    openId = fsUserWx.getOpenId();
+                }
+            }
+        }
+
         if(user!=null){
             if(order.getPayMoney().compareTo(new BigDecimal(0))==1){
                 String payCode =  OrderCodeUtils.getOrderSn();
                 if(StringUtils.isEmpty(payCode)){
                     return R.error("订单生成失败,请重试");
                 }
-                String json = configService.selectConfigByKey("his.pay");
-                PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
+//                String json = configService.selectConfigByKey("his.pay");
+//                PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
                 FsStorePayment storePayment=new FsStorePayment();
                 storePayment.setStatus(0);
                 storePayment.setPayMode(payConfigDTO.getType());
@@ -421,6 +476,7 @@ public class FsUserVipOrderServiceImpl implements IFsUserVipOrderService
                         HuiFuCreateOrder o = new HuiFuCreateOrder();
                         o.setTradeType("A_NATIVE");
 //                        o.setOpenid(user.getMaOpenId());
+                        o.setAppId(appId);
                         o.setReqSeqId("appvip-"+storePayment.getPayCode());
                         o.setTransAmt(storePayment.getPayMoney().toString());
                         o.setGoodsDesc("会员开通订单支付");
@@ -428,6 +484,7 @@ public class FsUserVipOrderServiceImpl implements IFsUserVipOrderService
                         FsStorePayment mt=new FsStorePayment();
                         mt.setPaymentId(storePayment.getPaymentId());
                         mt.setTradeNo(result.getHf_seq_id());
+                        mt.setAppId(appId);
                         storePaymentService.updateFsStorePayment(mt);
                         return R.ok().put("isPay", 0).put("data", result).put("type", "hf");
                     }

+ 16 - 2
fs-service/src/main/java/com/fs/course/service/impl/FsUserWatchCourseStatisticsServiceImpl.java

@@ -168,10 +168,24 @@ public class FsUserWatchCourseStatisticsServiceImpl extends ServiceImpl<FsUserWa
                     // 获取过滤时间后的销售会员数量
                     SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
 
+//                    int userNum = userTotalDataList.stream()
+//                            .filter(v -> v.getUserCreateDate().before(data.getCourseStartDateTime())).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
+//                    int newUserNum = userTotalDataList.stream()
+//                            .filter(v -> sdf.format(v.getUserCreateDate()).equals(sdf.format(data.getCourseStartDateTime()))).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
                     int userNum = userTotalDataList.stream()
-                            .filter(v -> v.getUserCreateDate().before(data.getCourseStartDateTime())).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
+                            .filter(v -> {
+                                if (v.getUserCreateDate() == null || data.getCourseStartDateTime() == null || v.getUserNum() == null){
+                                    return false;
+                                }
+                                return v.getUserCreateDate().before(data.getCourseStartDateTime());
+                            }).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
                     int newUserNum = userTotalDataList.stream()
-                            .filter(v -> sdf.format(v.getUserCreateDate()).equals(sdf.format(data.getCourseStartDateTime()))).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
+                            .filter(v -> {
+                                if (v.getUserCreateDate() == null || data.getCourseStartDateTime() == null || v.getUserNum() == null){
+                                    return false;
+                                }
+                                return sdf.format(v.getUserCreateDate()).equals(sdf.format(data.getCourseStartDateTime()));
+                            }).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
                     vo.setUserNum(userNum);
                     vo.setNewUserNum(newUserNum);
                 } else {

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

@@ -49,4 +49,10 @@ public class FsCoursePlaySourceConfigVO {
     @ApiModelProperty("修改时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime updateTime;
+
+    /**
+     * 是否是互医/商城小程序
+     */
+    private Integer isMall;
+    private Long createDeptId;
 }

+ 4 - 0
fs-service/src/main/java/com/fs/erp/dto/sdk/wangdian/api/WdtClient.java

@@ -26,6 +26,9 @@ public class WdtClient {
 	ConfigUtil configUtil;
 
 
+	private String getWarehouseNo() {
+		return configUtil.getErpConfig().getErpWarehouseCode();
+	}
 
 	private String getAppKey() {
 		return configUtil.getSysConfig().getErpWdAppKey();
@@ -135,6 +138,7 @@ public class WdtClient {
 		log.info("开始执行请求,相对路径:{},请求参数:{}", relativeUrl, params);
 
 
+		params.put("warehouse_no", getWarehouseNo());
 		params.put("appkey", appkey);
 		params.put("sid", sid);
 		params.put("timestamp", Long.toString(System.currentTimeMillis()/1000));

+ 5 - 5
fs-service/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java

@@ -83,7 +83,7 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
 
     @Override
     public ProductUploadResultDTO uploadGoods(JSONObject dto) {
-        String url = BASE_URL + "/open/jushuitan/itemsku/upload";
+        String url = BASE_URL + "open/jushuitan/itemsku/upload";
         log.info("上传商品信息 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
 
         HttpResponse response = executeJsonPost(url, dto);
@@ -92,7 +92,7 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
 
     @Override
     public ProductResponseDTO queryGoods(ProductQueryRequestDTO dto) {
-        String url = BASE_URL + "/open/sku/query";
+        String url = BASE_URL + "open/sku/query";
         log.info("查询商品信息 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
 
         HttpResponse response = executeJsonPost(url, dto);
@@ -119,7 +119,7 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
 
     @Override
     public CommonResponse cancel(OrderCancelRequestDTO dto) {
-        String url = BASE_URL + "/open/jushuitan/orderbyoid/cancel";
+        String url = BASE_URL + "open/jushuitan/orderbyoid/cancel";
         log.info("取消订单 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
 
         HttpResponse response = executeJsonPost(url, dto);
@@ -129,7 +129,7 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
 
     @Override
     public CommonResponse<AfterSaleResponseDTO> aftersaleUpload(RefundOrderDTO dto) {
-        String url = BASE_URL + "/open/aftersale/upload";
+        String url = BASE_URL + "open/aftersale/upload";
         log.info("售后上传 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
 
         HttpResponse response = executeJsonPost(url, Collections.singletonList(dto));
@@ -139,7 +139,7 @@ public class JstErpHttpServiceImpl implements JstErpHttpService {
 
     @Override
     public CommonResponse<AssetProcessResultDTO> aftersaleConfirm(AfterSaleConfirmRequestDTO dto) {
-        String url = BASE_URL + "/open/webapi/aftersaleapi/open/confirm";
+        String url = BASE_URL + "open/webapi/aftersaleapi/open/confirm";
         log.info("售后单确认 - URL: {}, 请求体: {}", url, JSON.toJSONString(dto));
 
         HttpResponse response = executeJsonPost(url, dto);

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

@@ -267,24 +267,11 @@ public class DfOrderServiceImpl implements IErpOrderService
             String response = client.execute(RequestUrlEnum.ORDER_DELIVERY_STATUS, map, sfAccountIndex);
             DFApiResponse dfApiResponse = JSON.parseObject(response, DFApiResponse.class);
             if ("运单不存在".equals(dfApiResponse.getMsg())){
-                //取消订单
-                FsStoreOrderSalesParam afterSalesParam = new FsStoreOrderSalesParam();
-                //修改订单状态 方便后续重新发货
-                order.setStatus(FsStoreOrderStatusEnum.STATUS_2.getValue());
-                order.setExtendOrderId("");
-                fsStoreOrderMapper.updateFsStoreOrder(order);
-                afterSalesParam.setOrderId(order.getOrderId());
-                afterSalesParam.setReasons("代服管家取消订单");
-                afterSalesParam.setOperator("代服管家");
-                fsStoreOrderService.afterSales(afterSalesParam);
-                FsStoreOrderDf df = new FsStoreOrderDf();
-                df.setOrderId(order.getOrderId());
-                df.setStatus(2);
-                df.setUpdateTime(new Date());
-                fsStoreOrderDfMapper.updateFsStoreOrderDf(df);
-                fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.REFUND_ORDER_DF.getValue(),
-                        "运单不存在,"+FsStoreOrderLogEnum.REFUND_ORDER_DF.getDesc());
-                log.info("代服管家 订单取消成功: {}", response);
+
+                //查看原来物流状态
+                cancelOrder(order);
+                log.info("代服管家 getOrderDeliveryStatus: {}", response);
+                return;
             }
             //3.处理请求结果
             if (dfApiResponse != null && "ok".equals(dfApiResponse.getCode())) {
@@ -350,22 +337,7 @@ public class DfOrderServiceImpl implements IErpOrderService
                         List<FsStoreOrder> fsStoreOrders = fsStoreOrderMapper.selectFsStoreOrderListByDeliverySn(mailNumber);
                         if (fsStoreOrders != null && !fsStoreOrders.isEmpty()) {
                             fsStoreOrders.forEach(tempOrder -> {
-                                FsStoreOrderSalesParam afterSalesParam = new FsStoreOrderSalesParam();
-                                //修改订单状态 方便后续重新发货
-                                order.setStatus(FsStoreOrderStatusEnum.STATUS_2.getValue());
-                                order.setExtendOrderId("");
-                                fsStoreOrderMapper.updateFsStoreOrder(order);
-                                afterSalesParam.setOrderId(tempOrder.getOrderId());
-                                afterSalesParam.setReasons("代服管家取消订单");
-                                afterSalesParam.setOperator("代服管家");
-                                fsStoreOrderService.afterSales(afterSalesParam);
-                                FsStoreOrderDf df = new FsStoreOrderDf();
-                                df.setOrderId(order.getOrderId());
-                                df.setStatus(2);
-                                df.setUpdateTime(new Date());
-                                fsStoreOrderDfMapper.updateFsStoreOrderDf(df);
-                                fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.REFUND_ORDER_DF.getValue(),
-                                        FsStoreOrderLogEnum.REFUND_ORDER_DF.getDesc());
+                                cancelOrder(order);
                                 log.info("代服管家 订单取消成功: {}", response);
                             });
                         }
@@ -380,6 +352,35 @@ public class DfOrderServiceImpl implements IErpOrderService
         }
     }
 
+    private void cancelOrder(FsStoreOrder order) {
+        Integer deliveryStatus = order.getDeliveryStatus();
+        if (deliveryStatus == null || deliveryStatus == 0) {
+            //没有物流信息
+            //修改订单状态 方便后续重新发货
+            order.setStatus(FsStoreOrderStatusEnum.STATUS_2.getValue());
+            order.setExtendOrderId("");
+            order.setDeliverySn("");
+            fsStoreOrderMapper.updateFsStoreOrder(order);
+            fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.UPDATE_ORDER_DF.getValue(),
+                    "运单不存在,"+FsStoreOrderLogEnum.UPDATE_ORDER_DF.getDesc());
+        } else {
+            //有物流信息->售后处理
+            //取消订单
+            FsStoreOrderSalesParam afterSalesParam = new FsStoreOrderSalesParam();
+            afterSalesParam.setOrderId(order.getOrderId());
+            afterSalesParam.setReasons("代服管家取消订单");
+            afterSalesParam.setOperator("代服管家");
+            fsStoreOrderService.afterSales(afterSalesParam);
+            fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.REFUND_ORDER_DF.getValue(),
+                    "运单不存在,"+FsStoreOrderLogEnum.REFUND_ORDER_DF.getDesc());
+        }
+        FsStoreOrderDf df = new FsStoreOrderDf();
+        df.setOrderId(order.getOrderId());
+        df.setStatus(2);
+        df.setUpdateTime(new Date());
+        fsStoreOrderDfMapper.updateFsStoreOrderDf(df);
+    }
+
     /**
      * 获取erp推送参数
      *
@@ -479,6 +480,11 @@ public class DfOrderServiceImpl implements IErpOrderService
         if (StringUtils.isNotBlank(callBackUrl)) {
             vo.setCallBackUrl(callBackUrl); //订单下单后异步通知地址
         }
+        FsStoreOrderDf temp = fsStoreOrderDfMapper.selectFsStoreOrderDfByOrderId(fsStoreOrder.getOrderId());
+        if (temp != null) {
+            vo.setParcelQuantity(temp.getParcelQuantity());//包裹数量
+        }
+
         vo.setOrderNumber(order.getPlatform_code()); //订单号(不能重复)
 
         int orderPayMethod = 0;
@@ -695,7 +701,7 @@ public class DfOrderServiceImpl implements IErpOrderService
                                             order.setDeliveryName(proCode);
                                             order.setDeliveryCode("SF");
                                             order.setStatus(3);
-                                            order.setDeliverySendTime(DateUtils.getNowDate());
+                                            order.setDeliverySendTime(DateUtils.getNowDate()); //更新发货时间
                                             fsStoreOrderMapper.updateFsStoreOrder(order);
                                             fsStoreOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.DELIVERY_GOODS.getValue(), FsStoreOrderLogEnum.DELIVERY_GOODS.getDesc());
                                             if (order.getCompanyId() != null && order.getCompanyId() > 0) {

+ 29 - 80
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.fs.erp.constant.AfterSalesOrderStatusEnum;
 import com.fs.erp.constant.ErpQueryOrderStatusEnum;
 import com.fs.erp.constant.OrderStatusEnum;
 import com.fs.erp.constant.TaskStatusEnum;
@@ -28,6 +29,7 @@ import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.service.IFsStoreProductScrmService;
 import com.fs.hisStore.vo.FsStoreOrderItemVO;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.ObjectUtils;
 import org.apache.http.util.Asserts;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -52,9 +54,6 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
     @Autowired
     private IFsStoreOrderScrmService fsStoreOrderScrmService;
 
-    @Autowired
-    private FsJstAftersalePushMapper fsJstAftersalePushMapper;
-
     @Autowired
     private IFsStoreOrderItemService fsStoreOrderItemService;
     @Autowired
@@ -65,12 +64,6 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
     @Autowired
     private IFsStoreProductScrmService fsStoreProductScrmService;
 
-//    @Autowired
-//    private FsStoreDeliversMapper fsStoreDeliversMapper;
-
-    @Autowired
-    private FsStoreAfterSalesMapper fsStoreAfterSalesMapper;
-
     @Autowired
     private FsJstCodPushMapper fsJstCodPushMapper;
 
@@ -103,12 +96,7 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         shopOrderDTO.setReceiverPhone(order.getReceiver_mobile());
         // 支付金额
         shopOrderDTO.setPayAmount(erpOrderPayment.getPayment());
-        // 运费 改
-//        if(ObjectUtil.isNull(fsStoreOrder.getPayPostage())) {
-//            shopOrderDTO.setFreight(Double.valueOf("0.00"));
-//        } else {
-//            shopOrderDTO.setFreight(fsStoreOrder.getPayPostage().doubleValue());
-//        }
+
         if (ObjectUtil.isNull(fsStoreOrder.getPayDelivery())) {
             shopOrderDTO.setFreight(Double.valueOf("0.00"));
         } else {
@@ -123,27 +111,6 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
 
         // 订单商品项列表
         List<OrderItemDTO> itemDTOList = new ArrayList<>();
-    // 改
-//        List<FsStoreOrderItemVO> fsStoreOrderItemVOS = fsStoreOrderItemService.selectFsStoreOrderItemListByOrderId(fsStoreOrder.getId());
-//        for (FsStoreOrderItemVO item : fsStoreOrderItemVOS) {
-//            OrderItemDTO orderItemDTO = new OrderItemDTO();
-//            JSONObject jsonObject = JSON.parseObject(item.getJsonInfo());
-//
-//            String barCode = jsonObject.getString("barCode");
-//            String productName = jsonObject.getString("productName");
-//
-//            orderItemDTO.setSkuId(barCode);
-//            orderItemDTO.setShopSkuId(barCode);
-//            orderItemDTO.setName(productName);
-//
-//            FsStoreProduct fsStoreProduct = fsStoreProductService.selectFsStoreProductById(item.getProductId());
-//
-//            orderItemDTO.setAmount(fsStoreProduct.getPrice());
-//
-//            orderItemDTO.setQty(item.getNum().intValue());
-//            orderItemDTO.setOuterOiId(String.format("%s%s",item.getOrderCode(),item.getItemId()));
-//            itemDTOList.add(orderItemDTO);
-//        }
 
         List<FsStoreOrderItem> fsStoreOrderItemVOS = fsStoreOrderItemService.selectFsStoreOrderItemListByOrderId(fsStoreOrder.getOrderId());
         for (FsStoreOrderItem item : fsStoreOrderItemVOS) {
@@ -182,7 +149,6 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
 
 
         // 如果是货到付款
-        log.info("订单支付方式: {},\"2\".equals(fsStoreOrder.getPayType()) || \"3\".equals(fsStoreOrder.getPayType()):{}",fsStoreOrder.getPayType(),fsStoreOrder.getPayType() == 2 || fsStoreOrder.getPayType() == 3);
         if(fsStoreOrder.getPayType() == 2 || fsStoreOrder.getPayType() == 3){
             shopOrderDTO.setIsCod(true);
             // 货到付款金额 = 物流代收金额-优惠金额
@@ -208,7 +174,6 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
             fsJstCodPush.setTaskStatus(TaskStatusEnum.PENDING.getCode());
             fsJstCodPushMapper.insert(fsJstCodPush);
 
-            //shopOrderDTO.setPay(paymentDTO);
         }
 
         ErpOrderResponseDTO upload = jstErpHttpService.upload(shopOrderDTO);
@@ -256,44 +221,19 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         // 支付金额
         shopOrderDTO.setPayAmount(erpOrderPayment.getPayment());
         // 运费 改
-//        if(ObjectUtil.isNull(fsStoreOrder.getPayPostage())) {
-//            shopOrderDTO.setFreight(Double.valueOf("0.00"));
-//        } else {
-//            shopOrderDTO.setFreight(fsStoreOrder.getPayPostage().doubleValue());
-//        }
         if (ObjectUtil.isNull(fsStoreOrder.getPayDelivery())) {
             shopOrderDTO.setFreight(Double.valueOf("0.00"));
         } else {
             shopOrderDTO.setFreight(fsStoreOrder.getPayDelivery().doubleValue());
         }
         // 备注
-        shopOrderDTO.setRemark(order.getBuyer_memo());
+        //shopOrderDTO.setRemark(order.getBuyer_memo());
+        shopOrderDTO.setRemark(DateUtil.format(new Date(), "dd"));
         // 买家留言
         shopOrderDTO.setBuyerMessage(order.getBuyer_memo());
 
         // 订单商品项列表
         List<OrderItemDTO> itemDTOList = new ArrayList<>();
-        // 改
-//        List<FsStoreOrderItemVO> fsStoreOrderItemVOS = fsStoreOrderItemService.selectFsStoreOrderItemListByOrderId(fsStoreOrder.getId());
-//        for (FsStoreOrderItemVO item : fsStoreOrderItemVOS) {
-//            OrderItemDTO orderItemDTO = new OrderItemDTO();
-//            JSONObject jsonObject = JSON.parseObject(item.getJsonInfo());
-//
-//            String barCode = jsonObject.getString("barCode");
-//            String productName = jsonObject.getString("productName");
-//
-//            orderItemDTO.setSkuId(barCode);
-//            orderItemDTO.setShopSkuId(barCode);
-//            orderItemDTO.setName(productName);
-//
-//            FsStoreProduct fsStoreProduct = fsStoreProductService.selectFsStoreProductById(item.getProductId());
-//
-//            orderItemDTO.setAmount(fsStoreProduct.getPrice());
-//
-//            orderItemDTO.setQty(item.getNum().intValue());
-//            orderItemDTO.setOuterOiId(String.format("%s%s",item.getOrderCode(),item.getItemId()));
-//            itemDTOList.add(orderItemDTO);
-//        }
 
         List<FsStoreOrderItemVO> fsStoreOrderItemVOS = fsStoreOrderItemScrmService.selectFsStoreOrderItemListByOrderId(fsStoreOrder.getId());
         log.info("fsStoreOrderItemVOS==========>{}",fsStoreOrderItemVOS);
@@ -526,26 +466,35 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         return baseResponse;
     }
 
+    @Autowired
+    private FsJstAftersalePushMapper fsJstAftersalePushMapper;
+
     @Override
     public BaseResponse refundUpdateScrm(ErpRefundUpdateRequest param) {
-
         FsStoreOrderScrm fsStoreOrder = fsStoreOrderScrmService.selectFsStoreOrderByOrderCode(param.getTid());
-        //todo 待合并
-//        FsStoreDelivers byOrderCode = fsStoreDeliversMapper.findByOrderCode(fsStoreOrder.getOrderCode());
-        //todo
+        log.info("订单号: {},发货状态: {},是否发货后: {}",fsStoreOrder.getOrderCode(),fsStoreOrder.getStatus(),ObjectUtils.equals(fsStoreOrder.getStatus(),2));
+
         // 发货后退款
-//        if(ObjectUtil.isNotNull(byOrderCode)){
+        if(ObjectUtils.equals(fsStoreOrder.getStatus(),2)){
 
-//        } else {
-        // 如果是发货前退款,直接走取消订单流程
-        // 如果是发货后退款,走售后流程
-        OrderCancelRequestDTO requestDTO = new OrderCancelRequestDTO();
-        requestDTO.setOIds(Collections.singletonList(Integer.valueOf(fsStoreOrder.getExtendOrderId())));
-        requestDTO.setCancelType("用户退款");
-        requestDTO.setRemark("用户退款");
-
-        jstErpHttpService.cancel(requestDTO);
-//        }
+            FsJstAftersalePush fsJstAftersalePush = new FsJstAftersalePush();
+            fsJstAftersalePush.setOrderId(fsStoreOrder.getOrderCode());
+            fsJstAftersalePush.setTaskStatus(com.fs.hisStore.enums.TaskStatusEnum.PENDING.getCode());
+            fsJstAftersalePush.setType(String.valueOf(AfterSalesOrderStatusEnum.WAIT_SELLER_AGREE.getIndex()));
+            fsJstAftersalePush.setRetryCount(0);
+            fsJstAftersalePush.setAfterSaleId(String.valueOf(param.getStoreAfterSalesId()));
+            fsJstAftersalePushMapper.insert(fsJstAftersalePush);
+
+        } else {
+            // 如果是发货前退款,直接走取消订单流程
+            // 如果是发货后退款,走售后流程
+            OrderCancelRequestDTO requestDTO = new OrderCancelRequestDTO();
+            requestDTO.setOIds(Collections.singletonList(Integer.valueOf(fsStoreOrder.getExtendOrderId())));
+            requestDTO.setCancelType("用户退款");
+            requestDTO.setRemark("用户退款");
+
+            jstErpHttpService.cancel(requestDTO);
+        }
 
 
         BaseResponse baseResponse = new BaseResponse();

+ 23 - 7
fs-service/src/main/java/com/fs/erp/service/impl/K9OrderScrmServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.erp.domain.*;
 import com.fs.erp.dto.*;
@@ -20,6 +21,7 @@ import com.fs.hisStore.dto.FsStoreCartDTO;
 import com.fs.hisStore.mapper.FsStoreOrderScrmMapper;
 import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
 import com.fs.hisStore.service.IFsStoreOrderItemScrmService;
+import com.fs.hisStore.vo.FsStoreOrderItemVO;
 import lombok.extern.slf4j.Slf4j;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -341,13 +343,27 @@ public class K9OrderScrmServiceImpl implements IErpOrderService {
 
     private List<KingbosOrderD1Data> buildOrderDetailData(FsStoreOrderScrm order, String orderId) {
         logger.debug("【金博网络订单】开始构建订单明细,订单号: {}", order.getOrderCode());
-        FsStoreOrderItemScrm query = new FsStoreOrderItemScrm();
-        query.setOrderId(order.getId());
-        List<FsStoreOrderItemScrm> orderItems = storeOrderItemService.selectFsStoreOrderItemList(query);
+//        FsStoreOrderItemScrm query = new FsStoreOrderItemScrm();
+//        query.setOrderId(order.getId());
+//        List<FsStoreOrderItemScrm> orderItems = storeOrderItemService.selectFsStoreOrderItemList(query);
+
+//        List<KingbosOrderD1Data> d1Datas = orderItems.stream()
+//                .map(item -> buildOrderDetailItem(item, orderId))
+//                .collect(Collectors.toList());
+
+
+        List<FsStoreOrderItemVO> orderItems = storeOrderItemService.selectFsStoreOrderItemListAndProductByOrderId(order.getId());
+        //2025.09.19 中康需求只推非药品
+        List<KingbosOrderD1Data> d1Datas = orderItems.stream().filter(item->{
+                    if (CloudHostUtils.hasCloudHostName("中康")){
+                        return item.getIsDrug() == null || item.getIsDrug() == 1;
+                    } else {
+                        return true;
+                    }
+                })
+                .map(item -> buildOrderDetailItem(item, orderId))
+                .collect(Collectors.toList());
 
-        List<KingbosOrderD1Data> d1Datas = orderItems.stream()
-            .map(item -> buildOrderDetailItem(item, orderId))
-            .collect(Collectors.toList());
 
         logger.debug("【金博网络订单】订单明细构建完成,订单号: {}, 明细数量: {}", order.getOrderCode(), d1Datas.size());
         return d1Datas;
@@ -360,7 +376,7 @@ public class K9OrderScrmServiceImpl implements IErpOrderService {
         return item;
     }
 
-    private KingbosOrderD1Data buildOrderDetailItem(FsStoreOrderItemScrm orderItem, String orderId) {
+    private KingbosOrderD1Data buildOrderDetailItem(FsStoreOrderItemVO orderItem, String orderId) {
         FsStoreCartDTO cartDTO = JSONUtil.toBean(orderItem.getJsonInfo(), FsStoreCartDTO.class);
         BigDecimal quantity = new BigDecimal(orderItem.getNum());
 

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

@@ -99,8 +99,8 @@ public class WdtErpGoodsServiceImpl implements IErpGoodsService {
                 for (ErpWdtStockDTO stock : stocks) {
                     ErpGoodsStock erpGoodsStock = new ErpGoodsStock();
                     erpGoodsStock.setBarcode(barcode);
-                    erpGoodsStock.setQty(stock.getStock_num());
-                    erpGoodsStock.setSalable_qty(stock.getAvaliable_num());
+                    erpGoodsStock.setQty(stock.getStock_num().split("\\.")[0]); //999979.0000
+                    erpGoodsStock.setSalable_qty(stock.getAvaliable_num().split("\\.")[0]); //999976.0000
                     list.add(erpGoodsStock);
                 }
             } else {

+ 314 - 29
fs-service/src/main/java/com/fs/fastgptApi/util/AudioUtils.java

@@ -1,16 +1,25 @@
 package com.fs.fastgptApi.util;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.base.BaseException;
+import com.fs.config.ai.AiHostProper;
+import com.fs.fastGpt.domain.FastgptChatVoiceHomo;
+import com.fs.fastGpt.service.IFastGptChatMsgService;
 import com.fs.fastgptApi.vo.AudioVO;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.http.HttpEntity;
 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.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
 
 import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;
@@ -20,16 +29,83 @@ import java.net.HttpURLConnection;
 import java.net.URL;
 import java.nio.file.Files;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
 
+@Slf4j
+@Component
 public class AudioUtils {
+
+    private static AiHostProper staticAiHostProper;
+
+    @Autowired
+    public void setAiHostProper(AiHostProper service) {
+        AudioUtils.staticAiHostProper = service;
+    }
+
     /**
      * 工具地址
      **/
     static String path = "c:\\";
     static String destinationDir = "c:\\hook\\";
+    public static AudioVO createVoiceUrl(Long id,String userVoiceUrl){
+        String fileUrl = staticAiHostProper.getVoiceApi() + "/app/common/createVoiceUrl?id=" + id + "&userVoiceUrl=" + userVoiceUrl;
+
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            HttpGet httpGet = new HttpGet(fileUrl);
+            HttpResponse response = httpClient.execute(httpGet);
+
+            // 检查响应状态码
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                String responseBody = EntityUtils.toString(response.getEntity());
+
+                // 使用JSON工具解析响应
+                ObjectMapper mapper = new ObjectMapper();
+                JsonNode jsonNode = mapper.readTree(responseBody);
+                JsonNode dataNode = jsonNode.get("data");
+                return mapper.treeToValue(dataNode, AudioVO.class);
+
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static AudioVO createUserUrlAndUrl(List<FastgptChatVoiceHomo> homos,Long id,String voiceTxt){
+        String voiceHomo = voiceHomo(homos,voiceTxt);
+        String fileUrl = staticAiHostProper.getVoiceApi() + "/app/common/createUserUrlAndUrl?id=" + id + "&voiceTxt=" + voiceHomo;
+
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            HttpGet httpGet = new HttpGet(fileUrl);
+            HttpResponse response = httpClient.execute(httpGet);
+
+            // 检查响应状态码
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                String responseBody = EntityUtils.toString(response.getEntity());
+
+                // 使用JSON工具解析响应
+                ObjectMapper mapper = new ObjectMapper();
+                JsonNode jsonNode = mapper.readTree(responseBody);
+                JsonNode dataNode = jsonNode.get("data");
+                return mapper.treeToValue(dataNode, AudioVO.class);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private static String voiceHomo(List<FastgptChatVoiceHomo> homos,String content){
+        for (FastgptChatVoiceHomo homo : homos) {
+            if (content.contains(homo.getContent())) {
+                // 如果包含目标字段,则替换
+                content= content.replace(homo.getContent(), homo.getChangeCount());
+            } else {
+            }
+        }
+        return content;
+    }
 
     /**
      * 从网络 URL 转换 MP3/WAV到SILk格式
@@ -69,13 +145,39 @@ public class AudioUtils {
             if (tempFile == null) {
                 throw new ServiceException("下载文件失败");
             }
-
-
             Integer durations = getDurations(destinationDir+tempFile);
             String silkUrl = transferAudioSilk(destinationDir, tempFile, isSource);
             AudioVO audioVO = new AudioVO();
             audioVO.setDuration(durations);
             audioVO.setUrl(silkUrl);
+            File wavFile = new File(destinationDir+tempFile);
+            if (wavFile.exists()) {
+                wavFile.delete();
+            }
+            // 调用原来的转换方法
+            return audioVO;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static AudioVO transferCompanyIdAudioSilkFromText(String voiceText,Long companyUserId, boolean isSource) {
+        try {
+            // 下载文件到本地临时路径
+            String tempFile = downloadFileFromText(voiceText,companyUserId);
+            if (tempFile == null) {
+                throw new ServiceException("下载文件失败");
+            }
+            Integer durations = getDurations(destinationDir+tempFile);
+            AudioVO  audioVO = transferCompanyAudioSilk(destinationDir, tempFile, isSource);
+
+            audioVO.setDuration(durations);
+
+            File wavFile = new File(destinationDir+tempFile);
+            if (wavFile.exists()) {
+                wavFile.delete();
+            }
             // 调用原来的转换方法
             return audioVO;
         } catch (Exception e) {
@@ -84,6 +186,7 @@ public class AudioUtils {
         return null;
     }
 
+
     public static AudioVO transferAudioSilkFromTextNew(String Text,Long id, boolean isSource) {
         try {
             // 下载文件到本地临时路径
@@ -132,7 +235,7 @@ public class AudioUtils {
                     path + "ffmpeg.exe",
                     "-i", audioFilePath,WAVPath
             };
-            System.out.println(command[2]);
+            log.info(command[2]);
             // 启动进程执行命令
             ProcessBuilder processBuilder = new ProcessBuilder(command);
             processBuilder.redirectErrorStream(true); // 合并标准输出和错误输出
@@ -167,11 +270,11 @@ public class AudioUtils {
             while ((line = reader.readLine()) != null) {
                 // 打印或解析输出信息
                 if (line.contains("Duration")) {
-                    System.out.println(line);
+                    log.info(line);
                     // 查找并解析时长信息
                     String duration = line.substring(line.indexOf("Duration: ") +10, line.indexOf(","));
                     int i = convertDurationToSeconds(duration);
-                    System.out.println("音频时长: " + i);
+                    log.info("音频时长: " + i);
                     durations=i;
                 }
             }
@@ -198,7 +301,7 @@ public class AudioUtils {
         }
     }
     /**
-//     * MP3/WAV转SILk格式
+     //     * MP3/WAV转SILk格式
      *
      * @param path 文件路径 例:D:\\file\\
      * @param name 文件名称 例:audio.mp3/audio.wav
@@ -219,16 +322,17 @@ public class AudioUtils {
                 throw new Exception("文件不存在!");
             }
             // 文件名时拼接
-            SimpleDateFormat ttime = new SimpleDateFormat("yyyyMMddhhmmss");
-            String time = ttime.format(new Date());
+            // 使用 UUID + 临时目录隔离
+            File tempDir = Files.createTempDirectory("audioSilk_").toFile();
+            String uuid = UUID.randomUUID().toString().replace("-", "");
             // 导出的pcm格式路径
-            String pcmPath = path + "PCM_" + time + ".pcm";
+            String pcmPath = tempDir.getAbsolutePath() + File.separator + "PCM_" + uuid + ".pcm";
             // 先将mp3/wav转换成pcm格式
-            transferAudioPcm(filePath, pcmPath);
+            transferAudioPcmSecond(filePath, pcmPath);
             // 导出的silk格式路径
-            String silkPath = path + "SILK_" + time + ".silk";
+            String silkPath = tempDir.getAbsolutePath() + File.separator + "SILK_" + uuid + ".silk";
             // 转换成silk格式
-            transferPcmSilk(pcmPath, silkPath);
+            transferPcmSilkSecond(pcmPath, silkPath);
             // 删除pcm文件
             File pcmFile = new File(pcmPath);
             if (pcmFile.exists()) {
@@ -252,12 +356,86 @@ public class AudioUtils {
             if (silkFile.exists()) {
                 silkFile.delete();
             }
+            //最后删除文件夹
+            if (tempDir.exists()) {
+                tempDir.delete();
+            }
             return silkUrl;
         } catch (Exception e) {
             e.printStackTrace();
         }
         return null;
     }
+
+    public static AudioVO transferCompanyAudioSilk(String path, String name, boolean isSource) {
+        AudioVO audioVO = new AudioVO();
+        try {
+            // 判断后缀格式
+            String suffix = name.split("\\.")[1];
+            if (!suffix.toLowerCase().equals("mp3") && !suffix.toLowerCase().equals("wav")) {
+                throw new ServiceException("文件格式必须是mp3/wav");
+            }
+            String filePath = path + name;
+            File file = new File(filePath);
+            if (!file.exists()) {
+                throw new Exception("文件不存在!");
+            }
+            File wavFile = new File(filePath);
+            //上传silk文件
+            String wavPathSuffix = filePath.split("\\.")[1];
+            CloudStorageService wavStorage = OSSFactory.build();
+            byte[] wavBytes = Files.readAllBytes(wavFile.toPath());
+            String wavUrl = wavStorage.uploadSuffix(wavBytes, "."+wavPathSuffix);
+            audioVO.setWavUrl(wavUrl);
+
+            // 文件名时拼接
+            // 使用 UUID + 临时目录隔离
+            File tempDir = Files.createTempDirectory("audio_").toFile();
+            String uuid = UUID.randomUUID().toString().replace("-", "");
+            // 导出的pcm格式路径
+            String pcmPath = tempDir.getAbsolutePath() + File.separator + "PCM_" + uuid + ".pcm";
+            // 先将mp3/wav转换成pcm格式
+            transferAudioPcmSecond(filePath, pcmPath);
+            // 导出的silk格式路径
+            String silkPath = tempDir.getAbsolutePath() + File.separator + "SILK_" + uuid + ".silk";
+            // 转换成silk格式
+            transferPcmSilkSecond(pcmPath, silkPath);
+            // 删除pcm文件
+            File pcmFile = new File(pcmPath);
+            if (pcmFile.exists()) {
+                pcmFile.delete();
+            }
+            if (isSource) {
+                File audioFile = new File(filePath);
+
+                if (audioFile.exists()) {
+                    audioFile.delete();
+                }
+            }
+
+            File silkFile = new File(silkPath);
+            //上传silk文件
+            String silkPathSuffix = silkPath.split("\\.")[1];
+            CloudStorageService storage = OSSFactory.build();
+            byte[] fileBytes = Files.readAllBytes(silkFile.toPath());
+            String silkUrl = storage.uploadSuffix(fileBytes, "."+silkPathSuffix);
+
+            if (silkFile.exists()) {
+                silkFile.delete();
+            }
+            //最后删除文件夹
+            if (tempDir.exists()) {
+                tempDir.delete();
+            }
+            audioVO.setUrl(silkUrl);
+            return audioVO;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return audioVO;
+    }
+
+
     public static String transferAudioSilkNew(String path, String name, boolean isSource) {
         try {
             // 判断后缀格式
@@ -334,7 +512,7 @@ public class AudioUtils {
      * @param target
      */
     private static void transferAudioPcm(String fpath, String target) {
-        System.out.println("pcm地址"+fpath);
+        log.info("pcm地址"+fpath);
         List<String> commend = new ArrayList<String>();
         commend.add("cmd");
         commend.add("/c");
@@ -354,7 +532,7 @@ public class AudioUtils {
         try {
             ProcessBuilder builder = new ProcessBuilder();
             builder.command(commend);
-           // builder.inheritIO();
+            // builder.inheritIO();
             Process p = builder.start();
             p.waitFor();
             p.destroy();
@@ -363,6 +541,66 @@ public class AudioUtils {
         }
     }
 
+    /**
+     * mp3/wav 通用
+     * @param fpath
+     * @param target
+     */
+    private static void transferAudioPcmSecond(String fpath, String target) {
+        Process process = null;
+        try {
+            List<String> command = new ArrayList<String>();
+            command.add(path + "ffmpeg.exe");
+            command.add("-y");
+            command.add("-i");
+            command.add(fpath);
+            command.add("-f");
+            command.add("s16le");
+            command.add("-ar");
+            command.add("24000");
+            command.add("-ac");
+            command.add("1");
+            command.add(target);
+
+            ProcessBuilder builder = new ProcessBuilder();
+            builder.command(command);
+            builder.redirectErrorStream(true);
+
+            process = builder.start();
+
+            // 添加超时机制,避免进程无限挂起
+            boolean finished = process.waitFor(10, TimeUnit.SECONDS);
+            if (!finished) {
+                process.destroyForcibly();
+                throw new ServiceException("PCM转换超时");
+            }
+
+            int exitCode = process.exitValue();
+            if (exitCode != 0) {
+                throw new ServiceException("PCM转换失败,退出码:" + exitCode);
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new ServiceException("PCM转换被中断"+ e);
+        } catch (IOException e) {
+            throw new ServiceException("PCM转换IO异常:" + e.getMessage() + e);
+        } catch (Exception e) {
+            throw new ServiceException("PCM转换异常:" + e.getMessage() + e);
+        } finally {
+            if (process != null) {
+                try {
+                    process.destroy();
+                } catch (Exception e) {
+                    // 记录但不抛出异常,避免掩盖主异常
+                    System.err.println("销毁进程时出错:" + e.getMessage());
+                }
+            }
+        }
+    }
+
+
+
+
 
     /**
      * silk_v3_encoder.exe,转成Silk格式
@@ -412,6 +650,52 @@ public class AudioUtils {
         }
     }
 
+    /**
+     * 解决语音较长时转换不完全的问题
+     * @param pcmPath
+     * @param target
+     */
+    public static void transferPcmSilkSecond(String pcmPath, String target) {
+        Process process = null;
+        try {
+            // 使用 ProcessBuilder 替代 Runtime.exec 提高可靠性
+            List<String> command = new ArrayList<>();
+            command.add("cmd");
+            command.add("/c");
+            command.add("start");
+            command.add("/wait");  // 等待程序执行完毕才退出
+            command.add(path + "silk_v3_encoder.exe");
+            command.add(pcmPath);
+            command.add(target);
+            command.add("-tencent");
+
+            ProcessBuilder builder = new ProcessBuilder(command);
+            builder.redirectErrorStream(true); // 合并标准输出和错误输出
+
+            process = builder.start();
+
+            // 可选:读取输出以确认执行状态
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    log.info(line);  // 打印日志便于调试
+                }
+            }
+
+            int exitCode = process.waitFor();  // 等待执行完成
+            if (exitCode != 0) {
+                System.err.println("silk_v3_encoder exited with code: " + exitCode);
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (process != null) {
+                process.destroy();
+            }
+        }
+    }
+
     public static void transferPcmSilkNew(String pcmPath, String target) {
         Process process = null;
         try {
@@ -446,6 +730,8 @@ public class AudioUtils {
             // 创建 HTTP 连接
             URL url = new URL(fileUrl);
             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+            // 设置Referer请求头
+//            connection.setRequestProperty("Referer", "cos.his.cdwjyyh.com");
             connection.setRequestMethod("GET");
             connection.connect();
 
@@ -458,12 +744,14 @@ public class AudioUtils {
             inputStream = connection.getInputStream();
 
             // 创建临时文件,并指定存放地址
-            String tempFileName = "temp_" + System.currentTimeMillis() + "_" + getFileExtension(fileUrl);
+            String tempFileName = "temp_" + UUID.randomUUID() + "_" + getFileExtension(fileUrl);
             File destinationDirectory = new File(destinationDir);
 
-            // 确保目标目录存在
-            if (!destinationDirectory.exists()) {
-                destinationDirectory.mkdirs();
+            // 参照 transferAudioSilk 方法,同步确保目录创建的线程安全
+            synchronized (AudioUtils.class) {
+                if (!destinationDirectory.exists()) {
+                    destinationDirectory.mkdirs();
+                }
             }
 
             // 将文件保存到指定路径
@@ -501,13 +789,13 @@ public class AudioUtils {
                 .replaceAll("[a-zA-Z]", "")
                 .replaceAll("\\s", "");
         try {
-            String fileUrl = "http://118.24.209.192:9881/?text="+text+"&ref_audio=./参考音频/companyUser"+userId+".wav&ref_text=在这片神奇的森林里,小鸟轻快地唱着歌,仿佛在诉说着春天的故事。"; // 替换为要下载的文件URL
+            String fileUrl = "http://127.0.0.1:9880/?text="+text+"&ref_audio=./参考音频/companyUser"+userId+".wav&ref_text=在这片神奇的森林里,小鸟轻快地唱着歌,仿佛在诉说着春天的故事。"; // 替换为要下载的文件URL
             try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                 HttpGet httpGet = new HttpGet(fileUrl);
                 HttpResponse response = httpClient.execute(httpGet);
                 HttpEntity entity = response.getEntity();https://hf-mirror.com
                 if (entity != null) {
-                     inputStream = entity.getContent();
+                    inputStream = entity.getContent();
                     byte[] fileBytes = convertInputStreamToByteArray(inputStream);
                     // 对文件字节数组进行进一步处理
                     String filePath = destinationDir+tempFileName; // 文件将被保存到 C 盘根目录下,文件名为 output_file.wav
@@ -548,13 +836,13 @@ public class AudioUtils {
                 .replaceAll("[a-zA-Z]", "")
                 .replaceAll("\\s", "");
         try {
-            String fileUrl = "http://127.0.0.1:9881/?text="+text+"&ref_audio=./参考音频/companyUser"+userId+".wav&ref_text=在这片神奇的森林里,小鸟轻快地唱着歌,仿佛在诉说着春天的故事。"; // 替换为要下载的文件URL
+            String fileUrl = "http://127.0.0.1:9880/?text="+text+"&ref_audio=./参考音频/companyUser"+userId+".wav&ref_text=在这片神奇的森林里,小鸟轻快地唱着歌,仿佛在诉说着春天的故事。"; // 替换为要下载的文件URL
             try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                 HttpGet httpGet = new HttpGet(fileUrl);
                 HttpResponse response = httpClient.execute(httpGet);
                 HttpEntity entity = response.getEntity();https://hf-mirror.com
                 if (entity != null) {
-                     inputStream = entity.getContent();
+                    inputStream = entity.getContent();
                     byte[] fileBytes = convertInputStreamToByteArray(inputStream);
                     // 对文件字节数组进行进一步处理
                     String filePath = destinationDir+tempFileName; // 文件将被保存到 C 盘根目录下,文件名为 output_file.wav
@@ -655,7 +943,7 @@ public class AudioUtils {
             Double v = clip.getMicrosecondLength() / 1000000D;
             return v;
         } catch (Exception e) {
-            System.out.println("cuo");
+            log.info("cuo");
             return null;
         } finally {
             if(clip!=null){
@@ -674,8 +962,5 @@ public class AudioUtils {
         }
         return byteOutput.toByteArray();
     }
-    // 省略其他方法的实现
-
-
 
 }

+ 4 - 0
fs-service/src/main/java/com/fs/fastgptApi/vo/AudioVO.java

@@ -4,6 +4,10 @@ import lombok.Data;
 
 @Data
 public class AudioVO {
+    private Long id;
     private Integer duration;//秒
+    private String voiceTxt;//文本
     private String url;
+    private String wavUrl;
+    private Integer recordType;
 }

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

@@ -0,0 +1,71 @@
+package com.fs.his.domain;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 兴趣聊天记录对象 fs_interest_ai_msg
+ *
+ * @author fs
+ * @date 2025-05-09
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsInterestAiMsg extends BaseEntity {
+
+    /** $column.columnComment */
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long msgId;
+
+    /** 消息id */
+    @Excel(name = "会话id")
+    private Long sessionId;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private String userId;
+
+    /** 消息内容 */
+    @Excel(name = "消息内容")
+    private String content;
+
+    /** 发送类型 1用户发送 2ai发送 */
+    @Excel(name = "发送类型 1用户发送 2ai发送")
+    private Integer sendType;
+
+    /** 角色ID */
+    @Excel(name = "角色ID")
+    private Long roleId;
+
+    /** 角色名称 */
+    @Excel(name = "角色名称")
+    private String roleName;
+
+    /** 昵称 */
+    @Excel(name = "昵称")
+    private String nickName;
+
+    /** 头像 */
+    @Excel(name = "头像")
+    private String avatar;
+
+    /** 用户输入的令牌数量 */
+    @Excel(name = "用户输入的令牌数量")
+    private Long promptTokens;
+
+    /** 生成的回复中使用的令牌 */
+    @Excel(name = "生成的回复中使用的令牌")
+    private Long completionTokens;
+
+    /** 总令牌数量 */
+    @Excel(name = "总令牌数量")
+    private Long totalTokens;
+
+    private String msgIdStr;
+
+
+}

+ 56 - 0
fs-service/src/main/java/com/fs/his/domain/FsInterestAiRole.java

@@ -0,0 +1,56 @@
+package com.fs.his.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 兴趣ai角色对象 fs_interest_ai_role
+ *
+ * @author fs
+ * @date 2025-05-09
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsInterestAiRole extends BaseEntity{
+
+    /** ID */
+    private Long roleId;
+
+    /** 角色名称 */
+    @Excel(name = "角色名称")
+    private String roleName;
+
+    /** 标签 */
+    @Excel(name = "标签")
+    private String roleTag;
+
+    /** 模型key */
+    @Excel(name = "模型key")
+    private String appKey;
+
+    /** 客服头像 */
+    @Excel(name = "客服头像")
+    private String avatar;
+
+    /** 欢迎语 */
+    @Excel(name = "欢迎语")
+    private String welcomeMessage;
+
+    @Excel(name = "封面图片")
+    private String imageUrl;
+
+    @Excel(name = "文本描述")
+    private String textDescription;
+
+    @Excel(name = "标题")
+    private String title;
+
+    @Excel(name = "是否推荐")
+    private int isRecommend;
+
+    @Excel(name = "推荐图")
+    private String recommendImgUrl;
+
+}

+ 50 - 0
fs-service/src/main/java/com/fs/his/domain/FsInterestAiSession.java

@@ -0,0 +1,50 @@
+package com.fs.his.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 兴趣对话关系对象 fs_interest_ai_session
+ *
+ * @author fs
+ * @date 2025-05-09
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsInterestAiSession extends BaseEntity{
+
+    /** 会话ID */
+    private Long sessionId;
+
+    /** 会话标识 */
+    @Excel(name = "会话标识")
+    private String chatId;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long userId;
+
+    /** 客服ID */
+    @Excel(name = "客服ID")
+    private Long roleId;
+
+    /** 客服名称 */
+    @Excel(name = "客服名称")
+    private String roleName;
+
+    /** 状态 1会话中 2已结束 */
+    @Excel(name = "状态 1会话中 2已结束")
+    private Integer status;
+
+    /** 客户昵称 */
+    @Excel(name = "客户昵称")
+    private String nickName;
+
+    /** 头像 */
+    @Excel(name = "头像")
+    private String avatar;
+
+
+}

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

@@ -103,4 +103,5 @@ public class FsPackage extends BaseEntity
     private String description;
     /** 节气 */
     private Long solarTerm;
+    private String appIds;
 }

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

@@ -57,5 +57,9 @@ public class FsStoreOrderDf extends BaseEntity{
     /** 失败原因 */
     private String failMsg;
 
+    /** 包裹数量 */
+    @Excel(name = "包裹数量 默认1")
+    private Integer parcelQuantity;
+
 
 }

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

@@ -97,6 +97,10 @@ public class FsStorePayment extends BaseEntity
     private Integer isShare;
 
 
+    //小程序appId
+    private String appId;
+
+
     public Integer getIsShare() {
         return isShare;
     }
@@ -307,6 +311,15 @@ public class FsStorePayment extends BaseEntity
         return storeId;
     }
 
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
@@ -328,6 +341,7 @@ public class FsStorePayment extends BaseEntity
             .append("refundMoney", getRefundMoney())
             .append("refundTime", getRefundTime())
             .append("storeId", getStoreId())
+            .append("appId", getAppId())
             .toString();
     }
 }

+ 1 - 0
fs-service/src/main/java/com/fs/his/enums/FsStoreOrderLogEnum.java

@@ -15,6 +15,7 @@ public enum FsStoreOrderLogEnum {
     REFUND_ORDER_APPLY("apply_refund","用户申请退款"),
     REFUND_ORDER_PLATFORM("REFUND_ORDER_PLATFORM","平台申请退款"),
     REFUND_ORDER_DF("refund_order_df","代服取消订单,申请退款"),
+    UPDATE_ORDER_DF("update_order_df","代服取消订单,需要重新发货"),
     TAKE_ORDER_DELIVERY("user_take_delivery","用户已收货"),
     PAY_ORDER_SUCCESS("pay_success","用户付款成功"),
     PAY_REMAIN_ORDER_SUCCESS("pay_remain_success","用户付款尾款成功"),

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

@@ -69,10 +69,14 @@ public interface FsInquiryOrderMapper
     public int deleteFsInquiryOrderByOrderIds(Long[] orderIds);
 
     @Select({"<script> " +
-            "select io.*,dc.doctor_name,dc.account as doctor_account,det.dept_name,c.company_name,cu.nick_name company_user_name ,io.patient_json->>'$.patientName'as  patient_name,syu.nick_name user_name from fs_inquiry_order io  LEFT JOIN fs_doctor dc ON io.doctor_id =dc.doctor_id LEFT JOIN fs_department det ON det.dept_id =io.department_id  LEFT JOIN sys_user syu ON syu.user_id =io.audit_user_id  LEFT JOIN company c ON c.company_id= io.company_id LEFT JOIN company_user cu ON cu.user_id=io.company_user_id  "+
+            "select io.*,dc.doctor_name,dc.account as doctor_account,det.dept_name,c.company_name,cu.nick_name company_user_name ,io.patient_json->>'$.patientName'as  patient_name,syu.nick_name user_name,csc.name miniProgramName from fs_inquiry_order io  LEFT JOIN fs_doctor dc ON io.doctor_id =dc.doctor_id LEFT JOIN fs_department det ON det.dept_id =io.department_id  LEFT JOIN sys_user syu ON syu.user_id =io.audit_user_id  LEFT JOIN company c ON c.company_id= io.company_id LEFT JOIN company_user cu ON cu.user_id=io.company_user_id  "+
             "            <if test=\"maps.phone != null  and maps.phone != ''\"> LEFT JOIN fs_user fu ON fu.user_id=io.user_id</if>\n" +
+            "LEFT JOIN (SELECT sp.*,ROW_NUMBER() OVER (PARTITION BY sp.pay_code ORDER BY sp.create_time DESC) as rn\n" +
+            "        FROM fs_store_payment sp) sp_latest ON sp_latest.business_code = io.order_sn AND sp_latest.rn = 1\n" +
+            "        LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id" +
             " where 1=1 " +
             "            <if test=\"maps.orderSn != null  and maps.orderSn != ''\"> and io.order_sn = #{maps.orderSn}</if>\n" +
+            "            <if test=\"maps.coursePlaySourceConfigId != null\"> and csc.id = #{maps.coursePlaySourceConfigId}</if>\n" +
             "            <if test=\"maps.title != null  and maps.title != ''\"> and io.title = #{maps.title}</if>\n" +
             "            <if test=\"maps.userId != null \"> and io.user_id = #{maps.userId}</if>\n" +
             "            <if test=\"maps.doctorName != null \">  and dc.doctor_name like concat('%', #{maps.doctorName}, '%')</if>\n" +

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

@@ -65,7 +65,7 @@ public interface FsInquiryOrderReportMapper
      */
     public int deleteFsInquiryOrderReportByReportIds(Long[] reportIds);
     @Select({"<script> " +
-            "select ior.*,ior.patient_json->>'$.patientName' as  patient_name,u.nick_name,u.phone,d.doctor_name,su.nick_name user_name,send.nick_name send_name,fio.status inquiry_status,fio.inquiry_sub_type,fio.order_sn,c.company_name,cuu.nick_name company_user_name,fz.nick_name fz_name  FROM fs_inquiry_order_report ior  " +
+            "select ior.*,ior.patient_json->>'$.patientName' as  patient_name,u.nick_name,u.phone,d.doctor_name,su.nick_name user_name,send.nick_name send_name,fio.status inquiry_status,fio.inquiry_sub_type,fio.order_sn,c.company_name,cuu.nick_name company_user_name,fz.nick_name fz_name,csc.name miniProgramName  FROM fs_inquiry_order_report ior  " +
             " LEFT JOIN fs_user u ON u.user_id=ior.user_id " +
             " LEFT JOIN fs_doctor d ON d.doctor_id=ior.doctor_id " +
             " LEFT JOIN sys_user su ON su.user_id=ior.audit_user_id " +
@@ -76,8 +76,12 @@ public interface FsInquiryOrderReportMapper
             " LEFT JOIN company_user cuu ON cuu.user_id=fio.company_user_id " +
             " LEFT JOIN company_user fz ON fz.user_id=ior.triage_user_id " +
             " LEFT join fs_patient fp on ior.patient_id = fp.patient_id " +
+            "LEFT JOIN (SELECT sp.*,ROW_NUMBER() OVER (PARTITION BY sp.pay_code ORDER BY sp.create_time DESC) as rn\n" +
+            "        FROM fs_store_payment sp) sp_latest ON sp_latest.business_code = fio.order_sn AND sp_latest.rn = 1\n" +
+            "        LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id" +
             " WHERE fio.`status`!=1 and fio.inquiry_sub_type!=3"+
             "            <if test=\"maps.orderId != null \"> and fio.order_sn = #{maps.orderId}</if>\n" +
+            "            <if test=\"maps.coursePlaySourceConfigId != null\"> and csc.id = #{maps.coursePlaySourceConfigId}</if>\n" +
             "            <if test=\"maps.doctorName != null \"> and d.doctor_name = #{maps.doctorName}</if>\n" +
             "            <if test=\"maps.reportSn != null  and maps.reportSn != ''\"> and ior.report_sn = #{maps.reportSn}</if>\n" +
             "            <if test=\"maps.sTime != null \">  and DATE(ior.create_time) &gt;= DATE(#{maps.sTime})</if>\n" +

Vissa filer visades inte eftersom för många filer har ändrats