ソースを参照

Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product

puhui999 2 年 前
コミット
16f2a5b91a
100 ファイル変更2681 行追加682 行削除
  1. 230 0
      sql/mysql/menu.sql
  2. 152 0
      sql/mysql/point.sql
  3. 0 1
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
  4. 1 11
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerRespVO.java
  5. 20 20
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/AppBannerController.java
  6. 20 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/vo/AppBannerRespVO.java
  7. 66 4
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java
  8. 109 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java
  9. 6 15
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/activity/AppCombinationActivityDetailRespVO.java
  10. 31 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/activity/AppCombinationActivityRespVO.java
  11. 21 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordDetailRespVO.java
  12. 45 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordRespVO.java
  13. 18 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordSummaryRespVO.java
  14. 0 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/vo/AppSeckillActivitiDetailRespVO.java
  15. 21 14
      yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
  16. 12 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java
  17. 26 4
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java
  18. 6 3
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/delivery/DeliveryExpressTemplateChargeMapper.java
  19. 5 4
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/delivery/DeliveryExpressTemplateFreeMapper.java
  20. 32 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/ExpressClientConfig.java
  21. 8 10
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/TradeExpressProperties.java
  22. 0 24
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/ExpressQueryClient.java
  23. 0 22
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/ExpressQueryProvider.java
  24. 0 33
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/ExpressQueryProviderEnum.java
  25. 0 19
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/ExpressQueryProviderFactory.java
  26. 23 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/ExpressClient.java
  27. 24 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/ExpressClientFactory.java
  28. 27 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/convert/ExpressQueryConvert.java
  29. 4 5
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/ExpressTrackQueryReqDTO.java
  30. 2 2
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/ExpressTrackRespDTO.java
  31. 2 3
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryReqDTO.java
  32. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java
  33. 2 3
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryReqDTO.java
  34. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java
  35. 54 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/ExpressClientFactoryImpl.java
  36. 24 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/NoProvideExpressClient.java
  37. 26 39
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kd100/Kd100ExpressClient.java
  38. 32 39
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java
  39. 0 27
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/convert/ExpressQueryConvert.java
  40. 28 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/enums/ExpressClientEnum.java
  41. 0 65
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/impl/ExpressQueryClientImpl.java
  42. 0 48
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/impl/ExpressQueryProviderFactoryImpl.java
  43. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressServiceImpl.java
  44. 5 6
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java
  45. 18 35
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java
  46. 0 29
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/DeliveryExpressTemplateChargeBO.java
  47. 0 26
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/DeliveryExpressTemplateFreeBO.java
  48. 80 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/DeliveryExpressTemplateRespBO.java
  49. 0 33
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/SpuDeliveryExpressTemplateRespBO.java
  50. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
  51. 5 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
  52. 25 28
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
  53. 2 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
  54. 18 15
      yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/Kd100ExpressClientTest.java
  55. 18 14
      yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/KdNiaoExpressClientTest.java
  56. 53 0
      yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/NoProvideExpressClientTest.java
  57. 46 55
      yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculatorTest.java
  58. 0 18
      yudao-module-mall/yudao-module-trade-biz/src/test/resources/application-trade-delivery-query.yaml
  59. 8 0
      yudao-module-mall/yudao-module-trade-biz/src/test/resources/application-unit-test.yaml
  60. 45 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/MemberPointConfigController.java
  61. 27 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/config/MemberPointConfigBaseVO.java
  62. 13 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/config/MemberPointConfigRespVO.java
  63. 13 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/config/MemberPointConfigSaveReqVO.java
  64. 1 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/recrod/package-info.java
  65. 20 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointConfigConvert.java
  66. 50 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/point/MemberPointConfigDO.java
  67. 14 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/point/MemberPointConfigMapper.java
  68. 29 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointConfigService.java
  69. 33 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointConfigServiceImpl.java
  70. 19 0
      yudao-module-point/pom.xml
  71. 28 0
      yudao-module-point/yudao-module-point-api/pom.xml
  72. 27 0
      yudao-module-point/yudao-module-point-api/src/main/java/cn/iocoder/yudao/module/point/enums/ErrorCodeConstants.java
  73. 57 0
      yudao-module-point/yudao-module-point-biz/pom.xml
  74. 102 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/PointRecordController.java
  75. 67 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordBaseVO.java
  76. 14 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordCreateReqVO.java
  77. 65 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordExcelVO.java
  78. 27 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordExportReqVO.java
  79. 29 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordPageReqVO.java
  80. 19 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordRespVO.java
  81. 18 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordUpdateReqVO.java
  82. 102 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/SignInConfigController.java
  83. 23 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigBaseVO.java
  84. 14 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigCreateReqVO.java
  85. 28 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigExcelVO.java
  86. 15 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigExportReqVO.java
  87. 17 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigPageReqVO.java
  88. 19 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigRespVO.java
  89. 18 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigUpdateReqVO.java
  90. 102 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/SignInRecordController.java
  91. 26 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordBaseVO.java
  92. 14 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordCreateReqVO.java
  93. 34 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordExcelVO.java
  94. 26 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordExportReqVO.java
  95. 28 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordPageReqVO.java
  96. 19 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordRespVO.java
  97. 18 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordUpdateReqVO.java
  98. 34 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/convert/pointrecord/PointRecordConvert.java
  99. 34 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/convert/signinconfig/SignInConfigConvert.java
  100. 34 0
      yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/convert/signinrecord/SignInRecordConvert.java

+ 230 - 0
sql/mysql/menu.sql

@@ -0,0 +1,230 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status, component_name
+)
+VALUES (
+    '积分设置管理', '', 2, 0, 2162,
+    'config', '', 'point/config/index', 0, 'PointConfig'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分设置查询', 'point:config:query', 3, 1, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分设置创建', 'point:config:create', 3, 2, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分设置更新', 'point:config:update', 3, 3, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分设置删除', 'point:config:delete', 3, 4, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分设置导出', 'point:config:export', 3, 5, @parentId,
+    '', '', '', 0
+);
+
+
+
+-- 菜单 SQL
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status, component_name
+)
+VALUES (
+    '积分签到规则管理', '', 2, 0, 2162,
+    'sign-in-config', '', 'point/signInConfig/index', 0, 'SignInConfig'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分签到规则查询', 'point:sign-in-config:query', 3, 1, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分签到规则创建', 'point:sign-in-config:create', 3, 2, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分签到规则更新', 'point:sign-in-config:update', 3, 3, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分签到规则删除', 'point:sign-in-config:delete', 3, 4, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '积分签到规则导出', 'point:sign-in-config:export', 3, 5, @parentId,
+    '', '', '', 0
+);
+
+
+-- 菜单 SQL
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status, component_name
+)
+VALUES (
+    '用户积分记录管理', '', 2, 0, 2162,
+    'record', '', 'point/record/index', 0, 'PointRecord'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户积分记录查询', 'point:record:query', 3, 1, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户积分记录创建', 'point:record:create', 3, 2, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户积分记录更新', 'point:record:update', 3, 3, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户积分记录删除', 'point:record:delete', 3, 4, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户积分记录导出', 'point:record:export', 3, 5, @parentId,
+    '', '', '', 0
+);
+
+
+
+-- 菜单 SQL
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status, component_name
+)
+VALUES (
+    '用户签到积分管理', '', 2, 0, 2162,
+    'sign-in-record', '', 'point/signInRecord/index', 0, 'SignInRecord'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户签到积分查询', 'point:sign-in-record:query', 3, 1, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户签到积分创建', 'point:sign-in-record:create', 3, 2, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户签到积分更新', 'point:sign-in-record:update', 3, 3, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户签到积分删除', 'point:sign-in-record:delete', 3, 4, @parentId,
+    '', '', '', 0
+);
+INSERT INTO system_menu(
+    name, permission, type, sort, parent_id,
+    path, icon, component, status
+)
+VALUES (
+    '用户签到积分导出', 'point:sign-in-record:export', 3, 5, @parentId,
+    '', '', '', 0
+);
+
+

+ 152 - 0
sql/mysql/point.sql

@@ -0,0 +1,152 @@
+/*
+ Navicat Premium Data Transfer
+
+ Source Server         : docer-master-root(3308)
+ Source Server Type    : MySQL
+ Source Server Version : 80030
+ Source Host           : 10.211.55.5:3308
+ Source Schema         : mall
+
+ Target Server Type    : MySQL
+ Target Server Version : 80030
+ File Encoding         : 65001
+
+ Date: 10/06/2023 20:13:57
+*/
+
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- ----------------------------
+-- Table structure for member_point_config
+-- ----------------------------
+DROP TABLE IF EXISTS `member_point_config`;
+CREATE TABLE `member_point_config` (
+  `id` int NOT NULL AUTO_INCREMENT COMMENT '自增主键',
+  `trade_deduct_enable` int DEFAULT NULL COMMENT '1 开启积分抵扣\n0 关闭积分抵扣',
+  `trade_deduct_unit_price` decimal(10,2) DEFAULT NULL COMMENT '积分抵扣,抵扣最低为分 以0.01表示 1积分抵扣0.01元(单位:元)',
+  `trade_deduct_max_price` bigint DEFAULT NULL COMMENT '积分抵扣最大值',
+  `trade_give_point` bigint DEFAULT NULL COMMENT '1元赠送多少分',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '变更时间',
+  `tenant_id` varchar(255) DEFAULT NULL COMMENT '租户id',
+  `deleted` int DEFAULT '0' COMMENT '是否被删除 0 未删除 1已删除',
+  `creator` varchar(255) DEFAULT NULL COMMENT '创建人',
+  `updater` varchar(255) DEFAULT NULL COMMENT '更新人',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='积分设置';
+
+-- ----------------------------
+-- Records of member_point_config
+-- ----------------------------
+BEGIN;
+INSERT INTO `member_point_config` (`id`, `trade_deduct_enable`, `trade_deduct_unit_price`, `trade_deduct_max_price`, `trade_give_point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (1, 1, 0.01, 10000, 1, '2023-06-10 10:57:22', '2023-06-10 03:06:58', '1', 1, '1', '1');
+INSERT INTO `member_point_config` (`id`, `trade_deduct_enable`, `trade_deduct_unit_price`, `trade_deduct_max_price`, `trade_give_point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (2, 1, 0.01, 10000, 10, '2023-06-10 11:07:12', '2023-06-10 11:07:12', '1', 0, '1', '1');
+INSERT INTO `member_point_config` (`id`, `trade_deduct_enable`, `trade_deduct_unit_price`, `trade_deduct_max_price`, `trade_give_point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (3, 1, 12.00, 12, 12, '2023-06-10 16:09:26', '2023-06-10 08:10:53', '1', 1, '1', '1');
+COMMIT;
+
+-- ----------------------------
+-- Table structure for member_point_record
+-- ----------------------------
+DROP TABLE IF EXISTS `member_point_record`;
+CREATE TABLE `member_point_record` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增主键',
+  `biz_id` varchar(255) DEFAULT NULL COMMENT '业务编码',
+  `biz_type` varchar(255) DEFAULT NULL COMMENT '业务类型',
+  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '1增加 0扣减',
+  `title` varchar(255) DEFAULT NULL COMMENT '积分标题',
+  `description` varchar(5000) DEFAULT NULL COMMENT '积分描述',
+  `point` int DEFAULT NULL COMMENT '积分',
+  `total_point` int NOT NULL COMMENT '变动后的积分',
+  `status` int DEFAULT NULL COMMENT '状态:1-订单创建,2-冻结期,3-完成,4-失效(订单退款)\n',
+  `user_id` int DEFAULT NULL COMMENT '用户id',
+  `freezing_time` datetime DEFAULT NULL COMMENT '冻结时间',
+  `thawing_time` datetime DEFAULT NULL COMMENT '解冻时间',
+  `create_time` datetime DEFAULT NULL COMMENT '发生时间',
+  `tenant_id` varchar(255) DEFAULT NULL COMMENT '租户',
+  `deleted` int DEFAULT '0' COMMENT '是否删除',
+  `creator` varchar(255) DEFAULT NULL COMMENT '创建用户',
+  `updater` varchar(255) DEFAULT NULL COMMENT '更新用户',
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  KEY `index_userId` (`user_id`),
+  KEY `index_title` (`title`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户积分记录';
+
+-- ----------------------------
+-- Records of member_point_record
+-- ----------------------------
+BEGIN;
+INSERT INTO `member_point_record` (`id`, `biz_id`, `biz_type`, `type`, `title`, `description`, `point`, `total_point`, `status`, `user_id`, `freezing_time`, `thawing_time`, `create_time`, `tenant_id`, `deleted`, `creator`, `updater`, `update_time`) VALUES (1, '1', '1', '1', '12', NULL, 212, 12, 1, 12, '2023-06-13 00:00:00', '2023-06-20 00:00:00', '2023-06-10 12:38:48', '1', 1, '1', '1', '2023-06-10 04:42:24');
+INSERT INTO `member_point_record` (`id`, `biz_id`, `biz_type`, `type`, `title`, `description`, `point`, `total_point`, `status`, `user_id`, `freezing_time`, `thawing_time`, `create_time`, `tenant_id`, `deleted`, `creator`, `updater`, `update_time`) VALUES (2, '12', '1', '0', NULL, NULL, 1212, 12, 2, 12, '2023-06-28 00:00:00', NULL, '2023-06-10 12:42:48', '1', 0, '1', '1', '2023-06-10 12:43:04');
+INSERT INTO `member_point_record` (`id`, `biz_id`, `biz_type`, `type`, `title`, `description`, `point`, `total_point`, `status`, `user_id`, `freezing_time`, `thawing_time`, `create_time`, `tenant_id`, `deleted`, `creator`, `updater`, `update_time`) VALUES (3, '12', '1', '1', '12', NULL, 12, 12, 1, 12, '2023-06-27 00:00:00', '2023-06-23 00:00:00', '2023-06-10 20:06:48', '1', 0, '1', '1', '2023-06-10 20:06:48');
+COMMIT;
+
+-- ----------------------------
+-- Table structure for member_sign_in_config
+-- ----------------------------
+DROP TABLE IF EXISTS `member_sign_in_config`;
+CREATE TABLE `member_sign_in_config` (
+  `id` int NOT NULL AUTO_INCREMENT COMMENT '规则自增主键',
+  `day` int DEFAULT NULL COMMENT '签到第x天',
+  `point` int DEFAULT NULL COMMENT '签到天数对应分数',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '变更时间',
+  `tenant_id` varchar(255) DEFAULT NULL COMMENT '租户id',
+  `deleted` int DEFAULT '0' COMMENT '是否删除',
+  `creator` varchar(255) DEFAULT NULL COMMENT '创建人',
+  `updater` varchar(255) DEFAULT NULL COMMENT '变更人',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='积分签到规则';
+
+-- ----------------------------
+-- Records of member_sign_in_config
+-- ----------------------------
+BEGIN;
+INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (1, 1, 10, '2023-06-10 11:34:43', '2023-06-10 11:34:43', '1', 0, '1', '1');
+INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (2, 2, 20, '2023-06-10 11:34:59', '2023-06-10 03:55:35', '1', 1, '1', '1');
+INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (3, 7, 1001, '2023-06-10 17:47:45', '2023-06-10 19:54:37', '1', 0, '1', '1');
+INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (4, 6, 12121, '2023-06-10 17:47:55', '2023-06-10 19:48:37', '1', 0, '1', '1');
+INSERT INTO `member_sign_in_config` (`id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (5, 2, 12, '2023-06-10 19:54:52', '2023-06-10 19:54:52', '1', 0, '1', '1');
+COMMIT;
+
+-- ----------------------------
+-- Table structure for member_sign_in_record
+-- ----------------------------
+DROP TABLE IF EXISTS `member_sign_in_record`;
+CREATE TABLE `member_sign_in_record` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '签到自增id',
+  `user_id` int DEFAULT NULL COMMENT '签到用户',
+  `day` int DEFAULT NULL COMMENT '第几天签到',
+  `point` int DEFAULT NULL COMMENT '签到的分数',
+  `create_time` datetime DEFAULT NULL COMMENT '签到时间',
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '变更时间',
+  `tenant_id` varchar(255) DEFAULT NULL COMMENT '租户id',
+  `deleted` int DEFAULT '0' COMMENT '是否删除',
+  `creator` varchar(255) DEFAULT NULL COMMENT '创建人',
+  `updater` varchar(255) DEFAULT NULL COMMENT '更新人',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户签到积分';
+
+-- ----------------------------
+-- Records of member_sign_in_record
+-- ----------------------------
+BEGIN;
+INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (1, 121, 1, 123, '2023-06-10 12:58:18', '2023-06-10 04:59:00', '1', 1, '1', '1');
+INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (2, 12, 12, 12, '2023-06-10 19:56:39', '2023-06-10 11:56:45', '1', 1, '1', '1');
+INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (3, 12, 12, 1212, '2023-06-10 20:01:17', '2023-06-10 12:01:23', '1', 1, '1', '1');
+INSERT INTO `member_sign_in_record` (`id`, `user_id`, `day`, `point`, `create_time`, `update_time`, `tenant_id`, `deleted`, `creator`, `updater`) VALUES (4, 12, 12, 1212, '2023-06-10 20:01:27', '2023-06-10 20:01:27', '1', 0, '1', '1');
+COMMIT;
+
+SET FOREIGN_KEY_CHECKS = 1;
+
+
+INSERT INTO `mall`.`system_dict_type` ( `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES ( '积分业务类型', 'point_biz_type', 0, '', '1', '2023-06-10 12:15:00', '1', '2023-06-10 04:25:07', b'0', '1970-01-01 00:00:00');
+INSERT INTO `mall`.`system_dict_type` ( `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES ( '积分订单状态', 'point_status', 0, '', '1', '2023-06-10 12:16:27', '1', '2023-06-10 12:16:27', b'0', '1970-01-01 00:00:00');
+
+INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 1, '购物', '1', 'point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:27', '1', '2023-06-10 04:25:15', b'0');
+INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 2, '签到', '2', 'point_biz_type', 0, '', '', '', '1', '2023-06-10 12:15:48', '1', '2023-06-10 04:25:18', b'0');
+INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 1, '订单创建', '1', 'point_status', 0, '', '', '', '1', '2023-06-10 12:16:42', '1', '2023-06-10 12:16:42', b'0');
+INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 2, '冻结期', '2', 'point_status', 0, '', '', '', '1', '2023-06-10 12:16:58', '1', '2023-06-10 12:16:58', b'0');
+INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 3, '完成', '3', 'point_status', 0, '', '', '', '1', '2023-06-10 12:17:07', '1', '2023-06-10 12:17:07', b'0');
+INSERT INTO `mall`.`system_dict_data` ( `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ( 4, '失效(订单退款)', '4', 'point_status', 0, '', '', '', '1', '2023-06-10 12:17:21', '1', '2023-06-10 12:17:21', b'0');

+ 0 - 1
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java

@@ -55,7 +55,6 @@ public class DateUtils {
         return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
     }
 
-    @Deprecated
     public static Date addTime(Duration duration) {
         return new Date(System.currentTimeMillis() + duration.toMillis());
     }

+ 1 - 11
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerRespVO.java

@@ -4,22 +4,12 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.ToString;
 
-import javax.validation.constraints.NotNull;
-import java.time.LocalDateTime;
-
-/**
- * @author xia
- */
 @Schema(description = "管理后台 - Banner Response VO")
 @Data
 @ToString(callSuper = true)
 public class BannerRespVO  extends BannerBaseVO {
 
-    @Schema(description = "banner编号", requiredMode = Schema.RequiredMode.REQUIRED)
-    @NotNull(message = "banner编号不能为空")
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
     private Long id;
 
-    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
-    private LocalDateTime createTime;
-
 }

+ 20 - 20
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/AppBannerController.java

@@ -1,42 +1,42 @@
 package cn.iocoder.yudao.module.promotion.controller.app.banner;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerRespVO;
-import cn.iocoder.yudao.module.promotion.convert.banner.BannerConvert;
-import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;
-import cn.iocoder.yudao.module.promotion.service.banner.BannerService;
+import cn.iocoder.yudao.module.promotion.controller.app.banner.vo.AppBannerRespVO;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
-import javax.annotation.Resource;
+import java.util.ArrayList;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
-/**
- * @author: XIA
- */
 @RestController
-@RequestMapping("/market/banner")
-@Tag(name = "用户APP- 首页Banner")
+@RequestMapping("/promotion/banner")
+@Tag(name = "用户 APP - 首页 Banner")
 @Validated
 public class AppBannerController {
 
-    @Resource
-    private BannerService bannerService;
-
-    // TODO @xia:新建一个 AppBannerRespVO,只返回必要的字段。status 要过滤下。然后 sort 下结果
     @GetMapping("/list")
-    @Operation(summary = "获得banner列表")
-    @PreAuthorize("@ss.hasPermission('market:banner:query')")
-    public CommonResult<List<BannerRespVO>> getBannerList() {
-        List<BannerDO> list = bannerService.getBannerList();
-        return success(BannerConvert.INSTANCE.convertList(list));
+    @Operation(summary = "获得 banner 列表")
+    // todo @芋艿:swagger 注解,待补全
+    // TODO @芋艿:可以增加缓存,提升性能
+    // TODO @芋艿:position = 1 时,首页;position = 10 时,拼团活动页
+    public CommonResult<List<AppBannerRespVO>> getBannerList(@RequestParam("position") Integer position) {
+        List<AppBannerRespVO> bannerList = new ArrayList<>();
+        AppBannerRespVO banner1 = new AppBannerRespVO();
+        banner1.setUrl("https://www.example.com/link1");
+        banner1.setPicUrl("https://api.java.crmeb.net/crmebimage/public/content/2022/08/04/0f78716213f64bfa83f191d51a832cbf73f6axavoy.jpg");
+        bannerList.add(banner1);
+        AppBannerRespVO banner2 = new AppBannerRespVO();
+        banner2.setUrl("https://www.example.com/link2");
+        banner2.setPicUrl("https://api.java.crmeb.net/crmebimage/public/content/2023/01/11/be09e755268b43ee90b0db3a3e1b7132r7a6t2wvsm.jpg");
+        bannerList.add(banner2);
+        return success(bannerList);
     }
 
 }

+ 20 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/vo/AppBannerRespVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.promotion.controller.app.banner.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "用户 App - Banner Response VO")
+@Data
+public class AppBannerRespVO {
+
+    @Schema(description = "跳转链接", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "跳转链接不能为空")
+    private String url;
+
+    @Schema(description = "图片地址", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "图片地址不能为空")
+    private String picUrl;
+
+}

+ 66 - 4
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationController.java → yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java

@@ -1,7 +1,10 @@
 package cn.iocoder.yudao.module.promotion.controller.app.combination;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO;
+import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityRespVO;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -21,20 +24,79 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 @RestController
 @RequestMapping("/promotion/combination-activity")
 @Validated
-public class AppCombinationController {
+public class AppCombinationActivityController {
+
+    @GetMapping("/list")
+    @Operation(summary = "获得拼团活动列表", description = "用于小程序首页")
+    // TODO 芋艿:增加 Spring Cache
+    // TODO 芋艿:缺少 swagger 注解
+    public CommonResult<List<AppCombinationActivityRespVO>> getCombinationActivityList(
+            @RequestParam(name = "count", defaultValue = "6") Integer count) {
+        List<AppCombinationActivityRespVO> activityList = new ArrayList<>();
+        AppCombinationActivityRespVO activity1 = new AppCombinationActivityRespVO();
+        activity1.setId(1L);
+        activity1.setName("618 大拼团");
+        activity1.setUserSize(3);
+        activity1.setSpuId(2048L);
+        activity1.setPicUrl("商品图片地址");
+        activity1.setMarketPrice(50);
+        activity1.setCombinationPrice(100);
+        activityList.add(activity1);
+
+        AppCombinationActivityRespVO activity2 = new AppCombinationActivityRespVO();
+        activity2.setId(2L);
+        activity2.setName("双十一拼团");
+        activity2.setUserSize(5);
+        activity2.setSpuId(4096L);
+        activity2.setPicUrl("商品图片地址");
+        activity2.setMarketPrice(100);
+        activity2.setCombinationPrice(200);
+        activityList.add(activity2);
+
+        return success(activityList);
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得拼团活动分页")
+    public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) {
+        List<AppCombinationActivityRespVO> activityList = new ArrayList<>();
+        AppCombinationActivityRespVO activity1 = new AppCombinationActivityRespVO();
+        activity1.setId(1L);
+        activity1.setName("618 大拼团");
+        activity1.setUserSize(3);
+        activity1.setSpuId(2048L);
+        activity1.setPicUrl("商品图片地址");
+        activity1.setMarketPrice(50);
+        activity1.setCombinationPrice(100);
+        activityList.add(activity1);
+
+        AppCombinationActivityRespVO activity2 = new AppCombinationActivityRespVO();
+        activity2.setId(2L);
+        activity2.setName("双十一拼团");
+        activity2.setUserSize(5);
+        activity2.setSpuId(4096L);
+        activity2.setPicUrl("商品图片地址");
+        activity2.setMarketPrice(100);
+        activity2.setCombinationPrice(200);
+        activityList.add(activity2);
+
+        return success(new PageResult<>(activityList, 2L));
+    }
 
     @GetMapping("/get-detail")
     @Operation(summary = "获得拼团活动明细")
     @Parameter(name = "id", description = "活动编号", required = true, example = "1024")
-    public CommonResult<AppCombinationActivityDetailRespVO> getCombinationActivity(@RequestParam("id") Long id) {
+    public CommonResult<AppCombinationActivityDetailRespVO> getCombinationActivityDetail(@RequestParam("id") Long id) {
         // TODO 芋艿:如果禁用的时候,需要抛出异常;
         AppCombinationActivityDetailRespVO obj = new AppCombinationActivityDetailRespVO();
         // 设置其属性的值
         obj.setId(id);
         obj.setName("晚九点限时秒杀");
         obj.setStatus(1);
-        obj.setStartTime(LocalDateTime.of(2023, 6, 11, 0, 0, 0));
-        obj.setEndTime(LocalDateTime.of(2023, 6, 11, 23, 59, 0));
+        obj.setStartTime(LocalDateTime.of(2023, 6, 15, 0, 0, 0));
+        obj.setEndTime(LocalDateTime.of(2023, 6, 15, 23, 59, 0));
+        obj.setUserSize(2);
+        obj.setSuccessCount(100);
         obj.setSpuId(633L);
         // 创建一个Product对象的列表
         List<AppCombinationActivityDetailRespVO.Product> productList = new ArrayList<>();

+ 109 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java

@@ -0,0 +1,109 @@
+package cn.iocoder.yudao.module.promotion.controller.app.combination;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.date.DateUtils;
+import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO;
+import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO;
+import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordSummaryRespVO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.constraints.Max;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "用户 APP - 拼团活动")
+@RestController
+@RequestMapping("/promotion/combination-record")
+@Validated
+public class AppCombinationRecordController {
+
+    @GetMapping("/get-summary")
+    @Operation(summary = "获得拼团活动的概要信息", description = "用于小程序首页")
+    // TODO 芋艿:增加 @Cache 缓存,1 分钟过期
+    public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationSummary() {
+        AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO();
+        summary.setUserCount(1024);
+        summary.setAvatars(new ArrayList<>());
+        summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLjFK35Wvia9lJKHoXfQuHhk0qZbvpPNxrAiaEKF7aL2k4I8kuqrdTWwliamdPHeyAA7DjAg725X2GIQ/132");
+        summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTK1pXgdj5DvBMwrbe8v3tFibSWeQATEsAibt3fllD8XwJ460P2r6KS3WCQvDefuv1bVpDhNCle6CTCA/132");
+        summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTL7KRGHBE62N0awFyBesmmxiaCicf1fJ7E7UCh6zA8GWlT1QC1zT01gG4OxI7BWDESkdPZ5o7tno4hA/132");
+        summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/ouwtwJycbic2JrCoZjETict0klxd1uRuicRneKk00ewMcCClxVcVHQT91Sh9MJGtwibf1fOicD1WpwSP4icJM6eQq1AA/132");
+        summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/RpUrhwens58qc99OcGs993xL4M5QPOe05ekqF9Eia440kRicAlicicIdQWicHBmy2bzLgHzHguWEzHHxnIgeictL7bLA/132");
+        summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/S4tfqmxc8GZGsKc1K4mnhpvtG16gtMrLnTQfDibhr7jJich9LRI5RQKZDoqEjZM3azMib5nic7F4ZXKMEgYyLO08KA/132");
+        summary.getAvatars().add("https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKXMYJOomfp7cebz3cIeb8sHk3GGSIJtWEgREe3j7J1WoAbTvIOicpcNdFkWAziatBSMod8b5RyS4CQ/132");
+        return success(summary);
+    }
+
+    @GetMapping("/get-head-list")
+    @Operation(summary = "获得最近 n 条拼团记录(团长发起的)")
+    // TODO @芋艿:注解要补全
+    public CommonResult<List<AppCombinationRecordRespVO>> getHeadCombinationRecordList(
+            @RequestParam(value = "activityId", required = false) Long activityId,
+            @RequestParam("status") Integer status,
+            @RequestParam(value = "count", defaultValue = "20") @Max(20) Integer count) {
+        List<AppCombinationRecordRespVO> list = new ArrayList<>();
+        for (int i = 1; i <= count; i++) {
+            AppCombinationRecordRespVO record = new AppCombinationRecordRespVO();
+            record.setId((long) i);
+            record.setNickname("用户" + i);
+            record.setAvatar("头像" + i);
+            record.setExpireTime(new Date());
+            record.setUserSize(10);
+            record.setUserCount(i);
+            record.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+            record.setActivityId(1L);
+            record.setSpuName("活动:" + i);
+            list.add(record);
+        }
+        return success(list);
+    }
+
+    @GetMapping("/get-detail")
+    @Operation(summary = "获得拼团记录明细")
+    @Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024")
+    public CommonResult<AppCombinationRecordDetailRespVO> getCombinationRecordDetail(@RequestParam("id") Long id) {
+        AppCombinationRecordDetailRespVO detail = new AppCombinationRecordDetailRespVO();
+        // 团长
+        AppCombinationRecordRespVO headRecord = new AppCombinationRecordRespVO();
+        headRecord.setId(1L);
+        headRecord.setNickname("用户" + 1);
+        headRecord.setAvatar("头像" + 1);
+        headRecord.setExpireTime(DateUtils.addTime(Duration.ofDays(1)));
+        headRecord.setUserSize(10);
+        headRecord.setUserCount(3);
+        headRecord.setStatus(1);
+        headRecord.setActivityId(10L);
+        headRecord.setPicUrl("https://demo26.crmeb.net/uploads/attach/2021/11/15/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg");
+        headRecord.setCombinationPrice(100);
+        detail.setHeadRecord(headRecord);
+        // 团员
+        List<AppCombinationRecordRespVO> list = new ArrayList<>();
+        for (int i = 1; i <= 2; i++) {
+            AppCombinationRecordRespVO record = new AppCombinationRecordRespVO();
+            record.setId((long) i);
+            record.setNickname("用户" + i);
+            record.setAvatar("头像" + i);
+            record.setExpireTime(new Date());
+            record.setUserSize(10);
+            record.setUserCount(i);
+            record.setStatus(1);
+            list.add(record);
+        }
+        detail.setMemberRecords(list);
+        // 订单编号
+        detail.setOrderId(100L);
+        return success(detail);
+    }
+
+}

+ 6 - 15
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/activity/AppCombinationActivityDetailRespVO.java

@@ -25,18 +25,18 @@ public class AppCombinationActivityDetailRespVO {
     @Schema(description = "活动结束时间", required = true)
     private LocalDateTime endTime;
 
+    @Schema(description = "拼团人数", required = true, example = "3")
+    private Integer userSize;
+
+    @Schema(description = "成功的拼团数量", required = true, example = "100")
+    private Integer successCount;
+
     @Schema(description = "商品 SPU 编号", required = true, example = "2048")
     private Long spuId;
 
     @Schema(description = "商品信息数组", required = true)
     private List<Product> products;
 
-    @Schema(description = "成功的拼团记录", required = true)
-    private List<Record> successRecords;
-
-    @Schema(description = "进行中的拼团记录", required = true)
-    private List<Record> runningRecords;
-
     @Schema(description = "商品信息")
     @Data
     public static class Product {
@@ -55,13 +55,4 @@ public class AppCombinationActivityDetailRespVO {
 
     }
 
-    @Schema(description = "拼团记录")
-    @Data
-    public static class Record {
-
-        @Schema(description = "拼团记录编号", required = true, example = "1024")
-        private Long id;
-
-    }
-
 }

+ 31 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/activity/AppCombinationActivityRespVO.java

@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "用户 App - 拼团活动 Response VO")
+@Data
+public class AppCombinationActivityRespVO {
+
+    @Schema(description = "拼团活动编号", required = true, example = "1024")
+    private Long id;
+
+    @Schema(description = "拼团活动名称", required = true, example = "618 大拼团")
+    private String name;
+
+    @Schema(description = "拼团人数", required = true, example = "3")
+    private Integer userSize;
+
+    @Schema(description = "商品 SPU 编号", required = true, example = "2048")
+    private Long spuId;
+
+    @Schema(description = "商品图片", required = true, example = "4096") // 从 SPU 的 picUrl 读取
+    private String picUrl;
+
+    @Schema(description = "商品市场价,单位:分", required = true, example = "50") // 从 SPU 的 marketPrice 读取
+    private Integer marketPrice;
+
+    @Schema(description = "拼团金额,单位:分", required = true, example = "100") // 从拼团商品里取最低价
+    private Integer combinationPrice;
+
+}

+ 21 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordDetailRespVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "用户 App - 拼团记录详细 Response VO")
+@Data
+public class AppCombinationRecordDetailRespVO {
+
+    @Schema(description = "团长的拼团记录", required = true)
+    private AppCombinationRecordRespVO headRecord;
+
+    @Schema(description = "成员的拼团记录", required = true)
+    private List<AppCombinationRecordRespVO> memberRecords;
+
+    @Schema(description = "当前用户参团记录对应的订单编号", required = true, example = "1024") // 如果没参团,返回 null
+    private Long orderId;
+
+}

+ 45 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordRespVO.java

@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+@Schema(description = "用户 App - 拼团记录 Response VO")
+@Data
+public class AppCombinationRecordRespVO {
+
+    @Schema(description = "拼团记录编号", required = true, example = "1024")
+    private Long id;
+
+    @Schema(description = "拼团活动编号", required = true, example = "1024")
+    private Long activityId;
+
+    @Schema(description = "用户昵称", required = true, example = "1024")
+    private String nickname;
+
+    @Schema(description = "用户头像", required = true, example = "1024")
+    private String avatar;
+
+    @Schema(description = "过期时间", required = true)
+    private Date expireTime;
+
+    @Schema(description = "可参团人数", required = true, example = "10")
+    private Integer userSize;
+
+    @Schema(description = "已参团人数", required = true, example = "5")
+    private Integer userCount;
+
+    @Schema(description = "拼团状态", required = true, example = "1")
+    private Integer status;
+
+    @Schema(description = "商品名字", required = true, example = "我是大黄豆")
+    private String spuName;
+
+    @Schema(description = "商品图片", required = true, example = "https://www.iocoder.cn/1.png")
+    private String picUrl;
+
+    @Schema(description = "拼团金额,单位:分", required = true, example = "100")
+    private Integer combinationPrice;
+
+}

+ 18 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordSummaryRespVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "用户 App - 拼团记录的简要概括 Response VO")
+@Data
+public class AppCombinationRecordSummaryRespVO {
+
+    @Schema(description = "拼团用户数量", required = true, example = "1024")
+    private Integer userCount;
+
+    @Schema(description = "拼团用户头像列表", required = true) // 只返回最近的 7 个
+    private List<String> avatars;
+
+}

+ 0 - 3
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/vo/AppSeckillActivitiDetailRespVO.java

@@ -27,9 +27,6 @@ public class AppSeckillActivitiDetailRespVO {
     @Schema(description = "活动结束时间", required = true)
     private LocalDateTime endTime;
 
-
-    private Long successGruopCount;
-
     @Schema(description = "商品 SPU 编号", required = true, example = "2048")
     private Long spuId;
 

+ 21 - 14
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java

@@ -45,19 +45,26 @@ public interface ErrorCodeConstants {
     // ==========  Cart 模块 1011002000 ==========
     ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1011002000, "购物车项不存在");
 
-    // ==========  物流配送模块 1011003000 ==========
-    ErrorCode DELIVERY_EXPRESS_NOT_EXISTS = new ErrorCode(1011003000, "快递公司不存在");
-    // TODO @jason:最好每个模块一段哈。express 一个;exmpresstemplate 一个;pickup 一个
-    ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1011003001, "已经存在该编码的快递公司");
-    ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011003002, "运费模板不存在");
-    ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1011003003, "已经存在该运费模板名");
-    ErrorCode DELIVERY_EXPRESS_USER_ADDRESS_IS_EMPTY = new ErrorCode(1011003004, "计算快递运费时,收件人地址编号为空"); // TODO @jaosn:这个错误码,放到 Price 这块
-    ErrorCode PRODUCT_EXPRESS_TEMPLATE_NOT_FOUND = new ErrorCode(1011003005, "找不到到商品对应的运费模板");  // TODO @jaosn:这个错误码,放到 Price 这块
-    ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1011003006, "快递查询接口异常");
-    ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1011003007, "快递查询返回失败, 原因:{}");
-    ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011003008, "自提门店不存在");
-
-    // ========== Price 相关 1011004000 ============
-    ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011004000, "支付价格计算异常,原因:价格小于等于 0");
+    // ========== Price 相关 1011003000 ============
+    ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011003000, "支付价格计算异常,原因:价格小于等于 0");
+    ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY = new ErrorCode(1011003001, "计算快递运费异常,收件人地址编号为空");
+    ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1011003002, "计算快递运费异常,找不到对应的运费模板");
+
+    // ==========  物流 Express 模块 1011004000 ==========
+    ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1011004000, "快递公司不存在");
+    ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1011004001, "已经存在该编码的快递公司");
+    ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1011004002, "需要接入快递服务商,比如【快递100】");
+
+    ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1011004101, "快递查询接口异常");
+    ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1011004102, "快递查询返回失败,原因:{}");
+
+    // ==========  物流 Template 模块 1011005000 ==========
+    ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1011005000, "已经存在该运费模板名");
+    ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011005001, "运费模板不存在");
+
+    // ==========  物流 PICK_UP 模块 1011006000 ==========
+
+    ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在");
+
 
 }

+ 12 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java

@@ -18,6 +18,7 @@ public class AppTradeOrderSettlementReqVO {
 
     @NotNull(message = "交易类型不能为空")
     @InEnum(value = TradeOrderTypeEnum.class, message = "交易类型必须是 {value}")
+    @Deprecated // TODO 芋艿:后续干掉这个字段,对于前端不需要关注这个
     private Integer type;
 
     @Schema(description = "商品项数组", required = true)
@@ -30,6 +31,17 @@ public class AppTradeOrderSettlementReqVO {
     @Schema(description = "优惠劵编号", example = "1024")
     private Long couponId;
 
+    // ========== 秒杀活动相关字段 ==========
+    @Schema(description = "秒杀活动编号", example = "1024")
+    private Long seckillActivityId;
+
+    // ========== 拼团活动相关字段 ==========
+    @Schema(description = "拼团活动编号", example = "1024")
+    private Long combinationActivityId;
+
+    @Schema(description = "拼团团长编号", example = "2048")
+    private Long combinationHeadId;
+
     @Data
     @Schema(description = "用户 App - 商品项")
     @Valid

+ 26 - 4
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java

@@ -6,12 +6,16 @@ import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplat
 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
+import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
+import com.google.common.collect.Maps;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
 
 import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;
 
 @Mapper
 public interface DeliveryExpressTemplateConvert {
@@ -49,7 +53,7 @@ public interface DeliveryExpressTemplateConvert {
 
     DeliveryExpressTemplateChargeDO convertTemplateCharge(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateChargeUpdateVO vo);
 
-    DeliveryExpressTemplateChargeBO convertTemplateCharge(DeliveryExpressTemplateChargeDO bean);
+    DeliveryExpressTemplateRespBO.Charge convertTemplateCharge(DeliveryExpressTemplateChargeDO bean);
 
     default List<DeliveryExpressTemplateChargeDO> convertTemplateChargeList(Long templateId, Integer chargeMode, List<ExpressTemplateChargeBaseVO> list) {
         return CollectionUtils.convertList(list, vo -> convertTemplateCharge(templateId, chargeMode, vo));
@@ -61,7 +65,7 @@ public interface DeliveryExpressTemplateConvert {
 
     DeliveryExpressTemplateFreeDO convertTemplateFree(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateFreeUpdateVO vo);
 
-    DeliveryExpressTemplateFreeBO convertTemplateFree(DeliveryExpressTemplateFreeDO bean);
+    DeliveryExpressTemplateRespBO.Free convertTemplateFree(DeliveryExpressTemplateFreeDO bean);
 
     List<ExpressTemplateChargeBaseVO> convertTemplateChargeList(List<DeliveryExpressTemplateChargeDO> list);
 
@@ -71,4 +75,22 @@ public interface DeliveryExpressTemplateConvert {
         return CollectionUtils.convertList(list, vo -> convertTemplateFree(templateId, vo));
     }
 
+    default Map<Long, DeliveryExpressTemplateRespBO> convertMap(Integer areaId, List<DeliveryExpressTemplateDO> templateList,
+                                                                List<DeliveryExpressTemplateChargeDO> chargeList,
+                                                                List<DeliveryExpressTemplateFreeDO> freeList) {
+        Map<Long, List<DeliveryExpressTemplateChargeDO>> templateIdChargeMap = convertMultiMap(chargeList,
+                DeliveryExpressTemplateChargeDO::getTemplateId);
+        Map<Long, List<DeliveryExpressTemplateFreeDO>> templateIdFreeMap = convertMultiMap(freeList,
+                DeliveryExpressTemplateFreeDO::getTemplateId);
+        // 组合运费模板配置 RespBO
+        Map<Long, DeliveryExpressTemplateRespBO> result = Maps.newHashMapWithExpectedSize(templateList.size());
+        templateList.forEach(template -> {
+            DeliveryExpressTemplateRespBO bo = new DeliveryExpressTemplateRespBO()
+                    .setChargeMode(template.getChargeMode())
+                    .setCharge(convertTemplateCharge(findFirst(templateIdChargeMap.get(template.getId()), charge -> charge.getAreaIds().contains(areaId))))
+                    .setFree(convertTemplateFree(findFirst(templateIdFreeMap.get(template.getId()), free -> free.getAreaIds().contains(areaId))));
+            result.put(template.getId(), bo);
+        });
+        return result;
+    }
 }

+ 6 - 3
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/delivery/DeliveryExpressTemplateChargeMapper.java

@@ -2,13 +2,11 @@ package cn.iocoder.yudao.module.trade.dal.mysql.delivery;
 
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
 
+import java.util.Collection;
 import java.util.List;
 
 @Mapper
@@ -23,6 +21,11 @@ public interface DeliveryExpressTemplateChargeMapper extends BaseMapperX<Deliver
        return delete(new LambdaQueryWrapper<DeliveryExpressTemplateChargeDO>()
                .eq(DeliveryExpressTemplateChargeDO::getTemplateId, templateId));
     }
+
+    default List<DeliveryExpressTemplateChargeDO> selectByTemplateIds(Collection<Long> templateIds) {
+        return selectList(DeliveryExpressTemplateChargeDO::getTemplateId, templateIds);
+    }
+
 }
 
 

+ 5 - 4
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/delivery/DeliveryExpressTemplateFreeMapper.java

@@ -1,14 +1,11 @@
 package cn.iocoder.yudao.module.trade.dal.mysql.delivery;
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.apache.ibatis.annotations.Mapper;
-import org.springframework.stereotype.Repository;
 
+import java.util.Collection;
 import java.util.List;
 
 @Mapper
@@ -23,6 +20,10 @@ public interface DeliveryExpressTemplateFreeMapper extends BaseMapperX<DeliveryE
         return delete(new LambdaQueryWrapper<DeliveryExpressTemplateFreeDO>()
                 .eq(DeliveryExpressTemplateFreeDO::getTemplateId, templateId));
     }
+
+    default List<DeliveryExpressTemplateFreeDO> selectListByTemplateIds(Collection<Long> templateIds) {
+        return selectList(DeliveryExpressTemplateFreeDO::getTemplateId, templateIds);
+    }
 }
 
 

+ 32 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/ExpressClientConfig.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.trade.framework.delivery.config;
+
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.ExpressClientFactoryImpl;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * 快递客户端端配置类:
+ *
+ * 1. 快递客户端工厂 {@link ExpressClientFactory}
+ * 2. 默认的快递客户端实现 {@link ExpressClient}
+ *
+ * @author jason
+ */
+@Configuration(proxyBeanMethods = false)
+public class ExpressClientConfig {
+
+    @Bean
+    public ExpressClientFactory expressClientFactory(TradeExpressProperties tradeExpressProperties,
+                                                     RestTemplate restTemplate) {
+        return new ExpressClientFactoryImpl(tradeExpressProperties, restTemplate);
+    }
+
+    @Bean
+    public ExpressClient defaultExpressClient(ExpressClientFactory expressClientFactory) {
+        return expressClientFactory.getDefaultExpressClient();
+    }
+
+}

+ 8 - 10
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/TradeExpressQueryProperties.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/TradeExpressProperties.java

@@ -1,6 +1,6 @@
 package cn.iocoder.yudao.module.trade.framework.delivery.config;
 
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderEnum;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.enums.ExpressClientEnum;
 import lombok.Data;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
@@ -9,27 +9,25 @@ import org.springframework.validation.annotation.Validated;
 import javax.validation.Valid;
 import javax.validation.constraints.NotEmpty;
 
-// TODO @jason:TradeExpressProperties;更通用哈
 // TODO @芋艿:未来要不要放数据库中?考虑 saas 多租户时,不同租户使用不同的配置?
 /**
- * 交易快递查询的配置项
+ * 交易运费快递的配置项
  *
  * @author jason
  */
 @Component
-@ConfigurationProperties(prefix = "yudao.trade.express.query")
+@ConfigurationProperties(prefix = "yudao.trade.express")
 @Data
 @Validated
-public class TradeExpressQueryProperties {
+public class TradeExpressProperties {
 
     /**
-     * 快递查询服务商
+     * 快递客户端
      *
-     * 如果未配置,默认使用快递鸟
+     * 默认不提供,需要提醒用户配置一个快递服务商。
      */
-    // TODO @jason:可以把 expressQueryProvider 改成 client 变量,更简洁一点;
-    private ExpressQueryProviderEnum expressQueryProvider; // TODO @jaosn:默认值可以通过属性直接赋值哈;
-    // TODO @jason:需要考虑下,用户只配置了其中一个;
+    private ExpressClientEnum client = ExpressClientEnum.NOT_PROVIDE;
+
     /**
      * 快递鸟配置
      */

+ 0 - 24
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/ExpressQueryClient.java

@@ -1,24 +0,0 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core;
-
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
-
-import java.util.List;
-
-// TODO @jason:可以改成 ExpressClient,未来可能还对接别的接口噢
-/**
- * 快递查询客户端
- *
- * @author jason
- */
-public interface ExpressQueryClient {
-
-    /**
-     * 快递实时查询
-     *
-     * @param reqDTO 查询请求参数
-     */
-    // TODO @jason:可以改成 getExpressTrackList。返回字段可以参考 https://doc.youzanyun.com/detail/API/0/5 响应的 data
-    List<ExpressQueryRespDTO> realTimeQuery(ExpressQueryReqDTO reqDTO);
-
-}

+ 0 - 22
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/ExpressQueryProvider.java

@@ -1,22 +0,0 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core;
-
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
-
-import java.util.List;
-
-/**
- * 快递查询服务商
- *
- * @author jason
- */
-public interface ExpressQueryProvider {
-
-    /**
-     * 快递实时查询
-     *
-     * @param reqDTO 查询请求参数
-     */
-    List<ExpressQueryRespDTO> realTimeQueryExpress(ExpressQueryReqDTO reqDTO);
-
-}

+ 0 - 33
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/ExpressQueryProviderEnum.java

@@ -1,33 +0,0 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core;
-
-import lombok.Getter;
-
-/**
- * 快递查询服务商枚举
- *
- * @author jason
- */
-@Getter
-
-public enum ExpressQueryProviderEnum {
-
-    KD_NIAO("kd-niao", "快递鸟"),
-    KD_100("kd-100", "快递100");
-
-    /**
-     * 快递服务商唯一编码
-     */
-    private final String code;
-
-    /**
-     * 快递服务商名称
-     */
-    private final String name;
-
-    // TODO @jaosn:@AllArgsConstructor 可以替代哈
-    ExpressQueryProviderEnum(String code, String name) {
-        this.code = code;
-        this.name = name;
-    }
-
-}

+ 0 - 19
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/ExpressQueryProviderFactory.java

@@ -1,19 +0,0 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core;
-
-/**
- * 快递服务商工厂,用于创建和缓存快递服务商服务
- *
- * @author jason
- */
-public interface ExpressQueryProviderFactory {
-
-    /**
-     * 通过枚举获取快递查询服务商
-     *
-     * 如果不存在,就创建一个对应的快递查询服务商
-     *
-     * @param queryProviderEnum 快递服务商枚举
-     */
-    ExpressQueryProvider getOrCreateExpressQueryProvider(ExpressQueryProviderEnum queryProviderEnum);
-
-}

+ 23 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/ExpressClient.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client;
+
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
+
+import java.util.List;
+
+/**
+ * 快递客户端接口
+ *
+ * @author jason
+ */
+public interface ExpressClient {
+
+    /**
+     * 快递实时查询
+     *
+     * @param reqDTO 查询请求参数
+     */
+    // TODO @jason:返回字段可以参考 https://doc.youzanyun.com/detail/API/0/5 响应的 data
+    List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO);
+
+}

+ 24 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/ExpressClientFactory.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client;
+
+import cn.iocoder.yudao.module.trade.framework.delivery.core.enums.ExpressClientEnum;
+
+/**
+ * 快递客户端工厂接口:用于创建和缓存快递客户端
+ *
+ * @author jason
+ */
+public interface ExpressClientFactory {
+
+    /**
+     * 获取默认的快递客户端
+     */
+    ExpressClient getDefaultExpressClient();
+
+    /**
+     * 通过枚举获取快递客户端,如果不存在,就创建一个对应快递客户端
+     *
+     * @param clientEnum 快递客户端枚举
+     */
+    ExpressClient getOrCreateExpressClient(ExpressClientEnum clientEnum);
+
+}

+ 27 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/convert/ExpressQueryConvert.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.convert;
+
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryRespDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryRespDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface ExpressQueryConvert {
+
+    ExpressQueryConvert INSTANCE = Mappers.getMapper(ExpressQueryConvert.class);
+
+    List<ExpressTrackRespDTO> convertList(List<KdNiaoExpressQueryRespDTO.ExpressTrack> expressTrackList);
+
+    List<ExpressTrackRespDTO> convertList2(List<Kd100ExpressQueryRespDTO.ExpressTrack> expressTrackList);
+
+    KdNiaoExpressQueryReqDTO convert(ExpressTrackQueryReqDTO dto);
+
+    Kd100ExpressQueryReqDTO convert2(ExpressTrackQueryReqDTO dto);
+
+}

+ 4 - 5
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/dto/ExpressQueryReqDTO.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/ExpressTrackQueryReqDTO.java

@@ -1,23 +1,22 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.dto;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto;
 
 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
 import lombok.Data;
 
 /**
- * 快递查询 Req DTO
+ * 快递轨迹的查询 Req DTO
  *
  * @author jason
  */
 @Data
-public class ExpressQueryReqDTO {
+public class ExpressTrackQueryReqDTO {
 
     /**
      * 快递公司编码
      *
      * 对应 {@link DeliveryExpressDO#getCode()}
      */
-    // TODO @jaosn:要不改成 expressCode;项目里使用这个哈
-    private String expressCompanyCode;
+    private String expressCode;
 
     /**
      * 发货快递单号

+ 2 - 2
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/dto/ExpressQueryRespDTO.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/ExpressTrackRespDTO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.dto;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto;
 
 import lombok.Data;
 
@@ -8,7 +8,7 @@ import lombok.Data;
  * @author jason
  */
 @Data
-public class ExpressQueryRespDTO {
+public class ExpressTrackRespDTO {
 
     // TODO @jason:LocalDateTime
     /**

+ 2 - 3
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/dto/provider/kd100/Kd100ExpressQueryReqDTO.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryReqDTO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -13,12 +13,11 @@ import lombok.Data;
 @JsonInclude(JsonInclude.Include.NON_NULL)
 public class Kd100ExpressQueryReqDTO {
 
-    // TODO @jaosn:要不改成 expressCode;项目里使用这个哈
     /**
      * 快递公司编码
      */
     @JsonProperty("com")
-    private String expressCompanyCode;
+    private String expressCode;
 
     /**
      * 快递单号

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/dto/provider/kd100/Kd100ExpressQueryRespDTO.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;

+ 2 - 3
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/dto/provider/kdniao/KdNiaoExpressQueryReqDTO.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryReqDTO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -13,12 +13,11 @@ import lombok.Data;
 @JsonInclude(JsonInclude.Include.NON_NULL)
 public class KdNiaoExpressQueryReqDTO {
 
-    // TODO @jaosn:要不改成 expressCode;项目里使用这个哈
     /**
      * 快递公司编码
      */
     @JsonProperty("ShipperCode")
-    private String expressCompanyCode;
+    private String expressCode;
     /**
      * 快递单号
      */

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/dto/provider/kdniao/KdNiaoExpressQueryRespDTO.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;

+ 54 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/ExpressClientFactoryImpl.java

@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
+
+import cn.hutool.core.lang.Assert;
+import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kd100.Kd100ExpressClient;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kdniao.KdNiaoExpressClient;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.enums.ExpressClientEnum;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
+import lombok.AllArgsConstructor;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 快递客户端工厂实现类
+ *
+ * @author jason
+ */
+@AllArgsConstructor
+public class ExpressClientFactoryImpl implements ExpressClientFactory {
+
+    private final Map<ExpressClientEnum, ExpressClient> clientMap = new ConcurrentHashMap<>(8);
+
+    private final TradeExpressProperties tradeExpressProperties;
+    private final RestTemplate restTemplate;
+
+    @Override
+    public ExpressClient getDefaultExpressClient() {
+        ExpressClient defaultClient = getOrCreateExpressClient(tradeExpressProperties.getClient());
+        Assert.notNull("默认的快递客户端不能为空");
+        return defaultClient;
+    }
+
+    @Override
+    public ExpressClient getOrCreateExpressClient(ExpressClientEnum clientEnum) {
+        return clientMap.computeIfAbsent(clientEnum,
+                client -> createExpressClient(client, tradeExpressProperties));
+    }
+
+    private ExpressClient createExpressClient(ExpressClientEnum queryProviderEnum,
+                                                TradeExpressProperties tradeExpressProperties) {
+        switch (queryProviderEnum) {
+            case NOT_PROVIDE:
+                return new NoProvideExpressClient();
+            case KD_NIAO:
+                return new KdNiaoExpressClient(restTemplate, tradeExpressProperties.getKdNiao());
+            case KD_100:
+                return new Kd100ExpressClient(restTemplate, tradeExpressProperties.getKd100());
+        }
+        return null;
+    }
+}

+ 24 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/NoProvideExpressClient.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
+
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
+
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_CLIENT_NOT_PROVIDE;
+
+/**
+ * 未实现的快递客户端,用来提醒用户需要接入快递服务商,
+ *
+ * @author jason
+ */
+public class NoProvideExpressClient implements ExpressClient {
+
+    @Override
+    public List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) {
+        throw exception(EXPRESS_CLIENT_NOT_PROVIDE);
+    }
+
+}

+ 26 - 39
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/impl/Kd100ExpressQueryProvider.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kd100/Kd100ExpressClient.java

@@ -1,15 +1,16 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.impl;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kd100;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.HexUtil;
 import cn.hutool.crypto.digest.DigestUtil;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProvider;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100.Kd100ExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100.Kd100ExpressQueryRespDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryRespDTO;
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.*;
 import org.springframework.util.LinkedMultiValueMap;
@@ -23,48 +24,40 @@ import java.util.Objects;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_ERROR;
 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_FAILED;
-import static cn.iocoder.yudao.module.trade.framework.delivery.core.convert.ExpressQueryConvert.INSTANCE;
+import static cn.iocoder.yudao.module.trade.framework.delivery.core.client.convert.ExpressQueryConvert.INSTANCE;
 
-// TODO @jason:可以参考 KdNiaoExpressQueryProvider 建议改改哈
 /**
- * 快递 100 服务商
+ * 快递 100 客户端
  *
  * @author jason
  */
 @Slf4j
-public class Kd100ExpressQueryProvider implements ExpressQueryProvider {
+@AllArgsConstructor
+public class Kd100ExpressClient implements ExpressClient {
 
     private static final String REAL_TIME_QUERY_URL = "https://poll.kuaidi100.com/poll/query.do";
 
     private final RestTemplate restTemplate;
-    private final TradeExpressQueryProperties.Kd100Config config;
-
-    public Kd100ExpressQueryProvider(RestTemplate restTemplate, TradeExpressQueryProperties.Kd100Config config) {
-        this.restTemplate = restTemplate;
-        this.config = config;
-    }
+    private final TradeExpressProperties.Kd100Config config;
 
     @Override
-    public List<ExpressQueryRespDTO> realTimeQueryExpress(ExpressQueryReqDTO reqDTO) {
+    public List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) {
         // 发起查询
         Kd100ExpressQueryReqDTO kd100ReqParam = INSTANCE.convert2(reqDTO);
-        kd100ReqParam.setExpressCompanyCode(kd100ReqParam.getExpressCompanyCode().toLowerCase()); // 快递公司编码需要转成小写
-        Kd100ExpressQueryRespDTO respDTO = sendExpressQueryReq(REAL_TIME_QUERY_URL, kd100ReqParam,
+        kd100ReqParam.setExpressCode(kd100ReqParam.getExpressCode().toLowerCase()); // 快递公司编码需要转成小写
+        Kd100ExpressQueryRespDTO respDTO = requestExpressQuery(REAL_TIME_QUERY_URL, kd100ReqParam,
                 Kd100ExpressQueryRespDTO.class);
-        log.debug("[realTimeQueryExpress][快递 100 接口 查询接口返回 {}]", respDTO);
+        log.debug("[getExpressTrackList][快递 100 接口 查询接口返回 {}]", respDTO);
+
         // 处理结果
         if (Objects.equals("false", respDTO.getResult())) {
-            log.error("[realTimeQueryExpress][快递 100 接口 返回失败 {}]", respDTO.getMessage());
+            log.error("[getExpressTrackList][快递 100 接口 返回失败 {}]", respDTO.getMessage());
             throw exception(EXPRESS_API_QUERY_FAILED, respDTO.getMessage());
-        // TODO @json:else 可以不用写哈;
-        } else {
-            // TODO @jason:convertList2 如果空,应该返回 list 了;
-            if (CollUtil.isNotEmpty(respDTO.getTracks())) {
-                return INSTANCE.convertList2(respDTO.getTracks());
-            } else {
-                return Collections.emptyList();
-            }
         }
+        if (CollUtil.isEmpty(respDTO.getTracks())) {
+            return Collections.emptyList();
+        }
+        return INSTANCE.convertList2(respDTO.getTracks());
     }
 
     /**
@@ -76,8 +69,7 @@ public class Kd100ExpressQueryProvider implements ExpressQueryProvider {
      * @param <Req> 每个请求的请求结构 Req DTO
      * @param <Resp> 每个请求的响应结构 Resp DTO
      */
-    // TODO @jason:可以改成 request,发起请求哈;
-    private <Req, Resp> Resp sendExpressQueryReq(String url, Req req, Class<Resp> respClass) {
+    private <Req, Resp> Resp requestExpressQuery(String url, Req req, Class<Resp> respClass) {
         // 请求头
         HttpHeaders headers = new HttpHeaders();
         headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
@@ -92,23 +84,18 @@ public class Kd100ExpressQueryProvider implements ExpressQueryProvider {
         log.debug("[sendExpressQueryReq][快递 100 接口的请求参数: {}]", requestBody);
         // 发送请求
         HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
-        // TODO @jason:可以使用 restTemplate 的 post 方法哇?
         ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
         log.debug("[sendExpressQueryReq][快递 100 接口响应结果 {}]", responseEntity);
 
         // 处理响应
-        // TODO @jason:if return 原则;if (!responseEntity.getStatusCode().is2xxSuccessful()) 抛出异常;接着处理成功的
-        if (responseEntity.getStatusCode().is2xxSuccessful()) {
-            String response = responseEntity.getBody();
-            return JsonUtils.parseObject(response, respClass);
-        } else {
+        if (!responseEntity.getStatusCode().is2xxSuccessful()) {
             throw exception(EXPRESS_API_QUERY_ERROR);
         }
+        return JsonUtils.parseObject(responseEntity.getBody(), respClass);
     }
 
     private String generateReqSign(String param, String key, String customer) {
         String plainText = String.format("%s%s%s", param, key, customer);
-        // TODO @jason:DigestUtil.md5Hex(plainText);
         return HexUtil.encodeHexStr(DigestUtil.md5(plainText), false);
     }
 

+ 32 - 39
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/impl/KdNiaoExpressQueryProvider.java → yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java

@@ -1,16 +1,17 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.impl;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kdniao;
 
 import cn.hutool.core.codec.Base64;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.net.URLEncodeUtil;
 import cn.hutool.crypto.digest.DigestUtil;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProvider;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao.KdNiaoExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao.KdNiaoExpressQueryRespDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryRespDTO;
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.*;
 import org.springframework.util.LinkedMultiValueMap;
@@ -23,15 +24,16 @@ import java.util.List;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_FAILED;
 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_API_QUERY_ERROR;
-import static cn.iocoder.yudao.module.trade.framework.delivery.core.convert.ExpressQueryConvert.INSTANCE;
+import static cn.iocoder.yudao.module.trade.framework.delivery.core.client.convert.ExpressQueryConvert.INSTANCE;
 
 /**
- * 快递鸟服务商
+ * 快递鸟客户端
  *
  * @author jason
  */
 @Slf4j
-public class KdNiaoExpressQueryProvider implements ExpressQueryProvider {
+@AllArgsConstructor
+public class KdNiaoExpressClient implements ExpressClient {
 
     private static final String REAL_TIME_QUERY_URL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";
 
@@ -39,15 +41,8 @@ public class KdNiaoExpressQueryProvider implements ExpressQueryProvider {
      * 快递鸟即时查询免费版 RequestType
      */
     private static final String REAL_TIME_FREE_REQ_TYPE = "1002";
-
     private final RestTemplate restTemplate;
-    private final TradeExpressQueryProperties.KdNiaoConfig config;
-
-    // TODO @jason:可以改成 lombok 哈
-    public KdNiaoExpressQueryProvider(RestTemplate restTemplate, TradeExpressQueryProperties.KdNiaoConfig config) {
-        this.restTemplate = restTemplate;
-        this.config = config;
-    }
+    private final TradeExpressProperties.KdNiaoConfig config;
 
     /**
      * 快递鸟即时查询免费版本
@@ -56,26 +51,27 @@ public class KdNiaoExpressQueryProvider implements ExpressQueryProvider {
      * @param reqDTO 查询请求参数
      */
     @Override
-    public List<ExpressQueryRespDTO> realTimeQueryExpress(ExpressQueryReqDTO reqDTO) {
+    public List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) {
         KdNiaoExpressQueryReqDTO kdNiaoReqData = INSTANCE.convert(reqDTO);
         // 快递公司编码需要转成大写
-        kdNiaoReqData.setExpressCompanyCode(reqDTO.getExpressCompanyCode().toUpperCase());
-        KdNiaoExpressQueryRespDTO respDTO = sendKdNiaoApiRequest(REAL_TIME_QUERY_URL, REAL_TIME_FREE_REQ_TYPE,
+        kdNiaoReqData.setExpressCode(reqDTO.getExpressCode().toUpperCase());
+        KdNiaoExpressQueryRespDTO respDTO = requestKdNiaoApi(REAL_TIME_QUERY_URL, REAL_TIME_FREE_REQ_TYPE,
                 kdNiaoReqData, KdNiaoExpressQueryRespDTO.class);
-        log.debug("[realTimeQueryExpress][快递鸟即时查询接口返回 {}]", respDTO);
-        if(!respDTO.getSuccess()){
-            throw exception(EXPRESS_API_QUERY_FAILED, respDTO.getReason());
-        }else{
-            if (CollUtil.isNotEmpty(respDTO.getTracks())) {
-                return INSTANCE.convertList(respDTO.getTracks());
-            }else{
-                return Collections.emptyList();
-            }
+        log.debug("[getExpressTrackList][快递鸟即时查询接口返回 {}]", respDTO);
+
+        // 处理结果
+        if (respDTO == null || !respDTO.getSuccess()) {
+            throw exception(EXPRESS_API_QUERY_FAILED, respDTO == null ? "" : respDTO.getReason());
         }
+        if (CollUtil.isNotEmpty(respDTO.getTracks())) {
+            return Collections.emptyList();
+        }
+        return INSTANCE.convertList(respDTO.getTracks());
     }
 
     /**
-     * 快递鸟 通用的 API 请求, 暂时没有其他应用场景, 暂时放这里
+     * 快递鸟 通用的 API 请求,暂时没有其他应用场景, 暂时放这里
+     *
      * @param url 请求 url
      * @param requestType 对应的请求指令 (快递鸟的RequestType)
      * @param req  对应请求的请求参数
@@ -83,8 +79,8 @@ public class KdNiaoExpressQueryProvider implements ExpressQueryProvider {
      * @param <Req> 每个请求的请求结构 Req DTO
      * @param <Resp> 每个请求的响应结构 Resp DTO
      */
-    private  <Req, Resp> Resp sendKdNiaoApiRequest(String url, String requestType, Req req,
-                                                   Class<Resp> respClass){
+    private <Req, Resp> Resp requestKdNiaoApi(String url, String requestType, Req req,
+                                              Class<Resp> respClass){
         // 请求头
         HttpHeaders headers = new HttpHeaders();
         headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
@@ -97,19 +93,16 @@ public class KdNiaoExpressQueryProvider implements ExpressQueryProvider {
         requestBody.add("EBusinessID", config.getBusinessId());
         requestBody.add("DataSign", dataSign);
         requestBody.add("RequestType", requestType);
-        log.debug("[sendKdNiaoApiRequest][快递鸟接口 RequestType : {}, 的请求参数 {}]", requestType, requestBody);
-
+        log.debug("[requestKdNiaoApi][快递鸟接口 RequestType : {}, 的请求参数 {}]", requestType, requestBody);
         // 发送请求
         HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
         ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
         log.debug("快递鸟接口 RequestType : {}, 的响应结果 {}", requestType,  responseEntity);
         // 处理响应
-        if (responseEntity.getStatusCode().is2xxSuccessful()) {
-            String response = responseEntity.getBody();
-            return JsonUtils.parseObject(response, respClass);
-        } else {
+        if (!responseEntity.getStatusCode().is2xxSuccessful()) {
             throw exception(EXPRESS_API_QUERY_ERROR);
         }
+        return JsonUtils.parseObject(responseEntity.getBody(), respClass);
     }
 
     /**

+ 0 - 27
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/convert/ExpressQueryConvert.java

@@ -1,27 +0,0 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.convert;
-
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100.Kd100ExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kd100.Kd100ExpressQueryRespDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao.KdNiaoExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.provider.kdniao.KdNiaoExpressQueryRespDTO;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-import java.util.List;
-
-@Mapper
-public interface ExpressQueryConvert {
-
-    ExpressQueryConvert INSTANCE = Mappers.getMapper(ExpressQueryConvert.class);
-
-    List<ExpressQueryRespDTO> convertList(List<KdNiaoExpressQueryRespDTO.ExpressTrack> expressTrackList);
-
-    List<ExpressQueryRespDTO> convertList2(List<Kd100ExpressQueryRespDTO.ExpressTrack> expressTrackList);
-
-    KdNiaoExpressQueryReqDTO convert(ExpressQueryReqDTO dto);
-
-    Kd100ExpressQueryReqDTO convert2(ExpressQueryReqDTO dto);
-
-}

+ 28 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/enums/ExpressClientEnum.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.trade.framework.delivery.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 快递客户端枚举
+ *
+ * @author jason
+ */
+@Getter
+@AllArgsConstructor
+public enum ExpressClientEnum {
+
+    NOT_PROVIDE("not-provide","未提供"),
+    KD_NIAO("kd-niao", "快递鸟"),
+    KD_100("kd-100", "快递100");
+
+    /**
+     * 快递服务商唯一编码
+     */
+    private final String code;
+    /**
+     * 快递服务商名称
+     */
+    private final String name;
+
+}

+ 0 - 65
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/impl/ExpressQueryClientImpl.java

@@ -1,65 +0,0 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.impl;
-
-import cn.hutool.core.lang.Assert;
-import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryClient;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProvider;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderEnum;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderFactory;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryRespDTO;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.Resource;
-import java.util.List;
-
-import static cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderEnum.KD_NIAO;
-
-// TODO @jason:可以把整体包结构调整下;参考 sms client 的方式;
-// + config
-// + core
-//      client
-//         + dto
-//         + impl:里面可以放 kdniaoclient、kd100client
-//         ExpressClient
-//         ExpressClientFactory: 通过它直接获取默认和创建默认的 Client
-//      enums
-/**
- * 快递查询客户端实现
- *
- * @author jason
- */
-@Component
-@Slf4j
-public class ExpressQueryClientImpl implements ExpressQueryClient  {
-
-    @Resource
-    private ExpressQueryProviderFactory expressQueryProviderFactory;
-    @Resource
-    private TradeExpressQueryProperties tradeExpressQueryProperties;
-
-    private ExpressQueryProvider expressQueryProvider;
-
-    @PostConstruct
-    private void init() {
-        // 如果未设置,默认使用快递鸟
-        ExpressQueryProviderEnum queryProvider = tradeExpressQueryProperties.getExpressQueryProvider();
-        if (queryProvider == null) {
-            queryProvider = KD_NIAO;
-        }
-        // 创建客户端
-        expressQueryProvider = expressQueryProviderFactory.getOrCreateExpressQueryProvider(queryProvider);
-        if (expressQueryProvider == null) {
-            log.error("获取创建快递查询服务商{}失败,请检查相关配置", queryProvider);
-        }
-        Assert.notNull(expressQueryProvider, "快递查询服务商不能为空");
-    }
-
-    @Override
-    public List<ExpressQueryRespDTO> realTimeQuery(ExpressQueryReqDTO reqDTO) {
-        return expressQueryProvider.realTimeQueryExpress(reqDTO);
-    }
-
-}

+ 0 - 48
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/impl/ExpressQueryProviderFactoryImpl.java

@@ -1,48 +0,0 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.impl;
-
-import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProvider;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderEnum;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.ExpressQueryProviderFactory;
-import org.springframework.stereotype.Component;
-import org.springframework.web.client.RestTemplate;
-
-import javax.annotation.Resource;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * // TODO @jason:注释不全
- * @author jason
- */
-@Component
-public class ExpressQueryProviderFactoryImpl implements ExpressQueryProviderFactory {
-
-    private final Map<ExpressQueryProviderEnum, ExpressQueryProvider> providerMap = new ConcurrentHashMap<>(8);
-
-    @Resource
-    private TradeExpressQueryProperties tradeExpressQueryProperties;
-    @Resource
-    private RestTemplate restTemplate;
-
-    @Override
-    public ExpressQueryProvider getOrCreateExpressQueryProvider(ExpressQueryProviderEnum queryProviderEnum) {
-        return providerMap.computeIfAbsent(queryProviderEnum,
-                provider -> createExpressQueryProvider(provider, tradeExpressQueryProperties));
-    }
-
-    private ExpressQueryProvider createExpressQueryProvider(ExpressQueryProviderEnum queryProviderEnum,
-                                                            TradeExpressQueryProperties tradeExpressQueryProperties) {
-        // TODO @jason:是不是直接 return 就好啦,更简洁一点
-        ExpressQueryProvider result = null;
-        switch (queryProviderEnum) {
-            case KD_NIAO:
-                result = new KdNiaoExpressQueryProvider(restTemplate, tradeExpressQueryProperties.getKdNiao());
-                break;
-            case KD_100:
-                result = new Kd100ExpressQueryProvider(restTemplate, tradeExpressQueryProperties.getKd100());
-                break;
-        }
-        return result;
-    }
-}

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressServiceImpl.java

@@ -75,7 +75,7 @@ public class DeliveryExpressServiceImpl implements DeliveryExpressService {
     }
     private void validateDeliveryExpressExists(Long id) {
         if (deliveryExpressMapper.selectById(id) == null) {
-            throw exception(DELIVERY_EXPRESS_NOT_EXISTS);
+            throw exception(EXPRESS_NOT_EXISTS);
         }
     }
 

+ 5 - 6
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java

@@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplat
 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO;
 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO;
 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
+import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
 
 import javax.validation.Valid;
 import java.util.Collection;
@@ -83,14 +83,13 @@ public interface DeliveryExpressTemplateService {
      */
     DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId);
 
-    // TODO @jason:可以把 spuIds 改成传递 ids 么?价格计算那,在 TradePriceCalculateRespBO 冗余好 templateId 字段。目的是,减少重复的查询
     /**
-     * 基于指定的 SPU 编号数组和收件人地址区域编号. 获取匹配运费模板
+     * 基于运费模板编号数组和收件人地址区域编号,获取匹配运费模板
      *
-     * @param spuIds    SPU 编号列表
+     * @param ids    编号列表
      * @param areaId 区域编号
-     * @return Map (spuId -> 运费模板设置)
+     * @return Map (templateId -> 运费模板设置)
      */
-    Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateMapBySpuIdsAndArea(Collection<Long> spuIds, Integer areaId);
+    Map<Long, DeliveryExpressTemplateRespBO> getExpressTemplateMapByIdsAndArea(Collection<Long> ids, Integer areaId);
 
 }

+ 18 - 35
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java

@@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.trade.service.delivery;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
-import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateCreateReqVO;
 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateDetailRespVO;
 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO;
@@ -15,9 +13,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemp
 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper;
 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper;
 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateMapper;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
+import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -46,8 +42,6 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
     private DeliveryExpressTemplateChargeMapper expressTemplateChargeMapper;
     @Resource
     private DeliveryExpressTemplateFreeMapper expressTemplateFreeMapper;
-    @Resource
-    private ProductSpuApi productSpuApi;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -228,41 +222,30 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
     }
 
     @Override
-    public Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateMapBySpuIdsAndArea(Collection<Long> spuIds, Integer areaId) {
+    public Map<Long, DeliveryExpressTemplateRespBO> getExpressTemplateMapByIdsAndArea(Collection<Long> ids, Integer areaId) {
         Assert.notNull(areaId, "区域编号 {} 不能为空", areaId);
-        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
-        if (CollUtil.isEmpty(spuList)) {
+        // 查询 template 数组
+        if (CollUtil.isEmpty(ids)) {
             return Collections.emptyMap();
         }
-        Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getDeliveryTemplateId);
-        List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(spuMap.keySet());
-        Map<Long, SpuDeliveryExpressTemplateRespBO> result = new HashMap<>(templateList.size());
-        templateList.forEach(item -> {
-            ProductSpuRespDTO spu = spuMap.get(item.getId());
-            if (spu == null) {
-                return;
-            }
-            // TODO @jason:避免循环查询;最好类似 expressTemplateMapper.selectBatchIds(spuMap.keySet()); 批量查询,内存组合;
-            SpuDeliveryExpressTemplateRespBO bo = new SpuDeliveryExpressTemplateRespBO()
-                    .setChargeMode(item.getChargeMode())
-                    .setTemplateCharge(findMatchExpressTemplateCharge(item.getId(), areaId))
-                    .setTemplateFree(findMatchExpressTemplateFree(item.getId(), areaId));
-            result.put(spu.getId(), bo);
-        });
-        return result;
+        List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(ids);
+        // 查询 templateCharge 数组
+        List<DeliveryExpressTemplateChargeDO> chargeList = expressTemplateChargeMapper.selectByTemplateIds(ids);
+        // 查询 templateFree 数组
+        List<DeliveryExpressTemplateFreeDO> freeList = expressTemplateFreeMapper.selectListByTemplateIds(ids);
+
+        // 组合运费模板配置 RespBO
+        return INSTANCE.convertMap(areaId, templateList, chargeList, freeList);
     }
 
-    private DeliveryExpressTemplateChargeBO findMatchExpressTemplateCharge(Long templateId, Integer areaId) {
-        return INSTANCE.convertTemplateCharge(findFirst(
-                        expressTemplateChargeMapper.selectListByTemplateId(templateId), item -> item.getAreaIds().contains(areaId)
-                )
-        );
+    private DeliveryExpressTemplateRespBO.Charge findMatchExpressTemplateCharge(
+            List<DeliveryExpressTemplateChargeDO> templateChargeList, Integer areaId) {
+        return INSTANCE.convertTemplateCharge(findFirst(templateChargeList, item -> item.getAreaIds().contains(areaId)));
     }
 
-    private DeliveryExpressTemplateFreeBO findMatchExpressTemplateFree(Long templateId, Integer areaId) {
-        return INSTANCE.convertTemplateFree(findFirst(
-                expressTemplateFreeMapper.selectListByTemplateId(templateId), item -> item.getAreaIds().contains(areaId)
-        ));
+    private DeliveryExpressTemplateRespBO.Free findMatchExpressTemplateFree(
+            List<DeliveryExpressTemplateFreeDO> templateFreeList, Integer areaId) {
+        return INSTANCE.convertTemplateFree(findFirst(templateFreeList, item -> item.getAreaIds().contains(areaId)));
     }
 
 }

+ 0 - 29
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/DeliveryExpressTemplateChargeBO.java

@@ -1,29 +0,0 @@
-package cn.iocoder.yudao.module.trade.service.delivery.bo;
-
-import lombok.Data;
-
-/**
- * 快递运费模板费用配置 BO
- *
- * @author jason
- */
-@Data
-public class DeliveryExpressTemplateChargeBO {
-
-    /**
-     * 首件数量(件数,重量,或体积)
-     */
-    private Double startCount;
-    /**
-     * 起步价,单位:分
-     */
-    private Integer startPrice;
-    /**
-     * 续件数量(件, 重量,或体积)
-     */
-    private Double extraCount;
-    /**
-     * 额外价,单位:分
-     */
-    private Integer extraPrice;
-}

+ 0 - 26
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/DeliveryExpressTemplateFreeBO.java

@@ -1,26 +0,0 @@
-package cn.iocoder.yudao.module.trade.service.delivery.bo;
-
-import lombok.Data;
-
-/**
- * 快递运费模板包邮配置 BO
- *
- * @author jason
- */
-@Data
-public class DeliveryExpressTemplateFreeBO {
-
-    /**
-     * 包邮金额,单位:分
-     *
-     * 订单总金额 > 包邮金额时,才免运费
-     */
-    private Integer freePrice;
-
-    /**
-     * 包邮件数
-     *
-     * 订单总件数 > 包邮件数时,才免运费
-     */
-    private Integer freeCount;
-}

+ 80 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/DeliveryExpressTemplateRespBO.java

@@ -0,0 +1,80 @@
+package cn.iocoder.yudao.module.trade.service.delivery.bo;
+
+import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
+import lombok.Data;
+
+/**
+ * 运费模板配置 Resp BO
+ *
+ * @author jason
+ */
+@Data
+public class DeliveryExpressTemplateRespBO {
+
+    /**
+     * 配送计费方式
+     *
+     * 枚举 {@link DeliveryExpressChargeModeEnum}
+     */
+    private Integer chargeMode;
+
+    /**
+     * 运费模板快递运费设置
+     */
+    private Charge charge;
+
+    /**
+     * 运费模板包邮设置
+     */
+    private Free free;
+
+    /**
+     * 快递运费模板费用配置 BO
+     *
+     * @author jason
+     */
+    @Data
+    public static class Charge {
+
+        /**
+         * 首件数量(件数,重量,或体积)
+         */
+        private Double startCount;
+        /**
+         * 起步价,单位:分
+         */
+        private Integer startPrice;
+        /**
+         * 续件数量(件, 重量,或体积)
+         */
+        private Double extraCount;
+        /**
+         * 额外价,单位:分
+         */
+        private Integer extraPrice;
+    }
+
+    /**
+     * 快递运费模板包邮配置 BO
+     *
+     * @author jason
+     */
+    @Data
+    public static class Free {
+
+        /**
+         * 包邮金额,单位:分
+         *
+         * 订单总金额 > 包邮金额时,才免运费
+         */
+        private Integer freePrice;
+
+        /**
+         * 包邮件数
+         *
+         * 订单总件数 > 包邮件数时,才免运费
+         */
+        private Integer freeCount;
+    }
+
+}

+ 0 - 33
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/SpuDeliveryExpressTemplateRespBO.java

@@ -1,33 +0,0 @@
-package cn.iocoder.yudao.module.trade.service.delivery.bo;
-
-import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
-import lombok.Data;
-
-/**
- * SPU 运费模板配置 Resp BO
- *
- * @author jason
- */
-@Data
-public class SpuDeliveryExpressTemplateRespBO {
-
-    /**
-     * 配送计费方式
-     *
-     * 枚举 {@link DeliveryExpressChargeModeEnum}
-     */
-    private Integer chargeMode;
-
-    // TODO @jaosn:可以把 DeliveryExpressTemplateChargeBO 和 DeliveryExpressTemplateFreeBO 搞成内嵌的类。这样简洁一点
-
-    /**
-     * 运费模板快递运费设置
-     */
-    private DeliveryExpressTemplateChargeBO templateCharge;
-
-    /**
-     * 运费模板包邮设置
-     */
-    private DeliveryExpressTemplateFreeBO templateFree;
-
-}

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

@@ -342,7 +342,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
         // TODO 芋艿:logisticsId 校验存在 发货物流公司 fix
         DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
         if (deliveryExpress == null) {
-            throw exception(DELIVERY_EXPRESS_NOT_EXISTS);
+            throw exception(EXPRESS_NOT_EXISTS);
         }
 
         // 更新 TradeOrderDO 状态为已发货,等待收货

+ 5 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java

@@ -180,6 +180,11 @@ public class TradePriceCalculateRespBO {
          */
         private Long categoryId;
 
+        /**
+         * 运费模板 Id
+         */
+        private Long deliveryTemplateId;
+
         // ========== 商品 SKU 信息 ==========
         /**
          * 商品重量,单位:kg 千克

+ 25 - 28
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java

@@ -7,9 +7,7 @@ import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
+import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem;
@@ -24,8 +22,8 @@ import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
-import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.DELIVERY_EXPRESS_USER_ADDRESS_IS_EMPTY;
-import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRODUCT_EXPRESS_TEMPLATE_NOT_FOUND;
+import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY;
+import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND;
 
 /**
  * 运费的 {@link TradePriceCalculator} 实现类
@@ -49,7 +47,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
             return;
         }
         if (param.getAddressId() == null) {
-            throw exception(DELIVERY_EXPRESS_USER_ADDRESS_IS_EMPTY);
+            throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY);
         }
         // 1.2 得到收件地址区域
         AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
@@ -57,29 +55,29 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
 
         // 2. 过滤出已选中的商品SKU
         List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);
-        Set<Long> spuIds = convertSet(selectedItem, OrderItem::getSpuId);
-        Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap =
-                deliveryExpressTemplateService.getExpressTemplateMapBySpuIdsAndArea(spuIds, address.getAreaId());
+        Set<Long> deliveryTemplateIds = convertSet(selectedItem, OrderItem::getDeliveryTemplateId);
+        Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap =
+                deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(deliveryTemplateIds, address.getAreaId());
         // 3. 计算配送费用
-        if (CollUtil.isEmpty(spuExpressTemplateMap)) {
-            log.error("[calculate][找不到商品 spuId{} areaId{} 对应的运费模板]", spuIds, address.getAreaId());
-            throw exception(PRODUCT_EXPRESS_TEMPLATE_NOT_FOUND);
+        if (CollUtil.isEmpty(expressTemplateMap)) {
+            log.error("[calculate][找不到商品 templateIds {} areaId{} 对应的运费模板]", deliveryTemplateIds, address.getAreaId());
+            throw exception(PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND);
         }
-        calculateDeliveryPrice(selectedItem, spuExpressTemplateMap, result);
+        calculateDeliveryPrice(selectedItem, expressTemplateMap, result);
     }
 
     private void calculateDeliveryPrice(List<OrderItem> selectedSkus,
-                                        Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap,
+                                        Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap,
                                         TradePriceCalculateRespBO result) {
-        // 按 SPU 来计算商品的运费:一个 spuId 可能对应多条订单商品 SKU
-        Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedSkus, OrderItem::getSpuId);
-        // 依次计算每个 SPU 的快递运费
-        for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) {
-            Long spuId  = entry.getKey();
+        // 按商品运费模板来计算商品的运费:相同的运费模板可能对应多条订单商品 SKU
+        Map<Long, List<OrderItem>> tplIdItemMap = convertMultiMap(selectedSkus, OrderItem::getDeliveryTemplateId);
+        // 依次计算快递运费
+        for (Map.Entry<Long, List<OrderItem>> entry : tplIdItemMap.entrySet()) {
+            Long templateId  = entry.getKey();
             List<OrderItem> orderItems = entry.getValue();
-            SpuDeliveryExpressTemplateRespBO templateBO = spuExpressTemplateMap.get(spuId);
+            DeliveryExpressTemplateRespBO templateBO = expressTemplateMap.get(templateId);
             if (templateBO == null) {
-                log.error("不能计算快递运费。不能找到 spuId : {}. 对应的运费模板配置 Resp BO", spuId);
+                log.error("[calculateDeliveryPrice][不能计算快递运费,找不到 templateId({}) 对应的运费模板配置]", templateId);
                 continue;
             }
             // 总件数, 总金额, 总重量, 总体积
@@ -95,12 +93,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
             }
             // 优先判断是否包邮. 如果包邮不计算快递运费
             if (isExpressFree(templateBO.getChargeMode(), totalCount, totalWeight,
-                            totalVolume, totalPrice, templateBO.getTemplateFree())) {
+                            totalVolume, totalPrice, templateBO.getFree())) {
                 continue;
             }
             // 计算快递运费
             calculateExpressFeeByChargeMode(totalCount, totalWeight, totalVolume,
-                    templateBO.getChargeMode(), templateBO.getTemplateCharge(), orderItems);
+                    templateBO.getChargeMode(), templateBO.getCharge(), orderItems);
 
         }
         TradePriceCalculatorHelper.recountAllPrice(result);
@@ -117,10 +115,10 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
      * @param orderItems SKU 商品项目
      */
     private void calculateExpressFeeByChargeMode(double totalCount, double totalWeight, double totalVolume,
-                                                 int chargeMode, DeliveryExpressTemplateChargeBO templateCharge,
+                                                 int chargeMode, DeliveryExpressTemplateRespBO.Charge templateCharge,
                                                  List<OrderItem> orderItems) {
         if (templateCharge == null) {
-            log.error("计算快递运费时,不能找到对应的快递运费模板费用配置。无法计算以下商品 SKU 项目运费: {}", orderItems);
+            log.error("[calculateExpressFeeByChargeMode][计算快递运费时,找不到 SKU({}) 对应的运费模版]", orderItems);
             return;
         }
         DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
@@ -147,7 +145,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
      * @param templateCharge 快递运费配置
      * @param orderItems     SKU 商品项目
      */
-    private void calculateExpressFee(double total, DeliveryExpressTemplateChargeBO templateCharge, List<OrderItem> orderItems) {
+    private void calculateExpressFee(double total, DeliveryExpressTemplateRespBO.Charge templateCharge, List<OrderItem> orderItems) {
         int deliveryPrice;
         if (total <= templateCharge.getStartCount()) {
             deliveryPrice = templateCharge.getStartPrice();
@@ -176,7 +174,6 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
         for (OrderItem item : orderItems) {
             // 更新快递运费
             item.setDeliveryPrice(dividePrice);
-
             TradePriceCalculatorHelper.recountPayPrice(item);
         }
     }
@@ -192,7 +189,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
      * @param templateFree 包邮配置
      */
     private boolean isExpressFree(Integer chargeMode, int totalCount, double totalWeight,
-                                  double totalVolume, int totalPrice, DeliveryExpressTemplateFreeBO templateFree) {
+                                  double totalVolume, int totalPrice, DeliveryExpressTemplateRespBO.Free templateFree) {
         if (templateFree == null) {
             return false;
         }

+ 2 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java

@@ -56,7 +56,8 @@ public class TradePriceCalculatorHelper {
             orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties())
                     .setWeight(sku.getWeight()).setVolume(sku.getVolume());
             // spu 信息
-            orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId());
+            orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId())
+                    .setDeliveryTemplateId(spu.getDeliveryTemplateId());
             if (orderItem.getPicUrl() == null) {
                 orderItem.setPicUrl(spu.getPicUrl());
             }

+ 18 - 15
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/impl/Kd100ExpressQueryProviderTest.java → yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/Kd100ExpressClientTest.java

@@ -1,8 +1,9 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.impl;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
 
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
-import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kd100.Kd100ExpressClient;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -18,39 +19,41 @@ import javax.annotation.Resource;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+// TODO @jason:可以参考 AliyunSmsClientTest 写,纯 mockito,无需启动 spring 容器
 /**
  * @author jason
  */
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = Kd100ExpressQueryProviderTest.Application.class)
-@ActiveProfiles("trade-delivery-query") // 设置使用 trade-delivery-query 配置文件
-public class Kd100ExpressQueryProviderTest {
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = Kd100ExpressClientTest.Application.class)
+@ActiveProfiles("unit-test") // 设置使用 trade-delivery-query 配置文件
+public class Kd100ExpressClientTest {
+
     @Resource
     private RestTemplateBuilder builder;
     @Resource
-    private TradeExpressQueryProperties expressQueryProperties;
+    private TradeExpressProperties expressQueryProperties;
 
-    private Kd100ExpressQueryProvider kd100ExpressQueryProvider;
+    private Kd100ExpressClient kd100ExpressClient;
 
     @BeforeEach
     public void init(){
-        kd100ExpressQueryProvider = new Kd100ExpressQueryProvider(builder.build(),expressQueryProperties.getKd100());
+        kd100ExpressClient = new Kd100ExpressClient(builder.build(),expressQueryProperties.getKd100());
     }
     @Test
     @Disabled("需要 授权 key. 暂时忽略")
     void testRealTimeQueryExpressFailed() {
         ServiceException t =  assertThrows(ServiceException.class, () -> {
-            ExpressQueryReqDTO reqDTO = new ExpressQueryReqDTO();
-            reqDTO.setExpressCompanyCode("yto");
+            ExpressTrackQueryReqDTO reqDTO = new ExpressTrackQueryReqDTO();
+            reqDTO.setExpressCode("yto");
             reqDTO.setLogisticsNo("YT9383342193097");
-            kd100ExpressQueryProvider.realTimeQueryExpress(reqDTO);
+            kd100ExpressClient.getExpressTrackList(reqDTO);
         });
-        assertEquals(1011003007, t.getCode());
+        assertEquals(1011003005, t.getCode());
     }
 
     @Import({
             RestTemplateAutoConfiguration.class
     })
-    @EnableConfigurationProperties(TradeExpressQueryProperties.class)
+    @EnableConfigurationProperties(TradeExpressProperties.class)
     public static class Application {
     }
-}
+}

+ 18 - 14
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/impl/KdNiaoExpressQueryProviderTest.java → yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/KdNiaoExpressClientTest.java

@@ -1,8 +1,9 @@
-package cn.iocoder.yudao.module.trade.framework.delivery.core.impl;
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
 
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
-import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressQueryProperties;
-import cn.iocoder.yudao.module.trade.framework.delivery.core.dto.ExpressQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kdniao.KdNiaoExpressClient;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -17,39 +18,42 @@ import javax.annotation.Resource;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
-// TODO @芋艿:单测最后 review
+// TODO @jason:可以参考 AliyunSmsClientTest 写,纯 mockito,无需启动 spring 容器
 /**
+ * {@link KdNiaoExpressClient} 的单元测试
+ *
  * @author jason
  */
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = KdNiaoExpressQueryProviderTest.Application.class)
-@ActiveProfiles("trade-delivery-query") // 设置使用 trade-delivery-query 配置文件 TODO @jason:可以直接写到 application-unit-test.yaml 配置文件里
-public class KdNiaoExpressQueryProviderTest {
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = KdNiaoExpressClientTest.Application.class)
+@ActiveProfiles("unit-test")
+public class KdNiaoExpressClientTest {
+
     @Resource
     private RestTemplateBuilder builder;
     @Resource
-    private TradeExpressQueryProperties expressQueryProperties;
+    private TradeExpressProperties expressQueryProperties;
 
-    private KdNiaoExpressQueryProvider kdNiaoExpressQueryProvider;
+    private KdNiaoExpressClient kdNiaoExpressClient;
 
     @BeforeEach
     public void init(){
-        kdNiaoExpressQueryProvider = new KdNiaoExpressQueryProvider(builder.build(),expressQueryProperties.getKdNiao());
+        kdNiaoExpressClient = new KdNiaoExpressClient(builder.build(),expressQueryProperties.getKdNiao());
     }
     @Test
     @Disabled("需要 授权 key. 暂时忽略")
     void testRealTimeQueryExpressFailed() {
         assertThrows(ServiceException.class,() ->{
-            ExpressQueryReqDTO reqDTO = new ExpressQueryReqDTO();
-            reqDTO.setExpressCompanyCode("yy");
+            ExpressTrackQueryReqDTO reqDTO = new ExpressTrackQueryReqDTO();
+            reqDTO.setExpressCode("yy");
             reqDTO.setLogisticsNo("YT9383342193097");
-            kdNiaoExpressQueryProvider.realTimeQueryExpress(reqDTO);
+            kdNiaoExpressClient.getExpressTrackList(reqDTO);
         });
     }
 
     @Import({
             RestTemplateAutoConfiguration.class
     })
-    @EnableConfigurationProperties(TradeExpressQueryProperties.class)
+    @EnableConfigurationProperties(TradeExpressProperties.class)
     public static class Application {
     }
 }

+ 53 - 0
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/NoProvideExpressClientTest.java

@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
+
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.module.trade.framework.delivery.config.ExpressClientConfig;
+import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
+import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+// TODO @jason:可以参考 AliyunSmsClientTest 写,纯 mockito,无需启动 spring 容器
+/**
+ * @author jason
+ */
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = NoProvideExpressClientTest.Application.class)
+@ActiveProfiles("unit-test") // 设置使用 trade-delivery-query 配置文件
+@Import({ExpressClientConfig.class})
+public class NoProvideExpressClientTest {
+
+    @Resource
+    private ExpressClient expressClient;
+
+    @Test
+    void getExpressTrackList() {
+        ServiceException t =  assertThrows(ServiceException.class, () -> {
+            expressClient.getExpressTrackList(null);
+        });
+        assertEquals(1011003006, t.getCode());
+    }
+
+    @Import({
+            RestTemplateAutoConfiguration.class,
+    })
+    @EnableConfigurationProperties(TradeExpressProperties.class)
+    public static class Application {
+
+        @Bean
+        private RestTemplate restTemplate(RestTemplateBuilder builder) {
+            return builder.build();
+        }
+    }
+}

+ 46 - 55
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculatorTest.java

@@ -1,12 +1,13 @@
 package cn.iocoder.yudao.module.trade.service.price.calculator;
 
+import cn.hutool.core.map.MapUtil;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import cn.iocoder.yudao.module.member.api.address.AddressApi;
 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
+import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
+import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
-import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
+import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
 import org.junit.jupiter.api.BeforeEach;
@@ -16,20 +17,17 @@ import org.mockito.InjectMocks;
 import org.mockito.Mock;
 
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
 
 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
-import static cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum.PIECE;
-import static cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum.EXPRESS;
 import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
-import static org.assertj.core.api.Assertions.assertThat;
-
 /**
+ * {@link TradeDeliveryPriceCalculator} 的单元测试
+ *
  * @author jason
  */
 public class TradeDeliveryPriceCalculatorTest  extends BaseMockitoUnitTest {
@@ -43,49 +41,52 @@ public class TradeDeliveryPriceCalculatorTest  extends BaseMockitoUnitTest {
 
     private TradePriceCalculateReqBO reqBO;
     private TradePriceCalculateRespBO resultBO;
-    private AddressRespDTO addressResp;
-    private DeliveryExpressTemplateChargeBO chargeBO;
-    private DeliveryExpressTemplateFreeBO freeBO;
-    private SpuDeliveryExpressTemplateRespBO spuTemplateRespBO;
+
+    private DeliveryExpressTemplateRespBO templateRespBO;
+    private DeliveryExpressTemplateRespBO.Charge chargeBO;
+    private DeliveryExpressTemplateRespBO.Free freeBO;
 
     @BeforeEach
     public void init(){
         // 准备参数
         reqBO = new TradePriceCalculateReqBO()
-                .setDeliveryType(EXPRESS.getMode())
+                .setDeliveryType(DeliveryTypeEnum.EXPRESS.getMode())
                 .setAddressId(10L)
                 .setUserId(1L)
                 .setItems(asList(
                         new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true),
                         new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(10).setSelected(true),
-                        new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(false)
+                        new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(false) // 未选中
                 ));
         resultBO = new TradePriceCalculateRespBO()
                 .setPrice(new TradePriceCalculateRespBO.Price())
                 .setPromotions(new ArrayList<>())
                 .setItems(asList(
-                        new TradePriceCalculateRespBO.OrderItem().setSpuId(1L).setSkuId(10L).setCount(2).setSelected(true)
+                        new TradePriceCalculateRespBO.OrderItem().setDeliveryTemplateId(1L).setSkuId(10L).setCount(2).setSelected(true)
                                 .setWeight(10d).setVolume(10d).setPrice(100),
-                        new TradePriceCalculateRespBO.OrderItem().setSpuId(1L).setSkuId(20L).setCount(10).setSelected(true)
+                        new TradePriceCalculateRespBO.OrderItem().setDeliveryTemplateId(1L).setSkuId(20L).setCount(10).setSelected(true)
                                 .setWeight(10d).setVolume(10d).setPrice(200),
-                        new TradePriceCalculateRespBO.OrderItem().setSpuId(1L).setSkuId(30L).setCount(1).setSelected(false)
+                        new TradePriceCalculateRespBO.OrderItem().setDeliveryTemplateId(1L).setSkuId(30L).setCount(1).setSelected(false)
                                 .setWeight(10d).setVolume(10d).setPrice(300)
                 ));
         // 保证价格被初始化上
         TradePriceCalculatorHelper.recountPayPrice(resultBO.getItems());
         TradePriceCalculatorHelper.recountAllPrice(resultBO);
+
         // 准备收件地址数据
-        addressResp = randomPojo(AddressRespDTO.class, item -> item.setAreaId(10));
+        AddressRespDTO addressResp = randomPojo(AddressRespDTO.class, item -> item.setAreaId(10));
+        when(addressApi.getAddress(eq(10L), eq(1L))).thenReturn(addressResp);
+
         // 准备运费模板费用配置数据
-        chargeBO = randomPojo(DeliveryExpressTemplateChargeBO.class,
+        chargeBO = randomPojo(DeliveryExpressTemplateRespBO.Charge.class,
                 item -> item.setStartCount(10D).setStartPrice(1000).setExtraCount(10D).setExtraPrice(2000));
-        // 准备运费模板包邮配置数据 订单总件数 < 包邮件数时 12 < 20
-        freeBO = randomPojo(DeliveryExpressTemplateFreeBO.class,
+        // 准备运费模板包邮配置数据订单总件数 < 包邮件数时 12 < 20
+        freeBO = randomPojo(DeliveryExpressTemplateRespBO.Free.class,
                 item -> item.setFreeCount(20).setFreePrice(100));
-        // 准备 SP 运费模板 数据
-        spuTemplateRespBO = randomPojo(SpuDeliveryExpressTemplateRespBO.class,
-                item -> item.setChargeMode(PIECE.getType())
-                        .setTemplateCharge(chargeBO).setTemplateFree(freeBO));
+        // 准备 SP 运费模板数据
+        templateRespBO = randomPojo(DeliveryExpressTemplateRespBO.class,
+                item -> item.setChargeMode(DeliveryExpressChargeModeEnum.PIECE.getType())
+                        .setCharge(chargeBO).setFree(freeBO));
     }
 
     @Test
@@ -94,32 +95,27 @@ public class TradeDeliveryPriceCalculatorTest  extends BaseMockitoUnitTest {
         // SKU 1 : 100 * 2  = 200
         // SKU 2 :200 * 10 = 2000
         // 运费  首件 1000 +  续件 2000 = 3000
-        Map<Long, SpuDeliveryExpressTemplateRespBO> respMap = new HashMap<>();
-        respMap.put(1L, spuTemplateRespBO);
-
         // mock 方法
-        when(addressApi.getAddress(eq(10L), eq(1L))).thenReturn(addressResp);
-        when(deliveryExpressTemplateService.getExpressTemplateMapBySpuIdsAndArea(eq(asSet(1L)), eq(10)))
-                .thenReturn(respMap);
+        when(deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(eq(asSet(1L)), eq(10)))
+                .thenReturn(MapUtil.of(1L, templateRespBO));
 
+        // 调用
         calculator.calculate(reqBO, resultBO);
-
+        // 断言
         TradePriceCalculateRespBO.Price price = resultBO.getPrice();
-
         assertThat(price)
                 .extracting("totalPrice","discountPrice","couponPrice","pointPrice","deliveryPrice","payPrice")
                 .containsExactly(2200, 0, 0, 0, 3000,  5200);
-        // 断言:SKU
         assertThat(resultBO.getItems()).hasSize(3);
-        // SKU1
+        // 断言:SKU1
         assertThat(resultBO.getItems().get(0))
                 .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
                 .containsExactly(100, 2, 0, 0, 0, 1500, 1700);
-        // SKU2
+        // 断言:SKU2
         assertThat(resultBO.getItems().get(1))
                 .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
                 .containsExactly(200, 10, 0, 0, 0, 1500, 3500);
-        // SKU3 未选中
+        // 断言:SKU3 未选中
         assertThat(resultBO.getItems().get(2))
                 .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
                 .containsExactly(300, 1, 0, 0, 0, 0, 300);
@@ -131,38 +127,33 @@ public class TradeDeliveryPriceCalculatorTest  extends BaseMockitoUnitTest {
         // SKU 1 : 100 * 2  = 200
         // SKU 2 :200 * 10 = 2000
         // 运费  0
-        Map<Long, SpuDeliveryExpressTemplateRespBO> respMap = new HashMap<>();
-        respMap.put(1L, spuTemplateRespBO);
-        // 准备运费模板包邮配置数据 包邮 订单总件数 > 包邮件数时 12 > 10
-        freeBO = randomPojo(DeliveryExpressTemplateFreeBO.class,
-                item -> item.setFreeCount(10).setFreePrice(1000));
-        spuTemplateRespBO.setTemplateFree(freeBO);
         // mock 方法
-        when(addressApi.getAddress(eq(10L), eq(1L))).thenReturn(addressResp);
-        when(deliveryExpressTemplateService.getExpressTemplateMapBySpuIdsAndArea(eq(asSet(1L)), eq(10)))
-                .thenReturn(respMap);
+        // 准备运费模板包邮配置数据 包邮 订单总件数 > 包邮件数时 12 > 10
+        templateRespBO.setFree(randomPojo(DeliveryExpressTemplateRespBO.Free.class,
+                item -> item.setFreeCount(10).setFreePrice(1000)));
+        when(deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(eq(asSet(1L)), eq(10)))
+                .thenReturn(MapUtil.of(1L, templateRespBO));
 
+        // 调用
         calculator.calculate(reqBO, resultBO);
-
+        // 断言
         TradePriceCalculateRespBO.Price price = resultBO.getPrice();
-
-        // 断言price
         assertThat(price)
                 .extracting("totalPrice","discountPrice","couponPrice","pointPrice","deliveryPrice","payPrice")
                 .containsExactly(2200, 0, 0, 0, 0,  2200);
-        // 断言:SKU
         assertThat(resultBO.getItems()).hasSize(3);
-        // SKU1
+        // 断言:SKU1
         assertThat(resultBO.getItems().get(0))
                 .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
                 .containsExactly(100, 2, 0, 0, 0, 0, 200);
-        // SKU2
+        // 断言:SKU2
         assertThat(resultBO.getItems().get(1))
                 .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
                 .containsExactly(200, 10, 0, 0, 0, 0, 2000);
-        // SKU3 未选中
+        // 断言:SKU3 未选中
         assertThat(resultBO.getItems().get(2))
                 .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
                 .containsExactly(300, 1, 0, 0, 0, 0, 300);
     }
-}
+
+}

+ 0 - 18
yudao-module-mall/yudao-module-trade-biz/src/test/resources/application-trade-delivery-query.yaml

@@ -1,18 +0,0 @@
-spring:
-  main:
-    lazy-initialization: true # 开启懒加载,加快速度
-    banner-mode: off # 单元测试,禁用 Banner
-
---- #################### 交易快递查询相关配置 ####################
-
-yudao:
-  trade:
-    express:
-      query:
-        express-query-provider: kd_niao
-        kd-niao:
-          api-key: xxx
-          business-id: xxxxxxxx
-        kd100:
-          customer: xxxx
-          key: xxxxx

+ 8 - 0
yudao-module-mall/yudao-module-trade-biz/src/test/resources/application-unit-test.yaml

@@ -51,3 +51,11 @@ yudao:
     order:
       app-id: 1
       merchant-order-id: 1
+    express:
+      kd-niao:
+        api-key: xxxx
+        business-id: xxxxx
+      kd100:
+        customer: xxxxx
+        key: xxxxx
+      client: not_provide

+ 45 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/MemberPointConfigController.java

@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.member.controller.admin.point;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigRespVO;
+import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigSaveReqVO;
+import cn.iocoder.yudao.module.member.convert.point.MemberPointConfigConvert;
+import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
+import cn.iocoder.yudao.module.member.service.point.MemberPointConfigService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 会员积分设置")
+@RestController
+@RequestMapping("/point/config")
+@Validated
+public class MemberPointConfigController {
+
+    @Resource
+    private MemberPointConfigService memberPointConfigService;
+
+    @PutMapping("/update")
+    @Operation(summary = "保存会员积分配置")
+    @PreAuthorize("@ss.hasPermission('member:point-config:save')")
+    public CommonResult<Boolean> updateConfig(@Valid @RequestBody MemberPointConfigSaveReqVO saveReqVO) {
+        memberPointConfigService.saveConfig(saveReqVO);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得会员积分配置")
+    @PreAuthorize("@ss.hasPermission('member:point-config:query')")
+    public CommonResult<MemberPointConfigRespVO> getConfig() {
+        MemberPointConfigDO config = memberPointConfigService.getConfig();
+        return success(MemberPointConfigConvert.INSTANCE.convert(config));
+    }
+
+}

+ 27 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/config/MemberPointConfigBaseVO.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.member.controller.admin.point.vo.config;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 会员积分配置 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class MemberPointConfigBaseVO {
+
+    @Schema(description = "积分抵扣开关", required = true, example = "true")
+    private Boolean tradeDeductEnable;
+
+    @Schema(description = "积分抵扣,单位:分", example = "13506")
+    private BigDecimal tradeDeductUnitPrice;
+
+    @Schema(description = "积分抵扣最大值", example = "32428")
+    private Long tradeDeductMaxPrice;
+
+    @Schema(description = "1 元赠送多少分")
+    private Long tradeGivePoint;
+
+}

+ 13 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/config/MemberPointConfigRespVO.java

@@ -0,0 +1,13 @@
+package cn.iocoder.yudao.module.member.controller.admin.point.vo.config;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - 会员积分配置 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MemberPointConfigRespVO extends MemberPointConfigBaseVO {
+}

+ 13 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/config/MemberPointConfigSaveReqVO.java

@@ -0,0 +1,13 @@
+package cn.iocoder.yudao.module.member.controller.admin.point.vo.config;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "管理后台 - 会员积分配置保存 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class MemberPointConfigSaveReqVO extends MemberPointConfigBaseVO {
+}

+ 1 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/point/vo/recrod/package-info.java

@@ -0,0 +1 @@
+package cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod;

+ 20 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointConfigConvert.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.member.convert.point;
+
+import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigRespVO;
+import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * 会员积分配置 Convert
+ *
+ * @author QingX
+ */
+@Mapper
+public interface MemberPointConfigConvert {
+
+    MemberPointConfigConvert INSTANCE = Mappers.getMapper(MemberPointConfigConvert.class);
+
+    MemberPointConfigRespVO convert(MemberPointConfigDO bean);
+
+}

+ 50 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/point/MemberPointConfigDO.java

@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.member.dal.dataobject.point;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+
+/**
+ * 会员积分配置 DO
+ *
+ * @author QingX
+ */
+@TableName("member_point_config")
+@KeySequence("member_point_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MemberPointConfigDO extends BaseDO {
+
+    /**
+     * 自增主键
+     */
+    @TableId
+    private Integer id;
+    /**
+     * 积分抵扣开关
+     */
+    private Boolean tradeDeductEnable;
+    /**
+     * 积分抵扣,单位:分
+     *
+     * 1 积分抵扣多少分
+     */
+    private BigDecimal tradeDeductUnitPrice;
+    /**
+     * 积分抵扣最大值
+     */
+    private Integer tradeDeductMaxPrice;
+    /**
+     * 1 元赠送多少分
+     */
+    private Integer tradeGivePoint;
+
+}

+ 14 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/point/MemberPointConfigMapper.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.member.dal.mysql.point;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 积分设置 Mapper
+ *
+ * @author QingX
+ */
+@Mapper
+public interface MemberPointConfigMapper extends BaseMapperX<MemberPointConfigDO> {
+}

+ 29 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointConfigService.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.member.service.point;
+
+import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigSaveReqVO;
+import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
+
+import javax.validation.Valid;
+
+/**
+ * 会员积分配置 Service 接口
+ *
+ * @author QingX
+ */
+public interface MemberPointConfigService {
+
+    /**
+     * 保存会员积分配置
+     *
+     * @param saveReqVO 更新信息
+     */
+    void saveConfig(@Valid MemberPointConfigSaveReqVO saveReqVO);
+
+    /**
+     * 获得会员积分配置
+     *
+     * @return 积分配置
+     */
+    MemberPointConfigDO getConfig();
+
+}

+ 33 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointConfigServiceImpl.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.member.service.point;
+
+import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigSaveReqVO;
+import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO;
+import cn.iocoder.yudao.module.member.dal.mysql.point.MemberPointConfigMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+
+/**
+ * 会员积分配置 Service 实现类
+ *
+ * @author QingX
+ */
+@Service
+@Validated
+public class MemberPointConfigServiceImpl implements MemberPointConfigService {
+
+    @Resource
+    private MemberPointConfigMapper memberPointConfigMapper;
+
+    @Override
+    public void saveConfig(MemberPointConfigSaveReqVO saveReqVO) {
+        // TODO qingx:配置存在,则 update;不存在则 insert
+    }
+
+    @Override
+    public MemberPointConfigDO getConfig() {
+        // TODO qingx:直接查询到一条;
+        return null;
+    }
+}

+ 19 - 0
yudao-module-point/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>yudao-module-point</artifactId>
+    <packaging>pom</packaging>
+    <modules>
+        <module>yudao-module-point-api</module>
+    </modules>
+
+
+</project>

+ 28 - 0
yudao-module-point/yudao-module-point-api/pom.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao-module-point</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>yudao-module-point-api</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-common</artifactId>
+        </dependency>
+
+        <!-- 参数校验 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+</project>

+ 27 - 0
yudao-module-point/yudao-module-point-api/src/main/java/cn/iocoder/yudao/module/point/enums/ErrorCodeConstants.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.point.enums;
+
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+
+/**
+ * Pay 错误码 Core 枚举类
+ *
+ * pay 系统,使用 1-007-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+    ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(499, "积分设置不存在");
+
+    ErrorCode CONFIG_EXISTS = new ErrorCode(499, "积分设置已存在,只允配置一条记录");
+
+
+    ErrorCode SIGN_IN_CONFIG_NOT_EXISTS = new ErrorCode(499, "签到天数规则不存在");
+    ErrorCode SIGN_IN_CONFIG_EXISTS = new ErrorCode(499, "签到天数规则已存在");
+
+    ErrorCode RECORD_NOT_EXISTS = new ErrorCode( 499, "用户积分记录不存在");
+
+    ErrorCode SIGN_IN_RECORD_NOT_EXISTS = new ErrorCode(499, "用户签到积分不存在");
+
+
+
+}

+ 57 - 0
yudao-module-point/yudao-module-point-biz/pom.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao-module-point</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>yudao-module-point-biz</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-point-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
+        <!-- 业务组件 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
+        </dependency>
+
+        <!-- Web 相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <!-- DB 相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
+        </dependency>
+
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-test</artifactId>
+        </dependency>
+
+        <!-- 工具类相关 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-excel</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 102 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/PointRecordController.java

@@ -0,0 +1,102 @@
+package cn.iocoder.yudao.module.point.controller.admin.pointrecord;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo.*;
+import cn.iocoder.yudao.module.point.dal.dataobject.pointrecord.PointRecordDO;
+import cn.iocoder.yudao.module.point.convert.pointrecord.PointRecordConvert;
+import cn.iocoder.yudao.module.point.service.pointrecord.PointRecordService;
+
+@Tag(name = "管理后台 - 用户积分记录")
+@RestController
+@RequestMapping("/point/record")
+@Validated
+public class PointRecordController {
+
+    @Resource
+    private PointRecordService recordService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建用户积分记录")
+    @PreAuthorize("@ss.hasPermission('point:record:create')")
+    public CommonResult<Long> createRecord(@Valid @RequestBody PointRecordCreateReqVO createReqVO) {
+        return success(recordService.createRecord(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新用户积分记录")
+    @PreAuthorize("@ss.hasPermission('point:record:update')")
+    public CommonResult<Boolean> updateRecord(@Valid @RequestBody PointRecordUpdateReqVO updateReqVO) {
+        recordService.updateRecord(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除用户积分记录")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('point:record:delete')")
+    public CommonResult<Boolean> deleteRecord(@RequestParam("id") Long id) {
+        recordService.deleteRecord(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得用户积分记录")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('point:record:query')")
+    public CommonResult<PointRecordRespVO> getRecord(@RequestParam("id") Long id) {
+        PointRecordDO record = recordService.getRecord(id);
+        return success(PointRecordConvert.INSTANCE.convert(record));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "获得用户积分记录列表")
+    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
+    @PreAuthorize("@ss.hasPermission('point:record:query')")
+    public CommonResult<List<PointRecordRespVO>> getRecordList(@RequestParam("ids") Collection<Long> ids) {
+        List<PointRecordDO> list = recordService.getRecordList(ids);
+        return success(PointRecordConvert.INSTANCE.convertList(list));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得用户积分记录分页")
+    @PreAuthorize("@ss.hasPermission('point:record:query')")
+    public CommonResult<PageResult<PointRecordRespVO>> getRecordPage(@Valid PointRecordPageReqVO pageVO) {
+        PageResult<PointRecordDO> pageResult = recordService.getRecordPage(pageVO);
+        return success(PointRecordConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出用户积分记录 Excel")
+    @PreAuthorize("@ss.hasPermission('point:record:export')")
+    @OperateLog(type = EXPORT)
+    public void exportRecordExcel(@Valid PointRecordExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<PointRecordDO> list = recordService.getRecordList(exportReqVO);
+        // 导出 Excel
+        List<PointRecordExcelVO> datas = PointRecordConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "用户积分记录.xls", "数据", PointRecordExcelVO.class, datas);
+    }
+
+}

+ 67 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordBaseVO.java

@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import javax.validation.constraints.*;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 用户积分记录 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class PointRecordBaseVO {
+
+    @Schema(description = "业务编码", example = "22706")
+    @NotNull(message = "业务编码不能为空")
+    private String bizId;
+
+    @Schema(description = "业务类型", example = "1")
+    @NotNull(message = "业务类型不能为空")
+    private String bizType;
+
+    @Schema(description = "1增加 0扣减", example = "1")
+    @NotNull(message = "操作类型不能为空")
+    private String type;
+
+    @Schema(description = "积分标题")
+    @NotNull(message = "积分标题不能为空")
+    private String title;
+
+    @Schema(description = "积分描述", example = "你猜")
+    private String description;
+
+    @Schema(description = "积分")
+    @NotNull(message = "操作积分不能为空")
+    private Integer point;
+
+    @Schema(description = "变动后的积分", requiredMode = Schema.RequiredMode.REQUIRED)
+//    @NotNull(message = "变动后的积分不能为空")
+    private Integer totalPoint;
+
+    @Schema(description = "状态:1-订单创建,2-冻结期,3-完成,4-失效(订单退款) ", example = "1")
+    @NotNull(message = "积分状态不能为空")
+    private Integer status;
+
+    @Schema(description = "用户id", example = "31169")
+    @NotNull(message = "用户ID不能为空")
+    private Integer userId;
+
+    @Schema(description = "冻结时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @NotNull(message = "冻结时间不能为空")
+    private LocalDateTime freezingTime;
+
+    @Schema(description = "解冻时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @NotNull(message = "解冻时间不能为空")
+    private LocalDateTime thawingTime;
+
+}

+ 14 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 用户积分记录创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class PointRecordCreateReqVO extends PointRecordBaseVO {
+
+}

+ 65 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordExcelVO.java

@@ -0,0 +1,65 @@
+package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+
+/**
+ * 用户积分记录 Excel VO
+ *
+ * @author QingX
+ */
+@Data
+public class PointRecordExcelVO {
+
+    @ExcelProperty("自增主键")
+    private Long id;
+
+    @ExcelProperty("业务编码")
+    private String bizId;
+
+    @ExcelProperty(value = "业务类型", converter = DictConvert.class)
+    @DictFormat("biz_type") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
+    private String bizType;
+
+    @ExcelProperty("1增加 0扣减")
+    private String type;
+
+    @ExcelProperty("积分标题")
+    private String title;
+
+    @ExcelProperty("积分描述")
+    private String description;
+
+    @ExcelProperty("积分")
+    private Integer point;
+
+    @ExcelProperty("变动后的积分")
+    private Integer totalPoint;
+
+    @ExcelProperty(value = "状态:1-订单创建,2-冻结期,3-完成,4-失效(订单退款) ", converter = DictConvert.class)
+    @DictFormat("point_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
+    private Integer status;
+
+    @ExcelProperty("用户id")
+    private Integer userId;
+
+    @ExcelProperty("冻结时间")
+    private LocalDateTime freezingTime;
+
+    @ExcelProperty("解冻时间")
+    private LocalDateTime thawingTime;
+
+    @ExcelProperty("发生时间")
+    private LocalDateTime createTime;
+
+}

+ 27 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordExportReqVO.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - 用户积分记录 Excel 导出 Request VO,参数和 PointRecordPageReqVO 是一致的")
+@Data
+public class PointRecordExportReqVO {
+
+    @Schema(description = "业务编码", example = "22706")
+    private String bizId;
+
+    @Schema(description = "业务类型", example = "1")
+    private String bizType;
+
+    @Schema(description = "1增加 0扣减", example = "1")
+    private String type;
+
+    @Schema(description = "积分标题")
+    private String title;
+
+    @Schema(description = "状态:1-订单创建,2-冻结期,3-完成,4-失效(订单退款) ", example = "1")
+    private Integer status;
+
+}

+ 29 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordPageReqVO.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - 用户积分记录分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class PointRecordPageReqVO extends PageParam {
+
+    @Schema(description = "业务编码", example = "22706")
+    private String bizId;
+
+    @Schema(description = "业务类型", example = "1")
+    private String bizType;
+
+    @Schema(description = "1增加 0扣减", example = "1")
+    private String type;
+
+    @Schema(description = "积分标题")
+    private String title;
+
+    @Schema(description = "状态:1-订单创建,2-冻结期,3-完成,4-失效(订单退款) ", example = "1")
+    private Integer status;
+
+}

+ 19 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 用户积分记录 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class PointRecordRespVO extends PointRecordBaseVO {
+
+    @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "31457")
+    private Long id;
+
+    @Schema(description = "发生时间")
+    private LocalDateTime createTime;
+
+}

+ 18 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/pointrecord/vo/PointRecordUpdateReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 用户积分记录更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class PointRecordUpdateReqVO extends PointRecordBaseVO {
+
+    @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "31457")
+    @NotNull(message = "自增主键不能为空")
+    private Long id;
+
+}

+ 102 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/SignInConfigController.java

@@ -0,0 +1,102 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinconfig;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo.*;
+import cn.iocoder.yudao.module.point.dal.dataobject.signinconfig.SignInConfigDO;
+import cn.iocoder.yudao.module.point.convert.signinconfig.SignInConfigConvert;
+import cn.iocoder.yudao.module.point.service.signinconfig.SignInConfigService;
+
+@Tag(name = "管理后台 - 积分签到规则")
+@RestController
+@RequestMapping("/point/sign-in-config")
+@Validated
+public class SignInConfigController {
+
+    @Resource
+    private SignInConfigService signInConfigService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建积分签到规则")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-config:create')")
+    public CommonResult<Integer> createSignInConfig(@Valid @RequestBody SignInConfigCreateReqVO createReqVO) {
+        return success(signInConfigService.createSignInConfig(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新积分签到规则")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-config:update')")
+    public CommonResult<Boolean> updateSignInConfig(@Valid @RequestBody SignInConfigUpdateReqVO updateReqVO) {
+        signInConfigService.updateSignInConfig(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除积分签到规则")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('point:sign-in-config:delete')")
+    public CommonResult<Boolean> deleteSignInConfig(@RequestParam("id") Integer id) {
+        signInConfigService.deleteSignInConfig(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得积分签到规则")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-config:query')")
+    public CommonResult<SignInConfigRespVO> getSignInConfig(@RequestParam("id") Integer id) {
+        SignInConfigDO signInConfig = signInConfigService.getSignInConfig(id);
+        return success(SignInConfigConvert.INSTANCE.convert(signInConfig));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "获得积分签到规则列表")
+    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-config:query')")
+    public CommonResult<List<SignInConfigRespVO>> getSignInConfigList(@RequestParam("ids") Collection<Integer> ids) {
+        List<SignInConfigDO> list = signInConfigService.getSignInConfigList(ids);
+        return success(SignInConfigConvert.INSTANCE.convertList(list));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得积分签到规则分页")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-config:query')")
+    public CommonResult<PageResult<SignInConfigRespVO>> getSignInConfigPage(@Valid SignInConfigPageReqVO pageVO) {
+        PageResult<SignInConfigDO> pageResult = signInConfigService.getSignInConfigPage(pageVO);
+        return success(SignInConfigConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出积分签到规则 Excel")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-config:export')")
+    @OperateLog(type = EXPORT)
+    public void exportSignInConfigExcel(@Valid SignInConfigExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<SignInConfigDO> list = signInConfigService.getSignInConfigList(exportReqVO);
+        // 导出 Excel
+        List<SignInConfigExcelVO> datas = SignInConfigConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "积分签到规则.xls", "数据", SignInConfigExcelVO.class, datas);
+    }
+
+}

+ 23 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigBaseVO.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import javax.validation.constraints.*;
+
+/**
+ * 积分签到规则 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class SignInConfigBaseVO {
+
+    @Schema(description = "签到第x天", example = "7")
+    private Integer day;
+
+    @Schema(description = "签到天数对应分数", example = "10")
+    private Integer point;
+
+}

+ 14 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 积分签到规则创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SignInConfigCreateReqVO extends SignInConfigBaseVO {
+
+}

+ 28 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigExcelVO.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+
+/**
+ * 积分签到规则 Excel VO
+ *
+ * @author QingX
+ */
+@Data
+public class SignInConfigExcelVO {
+
+    @ExcelProperty("签到第x天")
+    private Integer day;
+
+    @ExcelProperty("签到天数对应分数")
+    private Integer point;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 15 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigExportReqVO.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - 积分签到规则 Excel 导出 Request VO,参数和 SignInConfigPageReqVO 是一致的")
+@Data
+public class SignInConfigExportReqVO {
+
+    @Schema(description = "签到第x天", example = "7")
+    private Integer day;
+
+}

+ 17 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigPageReqVO.java

@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - 积分签到规则分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SignInConfigPageReqVO extends PageParam {
+
+    @Schema(description = "签到第x天", example = "7")
+    private Integer day;
+
+}

+ 19 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 积分签到规则 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SignInConfigRespVO extends SignInConfigBaseVO {
+
+    @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "20937")
+    private Integer id;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+}

+ 18 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinconfig/vo/SignInConfigUpdateReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 积分签到规则更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SignInConfigUpdateReqVO extends SignInConfigBaseVO {
+
+    @Schema(description = "规则自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "13653")
+    @NotNull(message = "规则自增主键不能为空")
+    private Integer id;
+
+}

+ 102 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/SignInRecordController.java

@@ -0,0 +1,102 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinrecord;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo.*;
+import cn.iocoder.yudao.module.point.dal.dataobject.signinrecord.SignInRecordDO;
+import cn.iocoder.yudao.module.point.convert.signinrecord.SignInRecordConvert;
+import cn.iocoder.yudao.module.point.service.signinrecord.SignInRecordService;
+
+@Tag(name = "管理后台 - 用户签到积分")
+@RestController
+@RequestMapping("/point/sign-in-record")
+@Validated
+public class SignInRecordController {
+
+    @Resource
+    private SignInRecordService signInRecordService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建用户签到积分")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-record:create')")
+    public CommonResult<Long> createSignInRecord(@Valid @RequestBody SignInRecordCreateReqVO createReqVO) {
+        return success(signInRecordService.createSignInRecord(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新用户签到积分")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-record:update')")
+    public CommonResult<Boolean> updateSignInRecord(@Valid @RequestBody SignInRecordUpdateReqVO updateReqVO) {
+        signInRecordService.updateSignInRecord(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除用户签到积分")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('point:sign-in-record:delete')")
+    public CommonResult<Boolean> deleteSignInRecord(@RequestParam("id") Long id) {
+        signInRecordService.deleteSignInRecord(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得用户签到积分")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-record:query')")
+    public CommonResult<SignInRecordRespVO> getSignInRecord(@RequestParam("id") Long id) {
+        SignInRecordDO signInRecord = signInRecordService.getSignInRecord(id);
+        return success(SignInRecordConvert.INSTANCE.convert(signInRecord));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "获得用户签到积分列表")
+    @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-record:query')")
+    public CommonResult<List<SignInRecordRespVO>> getSignInRecordList(@RequestParam("ids") Collection<Long> ids) {
+        List<SignInRecordDO> list = signInRecordService.getSignInRecordList(ids);
+        return success(SignInRecordConvert.INSTANCE.convertList(list));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得用户签到积分分页")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-record:query')")
+    public CommonResult<PageResult<SignInRecordRespVO>> getSignInRecordPage(@Valid SignInRecordPageReqVO pageVO) {
+        PageResult<SignInRecordDO> pageResult = signInRecordService.getSignInRecordPage(pageVO);
+        return success(SignInRecordConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出用户签到积分 Excel")
+    @PreAuthorize("@ss.hasPermission('point:sign-in-record:export')")
+    @OperateLog(type = EXPORT)
+    public void exportSignInRecordExcel(@Valid SignInRecordExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<SignInRecordDO> list = signInRecordService.getSignInRecordList(exportReqVO);
+        // 导出 Excel
+        List<SignInRecordExcelVO> datas = SignInRecordConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "用户签到积分.xls", "数据", SignInRecordExcelVO.class, datas);
+    }
+
+}

+ 26 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordBaseVO.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import javax.validation.constraints.*;
+
+/**
+ * 用户签到积分 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class SignInRecordBaseVO {
+
+    @Schema(description = "签到用户", example = "6507")
+    private Integer userId;
+
+    @Schema(description = "第几天签到")
+    private Integer day;
+
+    @Schema(description = "签到的分数")
+    private Integer point;
+
+}

+ 14 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 用户签到积分创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SignInRecordCreateReqVO extends SignInRecordBaseVO {
+
+}

+ 34 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordExcelVO.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+
+/**
+ * 用户签到积分 Excel VO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class SignInRecordExcelVO {
+
+    @ExcelProperty("签到自增id")
+    private Long id;
+
+    @ExcelProperty("签到用户")
+    private Integer userId;
+
+    @ExcelProperty("第几天签到")
+    private Integer day;
+
+    @ExcelProperty("签到的分数")
+    private Integer point;
+
+    @ExcelProperty("签到时间")
+    private LocalDateTime createTime;
+
+}

+ 26 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordExportReqVO.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import java.time.LocalDateTime;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 用户签到积分 Excel 导出 Request VO,参数和 SignInRecordPageReqVO 是一致的")
+@Data
+public class SignInRecordExportReqVO {
+
+    @Schema(description = "签到用户", example = "6507")
+    private Integer userId;
+
+    @Schema(description = "第几天签到")
+    private Integer day;
+
+    @Schema(description = "签到时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 28 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordPageReqVO.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 用户签到积分分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SignInRecordPageReqVO extends PageParam {
+
+    @Schema(description = "签到用户", example = "6507")
+    private Integer userId;
+
+    @Schema(description = "第几天签到")
+    private Integer day;
+
+    @Schema(description = "签到时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 19 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 用户签到积分 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SignInRecordRespVO extends SignInRecordBaseVO {
+
+    @Schema(description = "签到自增id", requiredMode = Schema.RequiredMode.REQUIRED, example = "11903")
+    private Long id;
+
+    @Schema(description = "签到时间")
+    private LocalDateTime createTime;
+
+}

+ 18 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/controller/admin/signinrecord/vo/SignInRecordUpdateReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 用户签到积分更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SignInRecordUpdateReqVO extends SignInRecordBaseVO {
+
+    @Schema(description = "签到自增id", requiredMode = Schema.RequiredMode.REQUIRED, example = "11903")
+    @NotNull(message = "签到自增id不能为空")
+    private Long id;
+
+}

+ 34 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/convert/pointrecord/PointRecordConvert.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.point.convert.pointrecord;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+import cn.iocoder.yudao.module.point.controller.admin.pointrecord.vo.*;
+import cn.iocoder.yudao.module.point.dal.dataobject.pointrecord.PointRecordDO;
+
+/**
+ * 用户积分记录 Convert
+ *
+ * @author QingX
+ */
+@Mapper
+public interface PointRecordConvert {
+
+    PointRecordConvert INSTANCE = Mappers.getMapper(PointRecordConvert.class);
+
+    PointRecordDO convert(PointRecordCreateReqVO bean);
+
+    PointRecordDO convert(PointRecordUpdateReqVO bean);
+
+    PointRecordRespVO convert(PointRecordDO bean);
+
+    List<PointRecordRespVO> convertList(List<PointRecordDO> list);
+
+    PageResult<PointRecordRespVO> convertPage(PageResult<PointRecordDO> page);
+
+    List<PointRecordExcelVO> convertList02(List<PointRecordDO> list);
+
+}

+ 34 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/convert/signinconfig/SignInConfigConvert.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.point.convert.signinconfig;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+import cn.iocoder.yudao.module.point.controller.admin.signinconfig.vo.*;
+import cn.iocoder.yudao.module.point.dal.dataobject.signinconfig.SignInConfigDO;
+
+/**
+ * 积分签到规则 Convert
+ *
+ * @author QingX
+ */
+@Mapper
+public interface SignInConfigConvert {
+
+    SignInConfigConvert INSTANCE = Mappers.getMapper(SignInConfigConvert.class);
+
+    SignInConfigDO convert(SignInConfigCreateReqVO bean);
+
+    SignInConfigDO convert(SignInConfigUpdateReqVO bean);
+
+    SignInConfigRespVO convert(SignInConfigDO bean);
+
+    List<SignInConfigRespVO> convertList(List<SignInConfigDO> list);
+
+    PageResult<SignInConfigRespVO> convertPage(PageResult<SignInConfigDO> page);
+
+    List<SignInConfigExcelVO> convertList02(List<SignInConfigDO> list);
+
+}

+ 34 - 0
yudao-module-point/yudao-module-point-biz/src/main/java/cn/iocoder/yudao/module/point/convert/signinrecord/SignInRecordConvert.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.point.convert.signinrecord;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+import cn.iocoder.yudao.module.point.controller.admin.signinrecord.vo.*;
+import cn.iocoder.yudao.module.point.dal.dataobject.signinrecord.SignInRecordDO;
+
+/**
+ * 用户签到积分 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface SignInRecordConvert {
+
+    SignInRecordConvert INSTANCE = Mappers.getMapper(SignInRecordConvert.class);
+
+    SignInRecordDO convert(SignInRecordCreateReqVO bean);
+
+    SignInRecordDO convert(SignInRecordUpdateReqVO bean);
+
+    SignInRecordRespVO convert(SignInRecordDO bean);
+
+    List<SignInRecordRespVO> convertList(List<SignInRecordDO> list);
+
+    PageResult<SignInRecordRespVO> convertPage(PageResult<SignInRecordDO> page);
+
+    List<SignInRecordExcelVO> convertList02(List<SignInRecordDO> list);
+
+}

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません