From 5dc7ab49d5cdf115f3623bfa2091a6fbafe7aa4c Mon Sep 17 00:00:00 2001 From: liwq <122639653@qq.com> Date: Mon, 28 Jul 2025 17:29:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0order=E5=92=8Cpay=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logs/app-server.log.2025-06-04.0.gz | Bin 0 -> 5839 bytes tashow-dependencies/pom.xml | 26 + tashow-feign/pom.xml | 2 + tashow-feign/tashow-pay-api/pom.xml | 44 + .../api/notify/dto/PayOrderNotifyReqDTO.java | 33 + .../api/notify/dto/PayRefundNotifyReqDTO.java | 33 + .../notify/dto/PayTransferNotifyReqDTO.java | 26 + .../cloud/payapi/api/notify/package-info.java | 0 .../cloud/payapi/api/order/PayOrderApi.java | 39 + .../api/order/dto/PayOrderCreateReqDTO.java | 65 + .../payapi/api/order/dto/PayOrderRespDTO.java | 46 + .../cloud/payapi/api/refund/PayRefundApi.java | 36 + .../api/refund/dto/PayRefundCreateReqDTO.java | 57 + .../api/refund/dto/PayRefundRespDTO.java | 43 + .../payapi/api/transfer/PayTransferApi.java | 36 + .../transfer/dto/PayTransferCreateReqDTO.java | 77 ++ .../api/transfer/dto/PayTransferRespDTO.java | 31 + .../cloud/payapi/api/wallet/PayWalletApi.java | 26 + .../wallet/dto/PayWalletAddBalanceReqDTO.java | 49 + .../cloud/payapi/enums/ApiConstants.java | 24 + .../cloud/payapi/enums/DictTypeConstants.java | 18 + .../payapi/enums/ErrorCodeConstants.java | 94 ++ .../enums/MessageTemplateConstants.java | 14 + .../enums/notify/PayNotifyStatusEnum.java | 32 + .../enums/notify/PayNotifyTypeEnum.java | 29 + .../enums/order/PayOrderStatusEnum.java | 74 ++ .../enums/refund/PayRefundStatusEnum.java | 32 + .../enums/transfer/PayTransferStatusEnum.java | 59 + .../enums/transfer/PayTransferTypeEnum.java | 44 + .../enums/wallet/PayWalletBizTypeEnum.java | 0 tashow-feign/tashow-trade-api/pom.xml | 40 + .../tradeapi/api/order/TradeOrderApi.java | 32 + .../api/order/dto/TradeOrderRespDTO.java | 67 + .../cloud/tradeapi/api/package-info.java | 1 + .../cloud/tradeapi/enums/ApiConstants.java | 24 + .../tradeapi/enums/DictTypeConstants.java | 12 + .../tradeapi/enums/ErrorCodeConstants.java | 106 ++ .../enums/MessageTemplateConstants.java | 18 + .../aftersale/AfterSaleOperateTypeEnum.java | 35 + .../enums/aftersale/AfterSaleStatusEnum.java | 95 ++ .../enums/aftersale/AfterSaleTypeEnum.java | 37 + .../enums/aftersale/AfterSaleWayEnum.java | 37 + .../brokerage/BrokerageBindModeEnum.java | 48 + .../BrokerageEnabledConditionEnum.java | 44 + .../brokerage/BrokerageRecordBizTypeEnum.java | 47 + .../brokerage/BrokerageRecordStatusEnum.java | 39 + .../BrokerageWithdrawStatusEnum.java | 42 + .../brokerage/BrokerageWithdrawTypeEnum.java | 51 + .../DeliveryExpressChargeModeEnum.java | 43 + .../enums/delivery/DeliveryTypeEnum.java | 37 + .../enums/notify/TradeNotifyEnums.java | 5 + .../enums/order/TradeOrderCancelTypeEnum.java | 39 + .../TradeOrderItemAfterSaleStatusEnum.java | 49 + .../order/TradeOrderOperateTypeEnum.java | 42 + .../order/TradeOrderRefundStatusEnum.java | 38 + .../enums/order/TradeOrderStatusEnum.java | 116 ++ .../enums/order/TradeOrderTypeEnum.java | 62 + tashow-module/pom.xml | 2 + .../src/main/resources/application-local.yaml | 4 +- .../src/main/resources/application.yaml | 2 +- .../codegen/java/controller/controller.vm | 253 ---- .../codegen/java/controller/vo/listReqVO.vm | 51 - .../codegen/java/controller/vo/pageReqVO.vm | 53 - .../codegen/java/controller/vo/respVO.vm | 57 - .../codegen/java/controller/vo/saveReqVO.vm | 72 -- .../src/main/resources/codegen/java/dal/do.vm | 52 - .../main/resources/codegen/java/dal/do_sub.vm | 49 - .../main/resources/codegen/java/dal/mapper.vm | 82 -- .../resources/codegen/java/dal/mapper.xml.vm | 12 - .../resources/codegen/java/dal/mapper_sub.vm | 57 - .../resources/codegen/java/enums/errorcode.vm | 22 - .../resources/codegen/java/service/service.vm | 147 --- .../codegen/java/service/serviceImpl.vm | 351 ------ .../codegen/java/test/serviceTest.vm | 168 --- .../src/main/resources/codegen/sql/h2.vm | 37 - .../src/main/resources/codegen/sql/sql.vm | 28 - .../main/resources/codegen/vue/api/api.js.vm | 141 --- .../vue/views/components/form_sub_erp.vue.vm | 205 --- .../views/components/form_sub_inner.vue.vm | 2 - .../views/components/form_sub_normal.vue.vm | 347 ------ .../vue/views/components/list_sub_erp.vue.vm | 165 --- .../views/components/list_sub_inner.vue.vm | 4 - .../resources/codegen/vue/views/form.vue.vm | 320 ----- .../resources/codegen/vue/views/index.vue.vm | 340 ----- .../main/resources/codegen/vue3/api/api.ts.vm | 115 -- .../vue3/views/components/form_sub_erp.vue.vm | 204 --- .../views/components/form_sub_inner.vue.vm | 2 - .../views/components/form_sub_normal.vue.vm | 360 ------ .../vue3/views/components/list_sub_erp.vue.vm | 184 --- .../views/components/list_sub_inner.vue.vm | 4 - .../resources/codegen/vue3/views/form.vue.vm | 300 ----- .../resources/codegen/vue3/views/index.vue.vm | 374 ------ .../resources/codegen/vue3_vben/api/api.ts.vm | 32 - .../codegen/vue3_vben/views/data.ts.vm | 260 ---- .../codegen/vue3_vben/views/form.vue.vm | 58 - .../codegen/vue3_vben/views/index.vue.vm | 92 -- tashow-module/tashow-module-pay/Dockerfile | 20 + tashow-module/tashow-module-pay/pom.xml | 97 ++ .../cloud/pay/PayServerApplication.java | 30 + .../cloud/pay/api/order/PayOrderApiImpl.java | 40 + .../pay/api/refund/PayRefundApiImpl.java | 32 + .../pay/api/transfer/PayTransferApiImpl.java | 39 + .../pay/api/wallet/PayWalletApiImpl.java | 39 + .../admin/app/PayAppController.java | 110 ++ .../controller/admin/app/vo/PayAppBaseVO.java | 47 + .../admin/app/vo/PayAppCreateReqVO.java | 14 + .../admin/app/vo/PayAppPageItemRespVO.java | 26 + .../admin/app/vo/PayAppPageReqVO.java | 33 + .../controller/admin/app/vo/PayAppRespVO.java | 25 + .../admin/app/vo/PayAppUpdateReqVO.java | 19 + .../admin/app/vo/PayAppUpdateStatusReqVO.java | 19 + .../admin/channel/PayChannelController.java | 82 ++ .../admin/channel/vo/PayChannelBaseVO.java | 32 + .../channel/vo/PayChannelCreateReqVO.java | 24 + .../admin/channel/vo/PayChannelRespVO.java | 28 + .../channel/vo/PayChannelUpdateReqVO.java | 24 + .../admin/demo/PayDemoOrderController.java | 74 ++ .../admin/demo/PayDemoTransferController.java | 51 + .../vo/order/PayDemoOrderCreateReqVO.java | 15 + .../demo/vo/order/PayDemoOrderRespVO.java | 54 + .../transfer/PayDemoTransferCreateReqVO.java | 67 + .../vo/transfer/PayDemoTransferRespVO.java | 47 + .../admin/notify/PayNotifyController.java | 150 +++ .../admin/notify/vo/PayNotifyTaskBaseVO.java | 45 + .../notify/vo/PayNotifyTaskDetailRespVO.java | 54 + .../notify/vo/PayNotifyTaskPageReqVO.java | 39 + .../admin/notify/vo/PayNotifyTaskRespVO.java | 25 + .../admin/order/PayOrderController.java | 143 +++ .../admin/order/vo/PayOrderBaseVO.java | 90 ++ .../admin/order/vo/PayOrderDetailsRespVO.java | 45 + .../admin/order/vo/PayOrderExcelVO.java | 67 + .../admin/order/vo/PayOrderExportReqVO.java | 37 + .../order/vo/PayOrderPageItemRespVO.java | 25 + .../admin/order/vo/PayOrderPageReqVO.java | 42 + .../admin/order/vo/PayOrderRespVO.java | 22 + .../admin/order/vo/PayOrderSubmitReqVO.java | 33 + .../admin/order/vo/PayOrderSubmitRespVO.java | 18 + .../admin/refund/PayRefundController.java | 98 ++ .../admin/refund/vo/PayRefundBaseVO.java | 78 ++ .../refund/vo/PayRefundDetailsRespVO.java | 40 + .../admin/refund/vo/PayRefundExcelVO.java | 61 + .../admin/refund/vo/PayRefundExportReqVO.java | 40 + .../refund/vo/PayRefundPageItemRespVO.java | 25 + .../admin/refund/vo/PayRefundPageReqVO.java | 45 + .../admin/transfer/PayTransferController.java | 53 + .../transfer/vo/PayTransferCreateReqVO.java | 95 ++ .../transfer/vo/PayTransferCreateRespVO.java | 16 + .../vo/PayTransferPageItemRespVO.java | 62 + .../transfer/vo/PayTransferPageReqVO.java | 48 + .../admin/transfer/vo/PayTransferRespVO.java | 79 ++ .../admin/wallet/PayWalletController.java | 70 ++ .../wallet/PayWalletRechargeController.java | 57 + .../PayWalletRechargePackageController.java | 74 ++ .../PayWalletTransactionController.java | 42 + .../WalletRechargePackageBaseVO.java | 30 + .../WalletRechargePackageCreateReqVO.java | 14 + .../WalletRechargePackagePageReqVO.java | 30 + .../WalletRechargePackageRespVO.java | 22 + .../WalletRechargePackageUpdateReqVO.java | 19 + .../PayWalletTransactionPageReqVO.java | 23 + .../PayWalletTransactionRespVO.java | 35 + .../wallet/vo/wallet/PayWalletBaseVO.java | 38 + .../wallet/vo/wallet/PayWalletPageReqVO.java | 33 + .../wallet/vo/wallet/PayWalletRespVO.java | 22 + .../wallet/PayWalletUpdateBalanceReqVO.java | 19 + .../wallet/vo/wallet/PayWalletUserReqVO.java | 15 + .../app/channel/AppPayChannelController.java | 42 + .../app/order/AppPayOrderController.http | 63 + .../app/order/AppPayOrderController.java | 74 ++ .../app/order/vo/AppPayOrderSubmitReqVO.java | 10 + .../app/order/vo/AppPayOrderSubmitRespVO.java | 11 + .../app/wallet/AppPayWalletController.java | 41 + .../AppPayWalletRechargeController.java | 67 + ...AppPayWalletRechargePackageController.java | 42 + .../AppPayWalletTransactionController.java | 60 + .../recharge/AppPayWalletPackageRespVO.java | 20 + .../AppPayWalletRechargeCreateReqVO.java | 26 + .../AppPayWalletRechargeCreateRespVO.java | 16 + .../recharge/AppPayWalletRechargeRespVO.java | 42 + .../AppPayWalletTransactionPageReqVO.java | 31 + .../AppPayWalletTransactionRespVO.java | 24 + .../AppPayWalletTransactionSummaryRespVO.java | 16 + .../wallet/vo/wallet/AppPayWalletRespVO.java | 19 + .../cloud/pay/controller/package-info.java | 6 + .../cloud/pay/convert/app/PayAppConvert.java | 48 + .../convert/channel/PayChannelConvert.java | 28 + .../pay/convert/demo/PayDemoOrderConvert.java | 26 + .../convert/demo/PayDemoTransferConvert.java | 21 + .../convert/notify/PayNotifyTaskConvert.java | 43 + .../pay/convert/order/PayOrderConvert.java | 76 ++ .../cloud/pay/convert/package-info.java | 6 + .../pay/convert/refund/PayRefundConvert.java | 56 + .../convert/transfer/PayTransferConvert.java | 31 + .../pay/convert/wallet/PayWalletConvert.java | 21 + .../wallet/PayWalletRechargeConvert.java | 43 + .../PayWalletRechargePackageConvert.java | 28 + .../wallet/PayWalletTransactionConvert.java | 19 + ...‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md | 1 + .../pay/dal/dataobject/app/PayAppDO.java | 66 + .../dal/dataobject/channel/PayChannelDO.java | 69 + .../dal/dataobject/demo/PayDemoOrderDO.java | 87 ++ .../dataobject/demo/PayDemoTransferDO.java | 84 ++ .../dal/dataobject/notify/PayNotifyLogDO.java | 51 + .../dataobject/notify/PayNotifyTaskDO.java | 100 ++ .../pay/dal/dataobject/order/PayOrderDO.java | 138 ++ .../dataobject/order/PayOrderExtensionDO.java | 96 ++ .../dal/dataobject/refund/PayRefundDO.java | 160 +++ .../dataobject/transfer/PayTransferDO.java | 158 +++ .../dal/dataobject/wallet/PayWalletDO.java | 59 + .../wallet/PayWalletRechargeDO.java | 116 ++ .../wallet/PayWalletRechargePackageDO.java | 47 + .../wallet/PayWalletTransactionDO.java | 66 + .../cloud/pay/dal/mysql/app/PayAppMapper.java | 26 + .../dal/mysql/channel/PayChannelMapper.java | 28 + .../dal/mysql/demo/PayDemoOrderMapper.java | 28 + .../dal/mysql/demo/PayDemoTransferMapper.java | 17 + .../dal/mysql/notify/PayNotifyLogMapper.java | 16 + .../dal/mysql/notify/PayNotifyTaskMapper.java | 44 + .../mysql/order/PayOrderExtensionMapper.java | 38 + .../pay/dal/mysql/order/PayOrderMapper.java | 62 + .../pay/dal/mysql/refund/PayRefundMapper.java | 78 ++ .../dal/mysql/transfer/PayTransferMapper.java | 53 + .../pay/dal/mysql/wallet/PayWalletMapper.java | 134 ++ .../mysql/wallet/PayWalletRechargeMapper.java | 30 + .../PayWalletRechargePackageMapper.java | 32 + .../wallet/PayWalletTransactionMapper.java | 65 + .../pay/dal/redis/RedisKeyConstants.java | 36 + .../cloud/pay/dal/redis/no/PayNoRedisDAO.java | 40 + .../redis/notify/PayNotifyLockRedisDAO.java | 39 + .../redis/wallet/PayWalletLockRedisDAO.java | 42 + .../job/config/PayJobConfiguration.java | 28 + .../pay/framework/job/core/package-info.java | 4 + .../cloud/pay/framework/package-info.java | 6 + .../pay/config/PayConfiguration.java | 9 + .../framework/pay/config/PayProperties.java | 69 + .../framework/pay/core/WalletPayClient.java | 195 +++ .../rpc/config/RpcConfiguration.java | 10 + .../cloud/pay/framework/rpc/package-info.java | 4 + .../config/SecurityConfiguration.java | 39 + .../framework/security/core/package-info.java | 4 + .../cloud/pay/job/notify/PayNotifyJob.java | 32 + .../pay/job/order/PayOrderExpireJob.java | 33 + .../cloud/pay/job/order/PayOrderSyncJob.java | 46 + .../pay/job/refund/PayRefundSyncJob.java | 33 + .../pay/job/transfer/PayTransferSyncJob.java | 32 + .../com/tashow/cloud/pay/package-info.java | 10 + .../cloud/pay/service/app/PayAppService.java | 115 ++ .../pay/service/app/PayAppServiceImpl.java | 162 +++ .../service/channel/PayChannelService.java | 104 ++ .../channel/PayChannelServiceImpl.java | 170 +++ .../pay/service/demo/PayDemoOrderService.java | 65 + .../service/demo/PayDemoOrderServiceImpl.java | 264 ++++ .../service/demo/PayDemoTransferService.java | 38 + .../demo/PayDemoTransferServiceImpl.java | 99 ++ .../pay/service/notify/PayNotifyService.java | 57 + .../service/notify/PayNotifyServiceImpl.java | 308 +++++ .../pay/service/order/PayOrderService.java | 159 +++ .../service/order/PayOrderServiceImpl.java | 605 +++++++++ .../pay/service/refund/PayRefundService.java | 82 ++ .../service/refund/PayRefundServiceImpl.java | 332 +++++ .../service/transfer/PayTransferService.java | 66 + .../transfer/PayTransferServiceImpl.java | 313 +++++ .../PayWalletRechargePackageService.java | 70 ++ .../PayWalletRechargePackageServiceImpl.java | 112 ++ .../wallet/PayWalletRechargeService.java | 63 + .../wallet/PayWalletRechargeServiceImpl.java | 328 +++++ .../pay/service/wallet/PayWalletService.java | 100 ++ .../service/wallet/PayWalletServiceImpl.java | 233 ++++ .../wallet/PayWalletTransactionService.java | 73 ++ .../PayWalletTransactionServiceImpl.java | 95 ++ .../bo/WalletTransactionCreateReqBO.java | 58 + .../src/main/resources/application-dev.yaml | 113 ++ .../src/main/resources/application-local.yaml | 141 +++ .../src/main/resources/application.yaml | 125 ++ .../src/main/resources/logback-spring.xml | 76 ++ .../pay/service/app/PayAppServiceTest.java | 260 ++++ .../channel/PayChannelServiceTest.java | 337 +++++ .../service/notify/PayNotifyServiceTest.java | 353 ++++++ .../service/order/PayOrderServiceTest.java | 1105 +++++++++++++++++ .../service/refund/PayRefundServiceTest.java | 700 +++++++++++ .../test/resources/application-unit-test.yaml | 53 + .../src/test/resources/logback.xml | 4 + .../src/test/resources/sql/clean.sql | 7 + .../src/test/resources/sql/create_tables.sql | 147 +++ .../src/main/resources/application-local.yaml | 4 +- tashow-module/tashow-module-trade/Dockerfile | 19 + tashow-module/tashow-module-trade/pom.xml | 100 ++ .../cloud/trade/TradeServerApplication.java | 30 + .../trade/api/order/TradeOrderApiImpl.java | 47 + .../tashow/cloud/trade/api/package-info.java | 1 + .../admin/aftersale/AfterSaleController.java | 145 +++ .../aftersale/TradeAfterSaleController.http | 33 + .../admin/aftersale/vo/AfterSaleBaseVO.java | 119 ++ .../aftersale/vo/AfterSaleDetailRespVO.java | 52 + .../aftersale/vo/AfterSaleDisagreeReqVO.java | 21 + .../aftersale/vo/AfterSalePageReqVO.java | 52 + .../aftersale/vo/AfterSaleRefuseReqVO.java | 20 + .../aftersale/vo/AfterSaleRespPageItemVO.java | 35 + .../aftersale/vo/log/AfterSaleLogRespVO.java | 37 + .../admin/base/member/package-info.java | 4 + .../base/member/user/MemberUserRespVO.java | 19 + .../controller/admin/base/package-info.java | 4 + .../ProductPropertyValueDetailRespVO.java | 22 + .../admin/base/system/package-info.java | 4 + .../base/system/user/UserSimpleBaseVO.java | 19 + .../brokerage/BrokerageRecordController.java | 66 + .../brokerage/BrokerageUserController.java | 121 ++ .../BrokerageWithdrawController.java | 96 ++ .../vo/record/BrokerageRecordBaseVO.java | 65 + .../vo/record/BrokerageRecordPageReqVO.java | 36 + .../vo/record/BrokerageRecordRespVO.java | 37 + .../vo/user/BrokerageUserBaseVO.java | 43 + .../BrokerageUserClearBrokerageUserReqVO.java | 17 + .../vo/user/BrokerageUserCreateReqVO.java | 18 + .../vo/user/BrokerageUserPageReqVO.java | 37 + .../vo/user/BrokerageUserRespVO.java | 45 + ...kerageUserUpdateBrokerageEnabledReqVO.java | 21 + ...BrokerageUserUpdateBrokerageUserReqVO.java | 21 + .../vo/withdraw/BrokerageWithdrawBaseVO.java | 68 + .../withdraw/BrokerageWithdrawPageReqVO.java | 47 + .../BrokerageWithdrawRejectReqVO.java | 22 + .../vo/withdraw/BrokerageWithdrawRespVO.java | 25 + .../admin/config/TradeConfigController.java | 53 + .../admin/config/vo/TradeConfigBaseVO.java | 100 ++ .../admin/config/vo/TradeConfigRespVO.java | 20 + .../admin/config/vo/TradeConfigSaveReqVO.java | 14 + .../delivery/DeliveryExpressController.java | 97 ++ .../DeliveryExpressTemplateController.java | 91 ++ .../DeliveryPickUpStoreController.java | 114 ++ .../vo/express/DeliveryExpressBaseVO.java | 34 + .../express/DeliveryExpressCreateReqVO.java | 12 + .../vo/express/DeliveryExpressExcelVO.java | 39 + .../express/DeliveryExpressExportReqVO.java | 28 + .../vo/express/DeliveryExpressPageReqVO.java | 31 + .../vo/express/DeliveryExpressRespVO.java | 22 + .../express/DeliveryExpressSimpleRespVO.java | 24 + .../express/DeliveryExpressUpdateReqVO.java | 20 + .../DeliveryExpressTemplateBaseVO.java | 27 + .../DeliveryExpressTemplateChargeBaseVO.java | 38 + .../DeliveryExpressTemplateCreateReqVO.java | 25 + .../DeliveryExpressTemplateDetailRespVO.java | 25 + .../DeliveryExpressTemplateFreeBaseVO.java | 28 + .../DeliveryExpressTemplatePageReqVO.java | 30 + .../DeliveryExpressTemplateRespVO.java | 22 + .../DeliveryExpressTemplateSimpleRespVO.java | 21 + .../DeliveryExpressTemplateUpdateReqVO.java | 30 + .../vo/pickup/DeliveryPickUpBindReqVO.java | 24 + .../vo/pickup/DeliveryPickUpStoreBaseVO.java | 68 + .../DeliveryPickUpStoreCreateReqVO.java | 14 + .../pickup/DeliveryPickUpStorePageReqVO.java | 39 + .../vo/pickup/DeliveryPickUpStoreRespVO.java | 27 + .../DeliveryPickUpStoreSimpleRespVO.java | 37 + .../DeliveryPickUpStoreUpdateReqVO.java | 19 + .../admin/order/TradeOrderController.http | 14 + .../admin/order/TradeOrderController.java | 170 +++ .../admin/order/vo/TradeOrderBaseVO.java | 151 +++ .../order/vo/TradeOrderDeliveryReqVO.java | 23 + .../order/vo/TradeOrderDetailRespVO.java | 63 + .../admin/order/vo/TradeOrderItemBaseVO.java | 67 + .../order/vo/TradeOrderPageItemRespVO.java | 35 + .../admin/order/vo/TradeOrderPageReqVO.java | 64 + .../admin/order/vo/TradeOrderRemarkReqVO.java | 21 + .../order/vo/TradeOrderSummaryRespVO.java | 22 + .../vo/TradeOrderUpdateAddressReqVO.java | 33 + .../order/vo/TradeOrderUpdatePriceReqVO.java | 20 + .../app/aftersale/AppAfterSaleController.java | 67 + .../aftersale/AppAfterSaleLogController.java | 42 + .../aftersale/vo/AppAfterSaleCreateReqVO.java | 40 + .../vo/AppAfterSaleDeliveryReqVO.java | 24 + .../app/aftersale/vo/AppAfterSaleRespVO.java | 109 ++ .../vo/log/AppAfterSaleLogRespVO.java | 21 + .../controller/app/base/package-info.java | 4 + .../AppProductPropertyValueDetailRespVO.java | 22 + .../app/base/sku/AppProductSkuBaseRespVO.java | 34 + .../app/base/spu/AppProductSpuBaseRespVO.java | 28 + .../AppBrokerageRecordController.java | 51 + .../brokerage/AppBrokerageUserController.java | 136 ++ .../AppBrokerageWithdrawController.java | 47 + .../AppBrokerageProductPriceRespVO.java | 19 + .../record/AppBrokerageRecordPageReqVO.java | 31 + .../vo/record/AppBrokerageRecordRespVO.java | 33 + .../vo/user/AppBrokerageUserBindReqVO.java | 16 + ...AppBrokerageUserChildSummaryPageReqVO.java | 30 + .../AppBrokerageUserChildSummaryRespVO.java | 33 + .../user/AppBrokerageUserMySummaryRespVO.java | 28 + .../AppBrokerageUserRankByPriceRespVO.java | 22 + ...AppBrokerageUserRankByUserCountRespVO.java | 22 + .../user/AppBrokerageUserRankPageReqVO.java | 22 + .../vo/user/AppBrokerageUserRespVO.java | 19 + .../AppBrokerageWithdrawCreateReqVO.java | 75 ++ .../AppBrokerageWithdrawPageReqVO.java | 22 + .../withdraw/AppBrokerageWithdrawRespVO.java | 27 + .../app/cart/AppCartController.http | 42 + .../app/cart/AppCartController.java | 80 ++ .../app/cart/vo/AppCartAddReqVO.java | 21 + .../app/cart/vo/AppCartDetailRespVO.java | 117 ++ .../app/cart/vo/AppCartListRespVO.java | 48 + .../app/cart/vo/AppCartResetReqVO.java | 26 + .../app/cart/vo/AppCartUpdateCountReqVO.java | 22 + .../cart/vo/AppCartUpdateSelectedReqVO.java | 21 + .../app/config/AppTradeConfigController.java | 46 + .../app/config/vo/AppTradeConfigRespVO.java | 44 + .../delivery/AppDeliverExpressController.java | 41 + .../AppDeliverPickUpStoreController.java | 58 + .../vo/config/AppDeliveryConfigRespVO.java | 17 + .../vo/express/AppDeliveryExpressRespVO.java | 16 + .../pickup/AppDeliveryPickUpStoreRespVO.java | 40 + .../app/order/AppTradeOrderController.http | 69 + .../app/order/AppTradeOrderController.java | 204 +++ .../order/vo/AppOrderExpressTrackRespDTO.java | 23 + .../order/vo/AppTradeOrderCreateReqVO.java | 21 + .../order/vo/AppTradeOrderCreateRespVO.java | 16 + .../order/vo/AppTradeOrderDetailRespVO.java | 151 +++ .../order/vo/AppTradeOrderPageItemRespVO.java | 58 + .../app/order/vo/AppTradeOrderPageReqVO.java | 20 + .../vo/AppTradeOrderSettlementReqVO.java | 109 ++ .../vo/AppTradeOrderSettlementRespVO.java | 174 +++ .../vo/AppTradeProductSettlementRespVO.java | 81 ++ .../AppTradeOrderItemCommentCreateReqVO.java | 38 + .../vo/item/AppTradeOrderItemRespVO.java | 61 + .../cloud/trade/controller/package-info.java | 6 + .../convert/aftersale/AfterSaleConvert.java | 88 ++ .../aftersale/AfterSaleLogConvert.java | 15 + .../brokerage/BrokerageRecordConvert.java | 79 ++ .../brokerage/BrokerageUserConvert.java | 99 ++ .../brokerage/BrokerageWithdrawConvert.java | 57 + .../trade/convert/cart/TradeCartConvert.java | 53 + .../convert/config/TradeConfigConvert.java | 25 + .../delivery/DeliveryExpressConvert.java | 34 + .../DeliveryExpressTemplateConvert.java | 94 ++ .../delivery/DeliveryPickUpStoreConvert.java | 55 + .../convert/order/TradeOrderConvert.java | 291 +++++ .../convert/order/TradeOrderLogConvert.java | 15 + .../dal/dataobject/aftersale/AfterSaleDO.java | 203 +++ .../dataobject/aftersale/AfterSaleLogDO.java | 71 ++ .../brokerage/BrokerageRecordDO.java | 97 ++ .../dataobject/brokerage/BrokerageUserDO.java | 62 + .../brokerage/BrokerageWithdrawDO.java | 98 ++ .../trade/dal/dataobject/cart/CartDO.java | 61 + .../dal/dataobject/config/TradeConfigDO.java | 118 ++ .../delivery/DeliveryExpressDO.java | 60 + .../DeliveryExpressTemplateChargeDO.java | 67 + .../delivery/DeliveryExpressTemplateDO.java | 43 + .../DeliveryExpressTemplateFreeDO.java | 57 + .../delivery/DeliveryPickUpStoreDO.java | 98 ++ .../dal/dataobject/order/TradeOrderDO.java | 363 ++++++ .../dataobject/order/TradeOrderItemDO.java | 215 ++++ .../dal/dataobject/order/TradeOrderLogDO.java | 81 ++ .../mysql/aftersale/AfterSaleLogMapper.java | 19 + .../dal/mysql/aftersale/AfterSaleMapper.java | 52 + .../brokerage/BrokerageRecordMapper.java | 112 ++ .../mysql/brokerage/BrokerageUserMapper.java | 167 +++ .../brokerage/BrokerageWithdrawMapper.java | 64 + .../trade/dal/mysql/cart/CartMapper.java | 62 + .../dal/mysql/config/TradeConfigMapper.java | 15 + .../mysql/delivery/DeliveryExpressMapper.java | 48 + .../DeliveryExpressTemplateChargeMapper.java | 33 + .../DeliveryExpressTemplateFreeMapper.java | 31 + .../DeliveryExpressTemplateMapper.java | 26 + .../delivery/DeliveryPickUpStoreMapper.java | 33 + .../dal/mysql/order/TradeOrderItemMapper.java | 56 + .../dal/mysql/order/TradeOrderLogMapper.java | 19 + .../dal/mysql/order/TradeOrderMapper.java | 140 +++ .../trade/dal/redis/RedisKeyConstants.java | 26 + .../trade/dal/redis/no/TradeNoRedisDAO.java | 44 + .../config/AfterSaleLogConfiguration.java | 22 + .../core/annotations/AfterSaleLog.java | 27 + .../core/aop/AfterSaleLogAspect.java | 133 ++ .../core/utils/AfterSaleLogUtils.java | 25 + .../delivery/config/ExpressClientConfig.java | 32 + .../config/TradeExpressProperties.java | 89 ++ .../delivery/core/client/ExpressClient.java | 23 + .../core/client/ExpressClientFactory.java | 24 + .../client/convert/ExpressQueryConvert.java | 33 + .../client/dto/ExpressTrackQueryReqDTO.java | 36 + .../core/client/dto/ExpressTrackRespDTO.java | 25 + .../dto/kd100/Kd100ExpressQueryReqDTO.java | 33 + .../dto/kd100/Kd100ExpressQueryRespDTO.java | 71 ++ .../dto/kdniao/KdNiaoExpressQueryReqDTO.java | 38 + .../dto/kdniao/KdNiaoExpressQueryRespDTO.java | 99 ++ .../client/impl/ExpressClientFactoryImpl.java | 54 + .../client/impl/NoProvideExpressClient.java | 24 + .../client/impl/kd100/Kd100ExpressClient.java | 107 ++ .../impl/kdniao/KdNiaoExpressClient.java | 127 ++ .../core/enums/ExpressClientEnum.java | 28 + .../order/config/TradeOrderConfig.java | 13 + .../order/config/TradeOrderProperties.java | 51 + .../order/core/annotations/TradeOrderLog.java | 31 + .../order/core/aop/TradeOrderLogAspect.java | 136 ++ .../order/core/utils/TradeOrderLogUtils.java | 27 + .../cloud/trade/framework/package-info.java | 6 + .../rpc/config/RpcConfiguration.java | 41 + .../trade/framework/rpc/package-info.java | 4 + .../config/SecurityConfiguration.java | 39 + .../framework/security/core/package-info.java | 4 + .../com/tashow/cloud/trade/package-info.java | 8 + .../aftersale/AfterSaleLogService.java | 34 + .../aftersale/AfterSaleLogServiceImpl.java | 36 + .../service/aftersale/AfterSaleService.java | 127 ++ .../aftersale/AfterSaleServiceImpl.java | 430 +++++++ .../aftersale/bo/AfterSaleLogCreateReqBO.java | 57 + .../brokerage/BrokerageRecordService.java | 158 +++ .../brokerage/BrokerageRecordServiceImpl.java | 370 ++++++ .../brokerage/BrokerageUserService.java | 145 +++ .../brokerage/BrokerageUserServiceImpl.java | 385 ++++++ .../brokerage/BrokerageWithdrawService.java | 91 ++ .../BrokerageWithdrawServiceImpl.java | 250 ++++ .../brokerage/bo/BrokerageAddReqBO.java | 53 + .../bo/BrokerageWithdrawSummaryRespBO.java | 31 + .../bo/UserBrokerageSummaryRespBO.java | 30 + .../cloud/trade/service/cart/CartService.java | 87 ++ .../trade/service/cart/CartServiceImpl.java | 197 +++ .../service/config/TradeConfigService.java | 29 + .../config/TradeConfigServiceImpl.java | 44 + .../delivery/DeliveryExpressService.java | 82 ++ .../delivery/DeliveryExpressServiceImpl.java | 113 ++ .../DeliveryExpressTemplateService.java | 95 ++ .../DeliveryExpressTemplateServiceImpl.java | 219 ++++ .../delivery/DeliveryPickUpStoreService.java | 82 ++ .../DeliveryPickUpStoreServiceImpl.java | 103 ++ .../bo/DeliveryExpressTemplateRespBO.java | 80 ++ .../service/message/TradeMessageService.java | 19 + .../message/TradeMessageServiceImpl.java | 44 + ...adeOrderMessageWhenDeliveryOrderReqBO.java | 32 + .../service/order/TradeOrderLogService.java | 35 + .../order/TradeOrderLogServiceImpl.java | 34 + .../service/order/TradeOrderQueryService.java | 160 +++ .../order/TradeOrderQueryServiceImpl.java | 260 ++++ .../order/TradeOrderUpdateService.java | 221 ++++ .../order/TradeOrderUpdateServiceImpl.java | 991 +++++++++++++++ .../order/bo/TradeOrderLogCreateReqBO.java | 54 + .../handler/TradeBargainOrderHandler.java | 78 ++ .../handler/TradeBrokerageOrderHandler.java | 114 ++ .../handler/TradeCombinationOrderHandler.java | 97 ++ .../handler/TradeCouponOrderHandler.java | 76 ++ .../handler/TradeMemberPointOrderHandler.java | 118 ++ .../order/handler/TradeOrderHandler.java | 78 ++ .../order/handler/TradePointOrderHandler.java | 83 ++ .../handler/TradeProductSkuOrderHandler.java | 46 + .../handler/TradeSeckillOrderHandler.java | 64 + .../service/price/TradePriceService.java | 34 + .../service/price/TradePriceServiceImpl.java | 155 +++ .../price/bo/TradePriceCalculateReqBO.java | 125 ++ .../price/bo/TradePriceCalculateRespBO.java | 405 ++++++ .../TradeBargainActivityPriceCalculator.java | 58 + ...adeCombinationActivityPriceCalculator.java | 54 + .../TradeCouponPriceCalculator.java | 162 +++ .../TradeDeliveryPriceCalculator.java | 240 ++++ .../TradeDiscountActivityPriceCalculator.java | 154 +++ .../TradePointActivityPriceCalculator.java | 94 ++ .../calculator/TradePointGiveCalculator.java | 63 + .../TradePointUsePriceCalculator.java | 117 ++ .../calculator/TradePriceCalculator.java | 40 + .../TradePriceCalculatorHelper.java | 345 +++++ .../TradeRewardActivityPriceCalculator.java | 160 +++ .../TradeSeckillActivityPriceCalculator.java | 72 ++ .../src/main/resources/application-dev.yaml | 116 ++ .../src/main/resources/application-local.yaml | 138 ++ .../src/main/resources/application.yaml | 141 +++ .../src/main/resources/logback-spring.xml | 76 ++ .../mapper/brokerage/BrokerageUserMapper.xml | 41 + .../test/resources/application-unit-test.yaml | 60 + .../src/test/resources/logback.xml | 4 + .../src/test/resources/sql/clean.sql | 7 + .../src/test/resources/sql/create_tables.sql | 235 ++++ 565 files changed, 36818 insertions(+), 5005 deletions(-) create mode 100644 logs/app-server.log.2025-06-04.0.gz create mode 100644 tashow-feign/tashow-pay-api/pom.xml create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayOrderNotifyReqDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayRefundNotifyReqDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayTransferNotifyReqDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/package-info.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/PayOrderApi.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/dto/PayOrderCreateReqDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/dto/PayOrderRespDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/PayRefundApi.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/dto/PayRefundCreateReqDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/dto/PayRefundRespDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/PayTransferApi.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/dto/PayTransferCreateReqDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/dto/PayTransferRespDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/wallet/PayWalletApi.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/wallet/dto/PayWalletAddBalanceReqDTO.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/ApiConstants.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/DictTypeConstants.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/ErrorCodeConstants.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/MessageTemplateConstants.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/notify/PayNotifyStatusEnum.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/notify/PayNotifyTypeEnum.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/order/PayOrderStatusEnum.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/refund/PayRefundStatusEnum.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/transfer/PayTransferStatusEnum.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/transfer/PayTransferTypeEnum.java create mode 100644 tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/wallet/PayWalletBizTypeEnum.java create mode 100644 tashow-feign/tashow-trade-api/pom.xml create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/order/TradeOrderApi.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/order/dto/TradeOrderRespDTO.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/package-info.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/ApiConstants.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/DictTypeConstants.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/ErrorCodeConstants.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/MessageTemplateConstants.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleOperateTypeEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleStatusEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleTypeEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleWayEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageBindModeEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageEnabledConditionEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageRecordBizTypeEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageRecordStatusEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageWithdrawStatusEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageWithdrawTypeEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/delivery/DeliveryExpressChargeModeEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/delivery/DeliveryTypeEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/notify/TradeNotifyEnums.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderCancelTypeEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderItemAfterSaleStatusEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderOperateTypeEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderRefundStatusEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderStatusEnum.java create mode 100644 tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderTypeEnum.java delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/controller.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/listReqVO.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/pageReqVO.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/saveReqVO.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/do.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/do_sub.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper.xml.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper_sub.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/enums/errorcode.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/service/service.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/java/test/serviceTest.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/sql/h2.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/sql/sql.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue/api/api.js.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/form.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/index.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/api/api.ts.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/form.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/index.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/api/api.ts.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/data.ts.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/form.vue.vm delete mode 100644 tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/index.vue.vm create mode 100644 tashow-module/tashow-module-pay/Dockerfile create mode 100644 tashow-module/tashow-module-pay/pom.xml create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/PayServerApplication.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/order/PayOrderApiImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/refund/PayRefundApiImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/transfer/PayTransferApiImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/wallet/PayWalletApiImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/PayAppController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppBaseVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppCreateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppPageItemRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppPageReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppUpdateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppUpdateStatusReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/PayChannelController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelBaseVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelCreateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelUpdateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/PayDemoOrderController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/PayDemoTransferController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/order/PayDemoOrderCreateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/order/PayDemoOrderRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/transfer/PayDemoTransferRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/PayNotifyController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskBaseVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskDetailRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/PayOrderController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderBaseVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderDetailsRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderExcelVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderExportReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderPageItemRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderPageReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderSubmitReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderSubmitRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/PayRefundController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundBaseVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundDetailsRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundExcelVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundExportReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundPageItemRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundPageReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/PayTransferController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferCreateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferCreateRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferPageItemRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferPageReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletRechargeController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletRechargePackageController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletTransactionController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageBaseVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageCreateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackagePageReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageUpdateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/transaction/PayWalletTransactionPageReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/transaction/PayWalletTransactionRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletBaseVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletPageReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletUpdateBalanceReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletUserReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/channel/AppPayChannelController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/AppPayOrderController.http create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/AppPayOrderController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/vo/AppPayOrderSubmitReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/vo/AppPayOrderSubmitRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletRechargeController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletRechargePackageController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletTransactionController.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletPackageRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeCreateReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeCreateRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionPageReqVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionSummaryRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/wallet/AppPayWalletRespVO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/package-info.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/app/PayAppConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/channel/PayChannelConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/demo/PayDemoOrderConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/demo/PayDemoTransferConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/notify/PayNotifyTaskConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/order/PayOrderConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/package-info.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/refund/PayRefundConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/transfer/PayTransferConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletRechargeConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletRechargePackageConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletTransactionConvert.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/ã€ŠèŠ‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/app/PayAppDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/channel/PayChannelDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/demo/PayDemoOrderDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/demo/PayDemoTransferDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/notify/PayNotifyLogDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/notify/PayNotifyTaskDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/order/PayOrderDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/order/PayOrderExtensionDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/refund/PayRefundDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/transfer/PayTransferDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletRechargeDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletRechargePackageDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletTransactionDO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/app/PayAppMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/channel/PayChannelMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/demo/PayDemoOrderMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/demo/PayDemoTransferMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/notify/PayNotifyLogMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/notify/PayNotifyTaskMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/order/PayOrderExtensionMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/order/PayOrderMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/refund/PayRefundMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/transfer/PayTransferMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletRechargeMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletRechargePackageMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletTransactionMapper.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/RedisKeyConstants.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/no/PayNoRedisDAO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/notify/PayNotifyLockRedisDAO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/wallet/PayWalletLockRedisDAO.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/job/config/PayJobConfiguration.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/job/core/package-info.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/package-info.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/config/PayConfiguration.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/config/PayProperties.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/core/WalletPayClient.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/rpc/config/RpcConfiguration.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/rpc/package-info.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/security/config/SecurityConfiguration.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/security/core/package-info.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/notify/PayNotifyJob.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/order/PayOrderExpireJob.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/order/PayOrderSyncJob.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/refund/PayRefundSyncJob.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/transfer/PayTransferSyncJob.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/package-info.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/app/PayAppService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/app/PayAppServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/channel/PayChannelService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/channel/PayChannelServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoOrderService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoOrderServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoTransferService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoTransferServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/notify/PayNotifyService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/notify/PayNotifyServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/order/PayOrderService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/order/PayOrderServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/refund/PayRefundService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/refund/PayRefundServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/transfer/PayTransferService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/transfer/PayTransferServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargePackageService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargePackageServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargeService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargeServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletTransactionService.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletTransactionServiceImpl.java create mode 100644 tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/bo/WalletTransactionCreateReqBO.java create mode 100644 tashow-module/tashow-module-pay/src/main/resources/application-dev.yaml create mode 100644 tashow-module/tashow-module-pay/src/main/resources/application-local.yaml create mode 100644 tashow-module/tashow-module-pay/src/main/resources/application.yaml create mode 100644 tashow-module/tashow-module-pay/src/main/resources/logback-spring.xml create mode 100644 tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/app/PayAppServiceTest.java create mode 100644 tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/channel/PayChannelServiceTest.java create mode 100644 tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/notify/PayNotifyServiceTest.java create mode 100644 tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/order/PayOrderServiceTest.java create mode 100644 tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/refund/PayRefundServiceTest.java create mode 100644 tashow-module/tashow-module-pay/src/test/resources/application-unit-test.yaml create mode 100644 tashow-module/tashow-module-pay/src/test/resources/logback.xml create mode 100644 tashow-module/tashow-module-pay/src/test/resources/sql/clean.sql create mode 100644 tashow-module/tashow-module-pay/src/test/resources/sql/create_tables.sql create mode 100644 tashow-module/tashow-module-trade/Dockerfile create mode 100644 tashow-module/tashow-module-trade/pom.xml create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/TradeServerApplication.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/api/order/TradeOrderApiImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/api/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/AfterSaleController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/TradeAfterSaleController.http create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleDetailRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleDisagreeReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSalePageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleRefuseReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleRespPageItemVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/log/AfterSaleLogRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/member/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/member/user/MemberUserRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/product/property/ProductPropertyValueDetailRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/system/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/system/user/UserSimpleBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageRecordController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageUserController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageWithdrawController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserClearBrokerageUserReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserCreateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageEnabledReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageUserReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRejectReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/TradeConfigController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigSaveReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryExpressController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryExpressTemplateController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryPickUpStoreController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressCreateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressExcelVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressExportReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressSimpleRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressUpdateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateChargeBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateCreateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateFreeBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplatePageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateUpdateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpBindReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreCreateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStorePageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreSimpleRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreUpdateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/TradeOrderController.http create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/TradeOrderController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderItemBaseVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderPageItemRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderRemarkReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderSummaryRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderUpdateAddressReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderUpdatePriceReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/AppAfterSaleController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/AppAfterSaleLogController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleCreateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleDeliveryReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/log/AppAfterSaleLogRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/property/AppProductPropertyValueDetailRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageRecordController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageUserController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageWithdrawController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageRecordPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageRecordRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserBindReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByUserCountRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/AppCartController.http create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/AppCartController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartAddReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartDetailRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartListRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartResetReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartUpdateCountReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartUpdateSelectedReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/config/AppTradeConfigController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/config/vo/AppTradeConfigRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/AppDeliverExpressController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/AppDeliverPickUpStoreController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/config/AppDeliveryConfigRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/express/AppDeliveryExpressRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/AppTradeOrderController.http create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/AppTradeOrderController.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppOrderExpressTrackRespDTO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderCreateRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderDetailRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderPageItemRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderPageReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/item/AppTradeOrderItemCommentCreateReqVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/item/AppTradeOrderItemRespVO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/aftersale/AfterSaleConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/aftersale/AfterSaleLogConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageRecordConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageUserConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageWithdrawConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/cart/TradeCartConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/config/TradeConfigConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryExpressConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryExpressTemplateConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryPickUpStoreConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/order/TradeOrderConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/order/TradeOrderLogConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/aftersale/AfterSaleDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/aftersale/AfterSaleLogDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageRecordDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageUserDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageWithdrawDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/cart/CartDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/config/TradeConfigDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateChargeDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateFreeDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryPickUpStoreDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderItemDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderLogDO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/aftersale/AfterSaleLogMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/aftersale/AfterSaleMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageRecordMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageUserMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/cart/CartMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/config/TradeConfigMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateChargeMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateFreeMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryPickUpStoreMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderItemMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderLogMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderMapper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/redis/RedisKeyConstants.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/redis/no/TradeNoRedisDAO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/config/AfterSaleLogConfiguration.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/annotations/AfterSaleLog.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/aop/AfterSaleLogAspect.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/utils/AfterSaleLogUtils.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/config/ExpressClientConfig.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/config/TradeExpressProperties.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/ExpressClient.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/ExpressClientFactory.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/convert/ExpressQueryConvert.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/ExpressTrackQueryReqDTO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/ExpressTrackRespDTO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryReqDTO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryReqDTO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/ExpressClientFactoryImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/NoProvideExpressClient.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/kd100/Kd100ExpressClient.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/enums/ExpressClientEnum.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/config/TradeOrderConfig.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/config/TradeOrderProperties.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/annotations/TradeOrderLog.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/aop/TradeOrderLogAspect.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/utils/TradeOrderLogUtils.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/rpc/config/RpcConfiguration.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/rpc/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/security/config/SecurityConfiguration.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/security/core/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/package-info.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleLogService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleLogServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/bo/AfterSaleLogCreateReqBO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageRecordService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageRecordServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageUserService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageUserServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageWithdrawService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageWithdrawServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/BrokerageAddReqBO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/BrokerageWithdrawSummaryRespBO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/UserBrokerageSummaryRespBO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/cart/CartService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/cart/CartServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/config/TradeConfigService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/config/TradeConfigServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressTemplateService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryPickUpStoreService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryPickUpStoreServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/bo/DeliveryExpressTemplateRespBO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/TradeMessageService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/TradeMessageServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/bo/TradeOrderMessageWhenDeliveryOrderReqBO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderLogService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderLogServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderQueryService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderQueryServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderUpdateService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderUpdateServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/bo/TradeOrderLogCreateReqBO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeBargainOrderHandler.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeBrokerageOrderHandler.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeCombinationOrderHandler.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeCouponOrderHandler.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeMemberPointOrderHandler.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeOrderHandler.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradePointOrderHandler.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeProductSkuOrderHandler.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeSeckillOrderHandler.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/TradePriceService.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/TradePriceServiceImpl.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/bo/TradePriceCalculateReqBO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/bo/TradePriceCalculateRespBO.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeBargainActivityPriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeCombinationActivityPriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeCouponPriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeDeliveryPriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointActivityPriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointGiveCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointUsePriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePriceCalculatorHelper.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java create mode 100644 tashow-module/tashow-module-trade/src/main/resources/application-dev.yaml create mode 100644 tashow-module/tashow-module-trade/src/main/resources/application-local.yaml create mode 100644 tashow-module/tashow-module-trade/src/main/resources/application.yaml create mode 100644 tashow-module/tashow-module-trade/src/main/resources/logback-spring.xml create mode 100644 tashow-module/tashow-module-trade/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml create mode 100644 tashow-module/tashow-module-trade/src/test/resources/application-unit-test.yaml create mode 100644 tashow-module/tashow-module-trade/src/test/resources/logback.xml create mode 100644 tashow-module/tashow-module-trade/src/test/resources/sql/clean.sql create mode 100644 tashow-module/tashow-module-trade/src/test/resources/sql/create_tables.sql diff --git a/logs/app-server.log.2025-06-04.0.gz b/logs/app-server.log.2025-06-04.0.gz new file mode 100644 index 0000000000000000000000000000000000000000..1e64eb5db5f593a0397e02d3e050e7ccc5a67af0 GIT binary patch literal 5839 zcmV;=7BJ}_iwFP!00000|Lt8{kK9Ifezt)8hr&XDyc=~5@0S*YKpI)L49OBkQjB0M z7;Lh7nkzQhOR^=8-}WhsO_1!D&wBcgV-Nm(7xo*pHHBfZ+)IXt8+B4uT(|(D(8%i4hv3*SVMGVSI+R z9luCKih9#Ue-V5!{=X&zdH-_?^BX3zT>wYEg~;R@)Lcl!i;~4(`#`BZxIEkm>*%L4K zo+n;FQndOrMyKou#1DCg=mE%Qznlhe!n5Gv`uZb=5TASV=sr42ljY*!lP{h=di(s% z_g|j8eEaGh%9g%QvTTSpluu)C_?D^(z)x_7CwK~fKOs|Z8RgGIVCWq_BJAYGafV;J;6AbowQUNgDd>>SM%$=wXtc;cSt@{%PvX$;TvpUr;+)ka$Y( zx%18sQ0Bu?4<8_JlI1BF_;Bh)8F_#r@5l3JfZ$=gj3U^*%pnL;@*)YyL*)VBgKGyQ z_Ro)J#D7mWm4_UAaS*~2BoFg+30DGWb1ywFFAV=g6Aw;V{m#5f9sCWzFYZ@teL?bB z5^QXzPFZivimd?w@X2{~$2;y<=`}I0?FuAa2GY-!5;Bk)3?xnIF_6L-9+QkC8Ak`? zwHxkzM$mcJMz=lAN+&v7i7qjW{6zG+kC$L$C)L zTtzv}pyKUVZPjfD>r-GtHiHJ>Cd>_IX1*84bO+#6H8jsKdP<)FQmV$B!y)iYfN><- zVTG%SmM}a3;fdztha9sY!$t7EroR-KcQY4WwnoMjmnoNzr zG1TeAQ>Qx+8g_~B+eatgT!S#b=+3(o#;YXDU!@6c5G5%KYG`$Fgy^21%&`}R6K~=b zrp2@kw%JyD1mS<-VckQ?6y@h&>*+qHzoU)iILQ%-!m|+GTkKtQMsn5#vEWE~2LNOg zbjgGaeNImxKX`Kp$?@e&(R?+6>-=yI7yKn=ZFYZ|(w9~Q!I%<{_AgwzE;?n{AH{G% z26VNDAr1&+WvE5ysve zd91U8o*l+e%X-09l4@8nTc^PM#~eq=nYRcr?|(`0g`|a_Cub+bPtxEO_3L$%xasBY z-sQo6{N?j!kMG`9I;YID44xm{lT}FNeTa8P(uii2UlSiFbi{fiC?gUeLoJ5EE$F#$ zwMXgMat_CzkSvMd`CU5r-CrU0qyPxeH%V#lR}!;Y?(&5>ulV}~-qOIXZ2cyOq<-WD zA42K~Fm1YFT96R#*qQf%P&+0BLH5i?E1%vkiBB2JYL%0h&%gK{vgOI6*KbbV9=~~e z^2ILVT6&wr-CZ962j>uS1?liH|MmKG*3cCtT@RG5R!?+g^*dxtFPM0d7yFPeY3JwT z;+LYG_k!1N8QkFmeVgpbnwIFE@zPU-$thj{a=YjImZTD4Q5{0C6Dh7QYzer-*^L7R2;HIHZa0pCT zjbH4(kYwILGS74k;Q_6Wn9L_62uOhQWbS)8e#8FxmQ46e95(%iZDNB3p%;Zeg0mU4 zMUv*oa1=*JpU?7qv2|LIhAqVw!sRVIe?~Ce5^ffIkSSAwU+3UxFAZLEDx(WCs0x0g zQ;u}&Kn2Ai($RknjxXY~Fed0C4hk+JKz6osj7}f1OB4<~QWdOX-Pi@s5_A*x;lE>C z8jVlc1fv=734T6$x0>MdtbA@8!w}_DNARFGCR-iz9`+^xex%*@xbVn|PM7Zm08bJK ziLF4!Ytr#(gTw|Yg@!vFh_G}8&9_cC(y*fo4>D{Y;d{CsQDg3W3JKuC@a;YH_T)JW z>jiHJ1sHuBLSoF*bF>Kk_tfEVGVO1sSr&Z=Jw)49Bi^`z-8&-MRYm4}4b%ps&F64o zK1y3g=-6nLOdT0vnL?HGEJ=%~d*Q|OU3@}*vm_a``g6!>@Y`9KQA}g>5)vUTJs{I9 zyc~+#n2l$i-n+*tiFuf@$*cgAc^%wim zG9c`t7fz#Brd7u|oPkmf3C%gC)$QTE8>8tmU&4zNjLN`>j6y83-)jQpLzti6r?n$aL=Q)srsAhWK4Ekk6Q znU8ZY;v@bw{Qb4(zXzXR>Qyc0ZBysIJN@*#fBy7ufA{I{|A@YZgWu+1l%bz~_lHlv z|Cg0{*6P%L5g-K6@1{q4iX{h*N#X-a@Ug-dnZ~H)qK3xiQ6%h6e}n zZV*0Mrh4Wx*i{M{c03JN5y?vZSqV(UC*EA#L=$Vm50Yw4(xO`7t}X7iDq)z33?(WL zypJv@NYMJ)NOd)=S`M_AH60)^Lh{pK=d_rtwgbE__I$(yTM#B&_d_PJr~nFOkaXf8 zL!TFYIw)Q8nR7H-S=e@st=%Mm+Y%F+l}?{RRT215Co)953wn%% zrAoUD5Rv$_W5D$~rES$K+5RZDxXyH;fv?r7C*E+MIYmz7`HBtJVW_*p zDdY|I&oP_RyAr2uYo_xB|kuG3#weq_rC(7uHG)=@~n-mTY#>Q*@dv)%(5n)^b-n;*JWk9 z7YeyXaNXQDN>vfTVK^?X{Tmxry}*vG6W zXS$WM;vke2M?v}Tg?ah9rOYvo%DW1||FVktI!tc82H-Ug z7O1kGkWpDHF;Aw+9Pw9ZQToSF@8HX|p9UbYV)q8OXx^6PT?L>yHuSNsV%w?!R1~9k zK|aGU7(xEZRn=$Nl&kg?!G_!>4;RRF%!5#K5$pVU9z zT>rQZB#~Su1L;;#LIzTkf#miWNK%k5) zPCHnI1o`Tog8b47ySld6wVAgoLH-Kiy3x;j(XK=&1^Kr~kY7^3vD~&PAO-nyUVd}) z@>j&Qtu~1(1^JS$&C<2iJSzqHlH>y*xzlPrd3b_+UkdUMn9ORMWR`+_$-XPu*J^ny z1^JSnH{++Pwfm}RDvryS73lfn2Jy*gcRfp zD8<)47OMWFAb&qMJGxL}Y)FvL?`~qXs&3zL_W=mIeT z*wjR($FwNOF9F$xNcWu-SUlru9a93Ausp<6I1^H_O_EA~&E6C^AZNOfy z)R2Pwi>@g)Jdi}Ni2=Yw}>lO8Y>uO5RdVB_9 zSaN*kD7zI$-AIj3aoMT}uEB}%`!?ZbT6{q7S_0^$g7{Wx_@wf&A>|{BBVESP&y*1| zjv9=k%DO?F#*q}_%P=}LuWbt>Xp~e|qP;SV2ByQO@GITNO~G$khEJhd?RdI76+RfD zIRgIfT!aq*b>-qr|0@AX4gPJ@;8O-vy6<-|p!1OhVQBpgEHJ1}S6-~$@ht4Yu7?$b z{nl;Ay)e8E2R#6rq1M7C*Upd+sdP`T{3$Yr^1h9uurk)gW*E>i#|=$d)(CYUWgg)Y>xV1r`{jI0jQ1{W)i zNGGCPWUv{WJe+ECV3;Mk28wRcx8t)Qq^udHBUM|Ur`r3=(f9n?ctxN=)Wv|GbkB%;7C zkZT9CMg)g$*Q)P2(iU?_POS>Pe`zs8Tm9}=cWAFw7Mo)Qied?UF5`Fe_N*w;!3@uq-(QuZ8gtI!J;Jj5J>K}N%8|wELJ}CJ<(N5 z78nOg=IVFrn9(33rD#zyZwK?t>US<`3~05iT4cDa z%1r9(aJBHM@8)qd`l)XY&lKs~@AgyQ1wcyxU>EXJ)7mOOWoR8;bi&b~#GA|pR~3!u z0oqVmfVf#3+E){S5$@(9Er)A_r3iIGNDZo(I+FWX6&g5{BGmod?CL`A%H~w4<``b$ zT4i;JU3VXb43$GR>LswD=ujExOCL!)xU36Cm^D4$g%XtkvJH^|7b!*URf@`=SoO=x z4L9r>(4sP&jw%wb2Pj58C@c3uD{l3Ir2s`?` zNr}zw3|qYqOYDWKtFK<{1+K#{hLrf~wND1V5JF*$ZPYTR8##V~sittZcRfCbs~h$D zoO=-Dn;Pv<)|^UxXREr^w;-Qk7?2>pbd}YPtM(VjtYNbvcM;*(U}0lAkx^XRFOGDae=1 z+rd2B79Y@RSCC)ea>Yihn-}DlI7Fr=btuT^@ElXDSG+?({yqRZ5#;Ok7C}Cs1y3ug z=!ByoiP;Mq+{3p))!MY7byX!d!cxRi#~_WcU0tLyk%D|7rFiLMq3TZx^7nDG3Q5Hl ziO0qS`TXvB&4OkSf_x6ys5$+1AcA}j+EPUe+QDUA72CRq5v>aHIUrXT8E}z;{5}Qw z#S^ogRvUIV7vvW>U6I*IhbPECEGzdCxsD|Bfs)y6lgv_(FWGk$ z`z)hP_DMm$Zs!CnG^i`&VG{Ufj2DPLh zUsNg1d@R)YE(Q7fx!JKr#_Ssvcqm z>3TuF?U>gI@=e#=+D)QkRjpT53{#NL3=nDQ4M>o$8?~J;1^Fx2Sz|>Tn^23_?C|bV zLcS-qUZnVYV(oBYgFc>g9ZmTPm#uW`xfa27CdgmotCzJ0Ey!OP${revision} + + com.tashow.cloud + tashow-trade-api + ${revision} + + + com.tashow.cloud + tashow-module-trade + ${revision} + + + com.tashow.cloud + tashow-pay-api + ${revision} + + + com.tashow.cloud + tashow-module-pay + ${revision} + + + com.tashow.cloud + tashow-sdk-payment + ${revision} + + diff --git a/tashow-feign/pom.xml b/tashow-feign/pom.xml index feb8d49..51f81e6 100644 --- a/tashow-feign/pom.xml +++ b/tashow-feign/pom.xml @@ -13,6 +13,8 @@ tashow-infra-api tashow-system-api + tashow-trade-api + tashow-pay-api diff --git a/tashow-feign/tashow-pay-api/pom.xml b/tashow-feign/tashow-pay-api/pom.xml new file mode 100644 index 0000000..4474ffc --- /dev/null +++ b/tashow-feign/tashow-pay-api/pom.xml @@ -0,0 +1,44 @@ + + + + com.tashow.cloud + tashow-feign + ${revision} + + 4.0.0 + tashow-pay-api + jar + + ${project.artifactId} + + pay æ¨¡å— API,暴露给其它模å—调用 + + + + + com.tashow.cloud + tashow-common + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + true + + + com.tashow.cloud + tashow-sdk-payment + + + + diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayOrderNotifyReqDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayOrderNotifyReqDTO.java new file mode 100644 index 0000000..7bb6dd3 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayOrderNotifyReqDTO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.payapi.api.notify.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 支付å•的通知 Request DTO + * + * @author èŠ‹é“æºç  + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayOrderNotifyReqDTO { + + /** + * 商户订å•ç¼–å· + */ + @NotEmpty(message = "商户订å•å·ä¸èƒ½ä¸ºç©º") + private String merchantOrderId; + + /** + * 支付订å•ç¼–å· + */ + @NotNull(message = "支付订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long payOrderId; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayRefundNotifyReqDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayRefundNotifyReqDTO.java new file mode 100644 index 0000000..f5c4ed3 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayRefundNotifyReqDTO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.payapi.api.notify.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 退款å•的通知 Request DTO + * + * @author èŠ‹é“æºç  + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayRefundNotifyReqDTO { + + /** + * 商户退款å•ç¼–å· + */ + @NotEmpty(message = "商户退款å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private String merchantOrderId; + + /** + * æ”¯ä»˜é€€æ¬¾ç¼–å· + */ + @NotNull(message = "支付退款编å·ä¸èƒ½ä¸ºç©º") + private Long payRefundId; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayTransferNotifyReqDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayTransferNotifyReqDTO.java new file mode 100644 index 0000000..f9e8baf --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/dto/PayTransferNotifyReqDTO.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.payapi.api.notify.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 转账å•的通知 Request DTO + * + * @author jason + */ +@Data +public class PayTransferNotifyReqDTO { + + /** + * 商户转账å•å· + */ + @NotEmpty(message = "商户转账å•å·ä¸èƒ½ä¸ºç©º") + private String merchantTransferId; + + /** + * 转账订å•ç¼–å· + */ + @NotNull(message = "转账订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long payTransferId; +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/package-info.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/notify/package-info.java new file mode 100644 index 0000000..e69de29 diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/PayOrderApi.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/PayOrderApi.java new file mode 100644 index 0000000..638cb62 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/PayOrderApi.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.payapi.api.order; + +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.payapi.api.order.dto.PayOrderCreateReqDTO; +import com.tashow.cloud.payapi.enums.ApiConstants; +import com.tashow.cloud.sdk.payment.dto.order.PayOrderRespDTO; +import jakarta.annotation.security.PermitAll; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +/** + * RPC æœåŠ¡ - æ”¯ä»˜å• + */ +@FeignClient(name = ApiConstants.NAME) // +public interface PayOrderApi { + + String PREFIX = ApiConstants.PREFIX + "/order"; + + /*创建支付å•*/ + @PostMapping(PREFIX + "/create") + CommonResult createOrder(@Valid @RequestBody PayOrderCreateReqDTO reqDTO); + + /*获得支付å•*/ + @GetMapping(PREFIX + "/get") + @PermitAll + CommonResult getOrder(@RequestParam("id") Long id); + + /** + * 更新支付订å•ä»·æ ¼ + * @param id 支付å•ç¼–å· + * @param payPrice 支付å•ä»·æ ¼ + * @return + */ + @PutMapping(PREFIX + "/update-price") + CommonResult updatePayOrderPrice(@RequestParam("id") Long id ,//支付å•ç¼–å· + @RequestParam("payPrice") Integer payPrice); + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/dto/PayOrderCreateReqDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/dto/PayOrderCreateReqDTO.java new file mode 100644 index 0000000..368720d --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/dto/PayOrderCreateReqDTO.java @@ -0,0 +1,65 @@ +package com.tashow.cloud.payapi.api.order.dto; + +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 支付å•创建 Request DTO + */ +@Data +public class PayOrderCreateReqDTO implements Serializable { + + public static final int SUBJECT_MAX_LENGTH = 32; + + /** + * 应用标识 + */ + @NotNull(message = "应用标识ä¸èƒ½ä¸ºç©º") + private String appKey; + /** + * 用户 IP + */ + @NotEmpty(message = "用户 IP ä¸èƒ½ä¸ºç©º") + private String userIp; + + // ========== 商户相关字段 ========== + + /** + * 商户订å•ç¼–å· + */ + @NotEmpty(message = "商户订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private String merchantOrderId; + /** + * 商哿 ‡é¢˜ + */ + @NotEmpty(message = "商哿 ‡é¢˜ä¸èƒ½ä¸ºç©º") + @Length(max = SUBJECT_MAX_LENGTH, message = "商哿 ‡é¢˜ä¸èƒ½è¶…过 32") + private String subject; + /** + * å•†å“æè¿° + */ + @Length(max = 128, message = "å•†å“æè¿°ä¿¡æ¯é•¿åº¦ä¸èƒ½è¶…过128") + private String body; + + // ========== 订å•相关字段 ========== + + /** + * 支付金é¢ï¼Œå•ä½ï¼šåˆ† + */ + @NotNull(message = "支付金é¢ä¸èƒ½ä¸ºç©º") + @DecimalMin(value = "0", inclusive = false, message = "支付金é¢å¿…须大于零") + private Integer price; + + /** + * 支付过期时间 + */ + @NotNull(message = "支付过期时间ä¸èƒ½ä¸ºç©º") + private LocalDateTime expireTime; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/dto/PayOrderRespDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/dto/PayOrderRespDTO.java new file mode 100644 index 0000000..f77af33 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/order/dto/PayOrderRespDTO.java @@ -0,0 +1,46 @@ +package com.tashow.cloud.payapi.api.order.dto; + +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import lombok.Data; + +/** + * 支付å•ä¿¡æ¯ Response DTO + * + * @author èŠ‹é“æºç  + */ +@Data +public class PayOrderRespDTO { + + /** + * 订å•ç¼–å·ï¼Œæ•°æ®åº“自增 + */ + private Long id; + /** + * 渠é“ç¼–ç  + * + * 枚举 PayChannelEnum + */ + private String channelCode; + + // ========== 商户相关字段 ========== + /** + * 商户订å•ç¼–å· + * 例如说,内部系统 A 的订å•å·ã€‚需è¦ä¿è¯æ¯ä¸ª PayMerchantDO 唯一 + */ + private String merchantOrderId; + + // ========== 订å•相关字段 ========== + /** + * 支付金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer price; + /** + * æ”¯ä»˜çŠ¶æ€ + * + * 枚举 {@link PayOrderStatusEnum} + */ + private Integer status; + + // ========== 渠é“相关字段 ========== + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/PayRefundApi.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/PayRefundApi.java new file mode 100644 index 0000000..e3ed7ea --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/PayRefundApi.java @@ -0,0 +1,36 @@ +package com.tashow.cloud.payapi.api.refund; + +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.payapi.enums.ApiConstants; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * RPC æœåŠ¡ - é€€æ¬¾å• + */ +@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = +public interface PayRefundApi { + + String PREFIX = ApiConstants.PREFIX + "/refund"; + + /** + * åˆ›å»ºé€€æ¬¾å• + * @param reqDTO + * @return + */ + @PostMapping(PREFIX + "/create") + CommonResult createRefund(@Valid @RequestBody PayRefundCreateReqDTO reqDTO); + + /** + * èŽ·å¾—é€€æ¬¾å• + * @param id 退款å•ç¼–å· + * @return + */ + @GetMapping(PREFIX + "/get") + CommonResult getRefund(@RequestParam("id") Long id); + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/dto/PayRefundCreateReqDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/dto/PayRefundCreateReqDTO.java new file mode 100644 index 0000000..9936e56 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/dto/PayRefundCreateReqDTO.java @@ -0,0 +1,57 @@ +package com.tashow.cloud.payapi.api.refund.dto; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +/** + * 退款å•创建 Request DTO + * + * @author èŠ‹é“æºç  + */ +@Data +public class PayRefundCreateReqDTO { + + /** + * 应用标识 + */ + @NotNull(message = "应用标识ä¸èƒ½ä¸ºç©º") + private String appKey; + /** + * 用户 IP + */ + @NotEmpty(message = "用户 IP ä¸èƒ½ä¸ºç©º") + private String userIp; + + // ========== 商户相关字段 ========== + /** + * 商户订å•ç¼–å· + */ + @NotEmpty(message = "商户订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private String merchantOrderId; + + /** + * å•†æˆ·é€€æ¬¾ç¼–å· + */ + @NotEmpty(message = "商户退款编å·ä¸èƒ½ä¸ºç©º") + private String merchantRefundId; + + /** + * 退款æè¿° + */ + @NotEmpty(message = "退款æè¿°ä¸èƒ½ä¸ºç©º") + @Length(max = 128, message = "退款æè¿°é•¿åº¦ä¸èƒ½è¶…过 128") + private String reason; + + // ========== 订å•相关字段 ========== + + /** + * 退款金é¢ï¼Œå•ä½ï¼šåˆ† + */ + @NotNull(message = "退款金é¢ä¸èƒ½ä¸ºç©º") + @Min(value = 1, message = "退款金é¢å¿…须大于零") + private Integer price; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/dto/PayRefundRespDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/dto/PayRefundRespDTO.java new file mode 100644 index 0000000..9d2228c --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/refund/dto/PayRefundRespDTO.java @@ -0,0 +1,43 @@ +package com.tashow.cloud.payapi.api.refund.dto; + +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 退款å•ä¿¡æ¯ Response DTO + * + * @author èŠ‹é“æºç  + */ +@Data +public class PayRefundRespDTO { + + /** + * 退款å•ç¼–å· + */ + private Long id; + + // ========== 退款相关字段 ========== + /** + * é€€æ¬¾çŠ¶æ€ + * + * 枚举 {@link PayRefundStatusEnum} + */ + private Integer status; + /** + * 退款金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer refundPrice; + + // ========== 商户相关字段 ========== + /** + * 商户订å•ç¼–å· + */ + private String merchantOrderId; + /** + * 退款æˆåŠŸæ—¶é—´ + */ + private LocalDateTime successTime; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/PayTransferApi.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/PayTransferApi.java new file mode 100644 index 0000000..01646bf --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/PayTransferApi.java @@ -0,0 +1,36 @@ +package com.tashow.cloud.payapi.api.transfer; + +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.payapi.enums.ApiConstants; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * RPC æœåŠ¡ - è½¬è´¦å• + */ +@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = +public interface PayTransferApi { + + String PREFIX = ApiConstants.PREFIX + "/transfer"; + + /** + * åˆ›å»ºè½¬è´¦å• + * @param reqDTO + * @return + */ + @PostMapping(PREFIX + "/create") + CommonResult createTransfer(@Valid @RequestBody PayTransferCreateReqDTO reqDTO); + + /** + * èŽ·å¾—è½¬è´¦å• + * @param id 转账å•ç¼–å· + * @return + */ + @GetMapping(PREFIX + "/get") + CommonResult getTransfer(@RequestParam("id") Long id); + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/dto/PayTransferCreateReqDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/dto/PayTransferCreateReqDTO.java new file mode 100644 index 0000000..5f2d9db --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/dto/PayTransferCreateReqDTO.java @@ -0,0 +1,77 @@ +package com.tashow.cloud.payapi.api.transfer.dto; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.Map; + +/** + * 转账å•创建 Request DTO + * + * @author jason + */ +@Data +public class PayTransferCreateReqDTO { + + /** + * 应用标识 + */ + @NotNull(message = "应用标识ä¸èƒ½ä¸ºç©º") + private String appKey; + + @NotEmpty(message = "转账渠é“ä¸èƒ½ä¸ºç©º") + private String channelCode; + + /** + * 转账渠é“çš„é¢å¤–傿•° + */ + private Map channelExtras; + + @NotEmpty(message = "用户 IP ä¸èƒ½ä¸ºç©º") + private String userIp; + + /** + * 类型 + */ + @NotNull(message = "转账类型ä¸èƒ½ä¸ºç©º") + @InEnum(PayTransferTypeEnum.class) + private Integer type; + + + /** + * 商户转账å•ç¼–å· + */ + @NotEmpty(message = "商户转账å•ç¼–å·èƒ½ä¸ºç©º") + private String merchantTransferId; + + /** + * 转账金é¢ï¼Œå•ä½ï¼šåˆ† + */ + @Min(value = 1, message = "转账金é¢å¿…须大于零") + @NotNull(message = "转账金é¢ä¸èƒ½ä¸ºç©º") + private Integer price; + + /** + * 转账标题 + */ + @NotEmpty(message = "转账标题ä¸èƒ½ä¸ºç©º") + private String subject; + + /** + * 收款人姓å + */ + @NotBlank(message = "收款人姓åä¸èƒ½ä¸ºç©º", groups = {PayTransferTypeEnum.Alipay.class}) + private String userName; + + @NotBlank(message = "支付å®ç™»å½•å·ä¸èƒ½ä¸ºç©º", groups = {PayTransferTypeEnum.Alipay.class}) + private String alipayLogonId; + + // ========== 微信转账相关字段 ========== + @NotBlank(message = "微信 openId ä¸èƒ½ä¸ºç©º", groups = {PayTransferTypeEnum.WxPay.class}) + private String openid; +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/dto/PayTransferRespDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/dto/PayTransferRespDTO.java new file mode 100644 index 0000000..330fdff --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/transfer/dto/PayTransferRespDTO.java @@ -0,0 +1,31 @@ +package com.tashow.cloud.payapi.api.transfer.dto; + +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; +import lombok.Data; + +@Data +public class PayTransferRespDTO { + + /** + * ç¼–å· + */ + private Long id; + + /** + * 转账å•å· + */ + private String no; + + /** + * 转账金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer price; + + /** + * è½¬è´¦çŠ¶æ€ + * + * 枚举 {@link PayTransferStatusEnum} + */ + private Integer status; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/wallet/PayWalletApi.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/wallet/PayWalletApi.java new file mode 100644 index 0000000..05b685d --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/wallet/PayWalletApi.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.payapi.api.wallet; + +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.payapi.enums.ApiConstants; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +/** + * RPC æœåŠ¡ - 钱包 + */ +@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = +public interface PayWalletApi { + + String PREFIX = ApiConstants.PREFIX + "/wallet"; + + /** + * æ·»åŠ é’±åŒ…ä½™é¢ + * @param reqDTO + * @return + */ + @PostMapping(PREFIX + "/add-balance") + CommonResult addWalletBalance(@Valid @RequestBody PayWalletAddBalanceReqDTO reqDTO); + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/wallet/dto/PayWalletAddBalanceReqDTO.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/wallet/dto/PayWalletAddBalanceReqDTO.java new file mode 100644 index 0000000..d644cba --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/api/wallet/dto/PayWalletAddBalanceReqDTO.java @@ -0,0 +1,49 @@ +package com.tashow.cloud.payapi.api.wallet.dto; + +import com.tashow.cloud.common.enums.UserTypeEnum; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 钱包余é¢å¢žåŠ  Request DTO + * + * @author èŠ‹é“æºç  + */ +@Data +public class PayWalletAddBalanceReqDTO { + + /** + * ç”¨æˆ·ç¼–å· + * + * å…³è” MemberUserDO çš„ id 属性,或者 AdminUserDO çš„ id 属性 + */ + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + /** + * 用户类型 + * + * å…³è” {@link UserTypeEnum} + */ + @NotNull(message = "用户类型ä¸èƒ½ä¸ºç©º") + private Integer userType; + + /** + * å…³è”业务分类 + */ + @NotNull(message = "å…³è”业务分类ä¸èƒ½ä¸ºç©º") + private Integer bizType; + /** + * å…³è”ä¸šåŠ¡ç¼–å· + */ + @NotNull(message = "å…³è”业务编å·ä¸èƒ½ä¸ºç©º") + private String bizId; + + /** + * 交易金é¢ï¼Œå•ä½åˆ† + * + * 正值表示余é¢å¢žåŠ ï¼Œè´Ÿå€¼è¡¨ç¤ºä½™é¢å‡å°‘ + */ + @NotNull(message = "交易金é¢ä¸èƒ½ä¸ºç©º") + private Integer price; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/ApiConstants.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/ApiConstants.java new file mode 100644 index 0000000..2019c34 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/ApiConstants.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.payapi.enums; + + +import com.tashow.cloud.common.enums.RpcConstants; + +/** + * API 相关的枚举 + * + * @author èŠ‹é“æºç  + */ +public class ApiConstants { + + /** + * æœåŠ¡å + * + * 注æ„,需è¦ä¿è¯å’Œ spring.application.name ä¿æŒä¸€è‡´ + */ + public static final String NAME = "pay-server"; + + public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/pay"; + + public static final String VERSION = "1.0.0"; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/DictTypeConstants.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/DictTypeConstants.java new file mode 100644 index 0000000..9c553e7 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/DictTypeConstants.java @@ -0,0 +1,18 @@ +package com.tashow.cloud.payapi.enums; + +/** + * Pay 字典类型的枚举类 + * + * @author èŠ‹é“æºç  + */ +public interface DictTypeConstants { + + String CHANNEL_CODE = "pay_channel_code"; // 支付渠é“ç¼–ç  + + String ORDER_STATUS = "pay_order_status"; // æ”¯ä»˜æ¸ é“ + + String REFUND_STATUS = "pay_order_status"; // é€€æ¬¾çŠ¶æ€ + + String NOTIFY_STATUS = "pay_notify_status"; // å›žè°ƒçŠ¶æ€ + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/ErrorCodeConstants.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/ErrorCodeConstants.java new file mode 100644 index 0000000..8dc4225 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/ErrorCodeConstants.java @@ -0,0 +1,94 @@ +package com.tashow.cloud.payapi.enums; + + +import com.tashow.cloud.common.exception.ErrorCode; + +/** + * Pay é”™è¯¯ç  Core 枚举类 + * + * pay 系统,使用 1-007-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== APP æ¨¡å— 1-007-000-000 ========== + ErrorCode APP_NOT_FOUND = new ErrorCode(1_007_000_000, "App ä¸å­˜åœ¨"); + ErrorCode APP_IS_DISABLE = new ErrorCode(1_007_000_002, "App å·²ç»è¢«ç¦ç”¨"); + ErrorCode APP_EXIST_ORDER_CANT_DELETE = new ErrorCode(1_007_000_003, "支付应用存在支付订å•,无法删除"); + ErrorCode APP_EXIST_REFUND_CANT_DELETE = new ErrorCode(1_007_000_004, "支付应用存在退款订å•,无法删除"); + ErrorCode APP_KEY_EXISTS = new ErrorCode(1_007_000_005, "支付应用标识已ç»å­˜åœ¨"); + + // ========== CHANNEL æ¨¡å— 1-007-001-000 ========== + ErrorCode CHANNEL_NOT_FOUND = new ErrorCode(1_007_001_000, "支付渠é“çš„é…ç½®ä¸å­˜åœ¨"); + ErrorCode CHANNEL_IS_DISABLE = new ErrorCode(1_007_001_001, "支付渠é“å·²ç»ç¦ç”¨"); + ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1_007_001_004, "已存在相åŒçš„æ¸ é“"); + + // ========== ORDER æ¨¡å— 1-007-002-000 ========== + ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1_007_002_000, "支付订å•ä¸å­˜åœ¨"); + ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_002_001, "支付订å•ä¸å¤„于待支付"); + ErrorCode PAY_ORDER_STATUS_IS_SUCCESS = new ErrorCode(1_007_002_002, "订å•已支付,请刷新页é¢"); + ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1_007_002_003, "支付订å•å·²ç»è¿‡æœŸ"); + ErrorCode PAY_ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_002_004, "å‘起支付报错,错误ç ï¼š{},错误æç¤ºï¼š{}"); + ErrorCode PAY_ORDER_REFUND_FAIL_STATUS_ERROR = new ErrorCode(1_007_002_005, "支付订å•退款失败,原因:状æ€ä¸æ˜¯å·²æ”¯ä»˜æˆ–已退款"); + + // ========== ORDER 模å—(拓展å•) 1-007-003-000 ========== + ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1_007_003_000, "支付交易拓展å•ä¸å­˜åœ¨"); + ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_003_001, "支付交易拓展å•ä¸å¤„于待支付"); + ErrorCode PAY_ORDER_EXTENSION_IS_PAID = new ErrorCode(1_007_003_002, "订å•已支付,请等待支付结果"); + + // ========== 支付模å—(退款) 1-007-006-000 ========== + ErrorCode REFUND_PRICE_EXCEED = new ErrorCode(1_007_006_000, "退款金é¢è¶…过订å•å¯é€€æ¬¾é‡‘é¢"); + ErrorCode REFUND_HAS_REFUNDING = new ErrorCode(1_007_006_002, "å·²ç»æœ‰é€€æ¬¾åœ¨å¤„ç†ä¸­"); + ErrorCode REFUND_EXISTS = new ErrorCode(1_007_006_003, "å·²ç»å­˜åœ¨é€€æ¬¾å•"); + ErrorCode REFUND_NOT_FOUND = new ErrorCode(1_007_006_004, "支付退款å•ä¸å­˜åœ¨"); + ErrorCode REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_006_005, "支付退款å•ä¸å¤„于待退款"); + + // ========== é’±åŒ…æ¨¡å— 1-007-007-000 ========== + ErrorCode WALLET_NOT_FOUND = new ErrorCode(1_007_007_000, "用户钱包ä¸å­˜åœ¨"); + ErrorCode WALLET_BALANCE_NOT_ENOUGH = new ErrorCode(1_007_007_001, "钱包余é¢ä¸è¶³"); + ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1_007_007_002, "未找到对应的钱包交易"); + ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1_007_007_003, "å·²ç»å­˜åœ¨é’±åŒ…退款"); + ErrorCode WALLET_FREEZE_PRICE_NOT_ENOUGH = new ErrorCode(1_007_007_004, "钱包冻结余é¢ä¸è¶³"); + + // ========== é’±åŒ…å……å€¼æ¨¡å— 1-007-008-000 ========== + ErrorCode WALLET_RECHARGE_NOT_FOUND = new ErrorCode(1_007_008_000, "钱包充值记录ä¸å­˜åœ¨"); + ErrorCode WALLET_RECHARGE_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1_007_008_001, "钱包充值更新支付状æ€å¤±è´¥ï¼Œé’±åŒ…å……å€¼è®°å½•ä¸æ˜¯ã€æœªæ”¯ä»˜ã€‘状æ€"); + ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1_007_008_002, "钱包充值更新支付状æ€å¤±è´¥ï¼Œæ”¯ä»˜å•ç¼–å·ä¸åŒ¹é…"); + ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1_007_008_003, "钱包充值更新支付状æ€å¤±è´¥ï¼Œæ”¯ä»˜å•状æ€ä¸æ˜¯ã€æ”¯ä»˜æˆåŠŸã€‘çŠ¶æ€"); + ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_PRICE_NOT_MATCH = new ErrorCode(1_007_008_004, "钱包充值更新支付状æ€å¤±è´¥ï¼Œæ”¯ä»˜å•金é¢ä¸åŒ¹é…"); + ErrorCode WALLET_RECHARGE_REFUND_FAIL_NOT_PAID = new ErrorCode(1_007_008_005, "钱包å‘èµ·é€€æ¬¾å¤±è´¥ï¼Œé’±åŒ…å……å€¼è®¢å•æœªæ”¯ä»˜"); + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUNDED = new ErrorCode(1_007_008_006, "钱包å‘起退款失败,钱包充值订å•已退款"); + ErrorCode WALLET_RECHARGE_REFUND_BALANCE_NOT_ENOUGH = new ErrorCode(1_007_008_007, "钱包å‘起退款失败,钱包余é¢ä¸è¶³"); + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1_007_008_008, "钱包退款更新失败,钱包退款å•ç¼–å·ä¸åŒ¹é…"); + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1_007_008_009, "钱包退款更新失败,退款订å•ä¸å­˜åœ¨"); + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1_007_008_010, "钱包退款更新失败,退款å•金é¢ä¸åŒ¹é…"); + ErrorCode WALLET_RECHARGE_PACKAGE_NOT_FOUND = new ErrorCode(1_007_008_011, "钱包充值套é¤ä¸å­˜åœ¨"); + ErrorCode WALLET_RECHARGE_PACKAGE_IS_DISABLE = new ErrorCode(1_007_008_012, "钱包充值套é¤å·²ç¦ç”¨"); + ErrorCode WALLET_RECHARGE_PACKAGE_NAME_EXISTS = new ErrorCode(1_007_008_013, "钱包充值套é¤å称已存在"); + + // ========== è½¬è´¦æ¨¡å— 1-007-009-000 ========== + ErrorCode PAY_TRANSFER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_009_000, "å‘起转账报错,错误ç ï¼š{},错误æç¤ºï¼š{}"); + ErrorCode PAY_TRANSFER_NOT_FOUND = new ErrorCode(1_007_009_001, "转账å•ä¸å­˜åœ¨"); + ErrorCode PAY_SAME_MERCHANT_TRANSFER_TYPE_NOT_MATCH = new ErrorCode(1_007_009_002, "两次相åŒè½¬è´¦è¯·æ±‚的类型ä¸åŒ¹é…"); + ErrorCode PAY_SAME_MERCHANT_TRANSFER_PRICE_NOT_MATCH = new ErrorCode(1_007_009_003, "两次相åŒè½¬è´¦è¯·æ±‚的金é¢ä¸åŒ¹é…"); + ErrorCode PAY_MERCHANT_TRANSFER_EXISTS = new ErrorCode(1_007_009_004, "该笔业务的转账已ç»å‘èµ·,请查询转账订å•相关状æ€"); + ErrorCode PAY_TRANSFER_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_009_005, "转账å•ä¸å¤„于待转账"); + ErrorCode PAY_TRANSFER_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_006, "转账å•ä¸å¤„于待转账或转账中"); + + // ========== ç¤ºä¾‹è®¢å• 1-007-900-000 ========== + ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1_007_900_000, "示例订å•ä¸å­˜åœ¨"); + ErrorCode DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1_007_900_001, "ç¤ºä¾‹è®¢å•æ›´æ–°æ”¯ä»˜çжæ€å¤±è´¥ï¼Œè®¢å•䏿˜¯ã€æœªæ”¯ä»˜ã€‘状æ€"); + ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1_007_900_002, "ç¤ºä¾‹è®¢å•æ›´æ–°æ”¯ä»˜çжæ€å¤±è´¥ï¼Œæ”¯ä»˜å•ç¼–å·ä¸åŒ¹é…"); + ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1_007_900_003, "ç¤ºä¾‹è®¢å•æ›´æ–°æ”¯ä»˜çжæ€å¤±è´¥ï¼Œæ”¯ä»˜å•状æ€ä¸æ˜¯ã€æ”¯ä»˜æˆåŠŸã€‘çŠ¶æ€"); + ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1_007_900_004, "ç¤ºä¾‹è®¢å•æ›´æ–°æ”¯ä»˜çжæ€å¤±è´¥ï¼Œæ”¯ä»˜å•金é¢ä¸åŒ¹é…"); + ErrorCode DEMO_ORDER_REFUND_FAIL_NOT_PAID = new ErrorCode(1_007_900_005, "å‘èµ·é€€æ¬¾å¤±è´¥ï¼Œç¤ºä¾‹è®¢å•æœªæ”¯ä»˜"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUNDED = new ErrorCode(1_007_900_006, "å‘起退款失败,示例订å•已退款"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1_007_900_007, "å‘起退款失败,退款订å•ä¸å­˜åœ¨"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS = new ErrorCode(1_007_900_008, "å‘èµ·é€€æ¬¾å¤±è´¥ï¼Œé€€æ¬¾è®¢å•æœªé€€æ¬¾æˆåŠŸ"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1_007_900_009, "å‘起退款失败,退款å•ç¼–å·ä¸åŒ¹é…"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1_007_900_010, "å‘起退款失败,退款å•金é¢ä¸åŒ¹é…"); + + // ========== ç¤ºä¾‹è½¬è´¦è®¢å• 1-007-901-001 ========== + ErrorCode DEMO_TRANSFER_NOT_FOUND = new ErrorCode(1_007_901_001, "示例转账å•ä¸å­˜åœ¨"); + ErrorCode DEMO_TRANSFER_FAIL_TRANSFER_ID_ERROR = new ErrorCode(1_007_901_002, "转账失败,转账å•ç¼–å·ä¸åŒ¹é…"); + ErrorCode DEMO_TRANSFER_FAIL_PRICE_NOT_MATCH = new ErrorCode(1_007_901_003, "转账失败,转账å•金é¢ä¸åŒ¹é…"); +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/MessageTemplateConstants.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/MessageTemplateConstants.java new file mode 100644 index 0000000..2723f09 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/MessageTemplateConstants.java @@ -0,0 +1,14 @@ +package com.tashow.cloud.payapi.enums; + +/** + * é€šçŸ¥æ¨¡æ¿æžšä¸¾ç±» + * + * @author HUIHUI + */ +public interface MessageTemplateConstants { + + // ======================= å°ç¨‹åºè®¢é˜…æ¶ˆæ¯ ======================= + + String WXA_WALLET_RECHARGER_PAID = "充值æˆåŠŸé€šçŸ¥"; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/notify/PayNotifyStatusEnum.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/notify/PayNotifyStatusEnum.java new file mode 100644 index 0000000..7bf09a8 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/notify/PayNotifyStatusEnum.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.payapi.enums.notify; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * æ”¯ä»˜é€šçŸ¥çŠ¶æ€æžšä¸¾ + * + * @author èŠ‹é“æºç  + */ +@Getter +@AllArgsConstructor +public enum PayNotifyStatusEnum { + + WAITING(0, "等待通知"), + SUCCESS(10, "通知æˆåŠŸ"), + FAILURE(20, "通知失败"), // 多次å°è¯•,彻底失败 + REQUEST_SUCCESS(21, "请求æˆåŠŸï¼Œä½†æ˜¯ç»“æžœå¤±è´¥"), + REQUEST_FAILURE(22, "请求失败"), + + ; + + /** + * çŠ¶æ€ + */ + private final Integer status; + /** + * åå­— + */ + private final String name; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/notify/PayNotifyTypeEnum.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/notify/PayNotifyTypeEnum.java new file mode 100644 index 0000000..bd0cc60 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/notify/PayNotifyTypeEnum.java @@ -0,0 +1,29 @@ +package com.tashow.cloud.payapi.enums.notify; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 支付通知类型 + * + * @author èŠ‹é“æºç  + */ +@Getter +@AllArgsConstructor +public enum PayNotifyTypeEnum { + + ORDER(1, "支付å•"), + REFUND(2, "退款å•"), + TRANSFER(3, "转账å•") + ; + + /** + * 类型 + */ + private final Integer type; + /** + * åå­— + */ + private final String name; + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/order/PayOrderStatusEnum.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/order/PayOrderStatusEnum.java new file mode 100644 index 0000000..e296926 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/order/PayOrderStatusEnum.java @@ -0,0 +1,74 @@ +package com.tashow.cloud.payapi.enums.order; + +import com.tashow.cloud.common.core.ArrayValuable; +import com.tashow.cloud.common.util.object.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * 支付订å•çš„çŠ¶æ€æžšä¸¾ + * + * @author èŠ‹é“æºç  + */ +@Getter +@AllArgsConstructor +public enum PayOrderStatusEnum implements ArrayValuable { + + WAITING(0, "未支付"), + SUCCESS(10, "支付æˆåŠŸ"), + REFUND(20, "已退款"), + CLOSED(30, "支付关闭"), // 注æ„:全部退款åŽï¼Œè¿˜æ˜¯ REFUND çŠ¶æ€ + ; + + private final Integer status; + private final String name; + + @Override + public Integer[] array() { + return new Integer[0]; + } + + /** + * 判断是å¦ç­‰å¾…支付 + * + * @param status çŠ¶æ€ + * @return 是å¦ç­‰å¾…支付 + */ + public static boolean isWaiting(Integer status) { + return Objects.equals(status, WAITING.getStatus()); + } + + /** + * åˆ¤æ–­æ˜¯å¦æ”¯ä»˜æˆåŠŸ + * + * @param status çŠ¶æ€ + * @return æ˜¯å¦æ”¯ä»˜æˆåŠŸ + */ + public static boolean isSuccess(Integer status) { + return Objects.equals(status, SUCCESS.getStatus()); + } + + /** + * åˆ¤æ–­æ˜¯å¦æ”¯ä»˜æˆåŠŸæˆ–è€…å·²é€€æ¬¾ + * + * @param status çŠ¶æ€ + * @return æ˜¯å¦æ”¯ä»˜æˆåŠŸæˆ–è€…å·²é€€æ¬¾ + */ + public static boolean isSuccessOrRefund(Integer status) { + return ObjectUtils.equalsAny(status, + SUCCESS.getStatus(), REFUND.getStatus()); + } + + /** + * åˆ¤æ–­æ˜¯å¦æ”¯ä»˜å…³é—­ + * + * @param status çŠ¶æ€ + * @return æ˜¯å¦æ”¯ä»˜å…³é—­ + */ + public static boolean isClosed(Integer status) { + return Objects.equals(status, CLOSED.getStatus()); + } + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/refund/PayRefundStatusEnum.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/refund/PayRefundStatusEnum.java new file mode 100644 index 0000000..a4a2b51 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/refund/PayRefundStatusEnum.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.payapi.enums.refund; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * 渠é“çš„é€€æ¬¾çŠ¶æ€æžšä¸¾ + * + * @author èŠ‹é“æºç  + */ +@Getter +@AllArgsConstructor +public enum PayRefundStatusEnum { + + WAITING(0, "未退款"), + SUCCESS(10, "退款æˆåŠŸ"), + FAILURE(20, "退款失败"); + + private final Integer status; + private final String name; + + public static boolean isSuccess(Integer status) { + return Objects.equals(status, SUCCESS.getStatus()); + } + + public static boolean isFailure(Integer status) { + return Objects.equals(status, FAILURE.getStatus()); + } + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/transfer/PayTransferStatusEnum.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/transfer/PayTransferStatusEnum.java new file mode 100644 index 0000000..ef181d6 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/transfer/PayTransferStatusEnum.java @@ -0,0 +1,59 @@ +package com.tashow.cloud.payapi.enums.transfer; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +/** + * @author jason + */ +@Getter +@AllArgsConstructor +public enum PayTransferStatusEnum { + + WAITING(0, "等待转账"), + /** + * TODO 转账到银行å¡. 会有T+0 T+1 到账的请情况。 还未实现 + */ + IN_PROGRESS(10, "转账进行中"), + + SUCCESS(20, "转账æˆåŠŸ"), + /** + * 转账关闭 (失败,或者其它情况) // TODO æ”¹æˆ è½¬è´¦å¤±è´¥çŠ¶æ€ + */ + CLOSED(30, "转账关闭"); + + /** + * çŠ¶æ€ + */ + private final Integer status; + /** + * 状æ€å + */ + private final String name; + + public static boolean isSuccess(Integer status) { + return Objects.equals(status, SUCCESS.getStatus()); + } + + public static boolean isClosed(Integer status) { + return Objects.equals(status, CLOSED.getStatus()); + } + + public static boolean isWaiting(Integer status) { + return Objects.equals(status, WAITING.getStatus()); + } + public static boolean isInProgress(Integer status) { + return Objects.equals(status, IN_PROGRESS.getStatus()); + } + + /** + * 是å¦å¤„äºŽå¾…è½¬è´¦æˆ–è€…è½¬è´¦ä¸­çš„çŠ¶æ€ + * @param status çŠ¶æ€ + */ + public static boolean isPendingStatus(Integer status) { + return Objects.equals(status, WAITING.getStatus()) || Objects.equals(status, IN_PROGRESS.getStatus()); + } + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/transfer/PayTransferTypeEnum.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/transfer/PayTransferTypeEnum.java new file mode 100644 index 0000000..6b00742 --- /dev/null +++ b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/transfer/PayTransferTypeEnum.java @@ -0,0 +1,44 @@ +package com.tashow.cloud.payapi.enums.transfer; + +import cn.hutool.core.util.ArrayUtil; +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 转账类型枚举 + * + * @author jason + */ +@AllArgsConstructor +@Getter +public enum PayTransferTypeEnum implements ArrayValuable { + + ALIPAY_BALANCE(1, "支付å®ä½™é¢"), + WX_BALANCE(2, "微信余é¢"), + BANK_CARD(3, "银行å¡"), + WALLET_BALANCE(4, "钱包余é¢"); + + public interface WxPay { + } + + public interface Alipay { + } + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(PayTransferTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static PayTransferTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + +} diff --git a/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/wallet/PayWalletBizTypeEnum.java b/tashow-feign/tashow-pay-api/src/main/java/com/tashow/cloud/payapi/enums/wallet/PayWalletBizTypeEnum.java new file mode 100644 index 0000000..e69de29 diff --git a/tashow-feign/tashow-trade-api/pom.xml b/tashow-feign/tashow-trade-api/pom.xml new file mode 100644 index 0000000..3e8d53a --- /dev/null +++ b/tashow-feign/tashow-trade-api/pom.xml @@ -0,0 +1,40 @@ + + + + com.tashow.cloud + tashow-feign + ${revision} + + 4.0.0 + tashow-trade-api + jar + + ${project.artifactId} + + trade æ¨¡å— API,暴露给其它模å—调用 + + + + + com.tashow.cloud + tashow-common + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + true + + + + diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/order/TradeOrderApi.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/order/TradeOrderApi.java new file mode 100644 index 0000000..57d0e30 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/order/TradeOrderApi.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.tradeapi.api.order; + +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.tradeapi.api.order.dto.TradeOrderRespDTO; +import com.tashow.cloud.tradeapi.enums.ApiConstants; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.Collection; +import java.util.List; + +@FeignClient(name = ApiConstants.NAME) +public interface TradeOrderApi { + + String PREFIX = ApiConstants.PREFIX + "/order"; + + /*获得订å•列表*/ + @GetMapping(PREFIX + "/order/list") + CommonResult> getOrderList(@RequestParam("ids") Collection ids); + + /*获得订å•*/ + @GetMapping(PREFIX + "/get") + CommonResult getOrder(@RequestParam("id") Long id); + + @PutMapping(PREFIX + "/cancel-paid") + CommonResult cancelPaidOrder(@RequestParam("userId") Long userId, + @RequestParam("orderId") Long orderId, + @RequestParam("cancelType") Integer cancelType); + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/order/dto/TradeOrderRespDTO.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/order/dto/TradeOrderRespDTO.java new file mode 100644 index 0000000..4dfab03 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/order/dto/TradeOrderRespDTO.java @@ -0,0 +1,67 @@ +package com.tashow.cloud.tradeapi.api.order.dto; + +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 订å•ä¿¡æ¯ Response DTO + * RPC æœåŠ¡ - 订å•ä¿¡æ¯ Response DTO + * @author HUIHUI + */ +@Data +public class TradeOrderRespDTO { + + // ========== 订å•åŸºæœ¬ä¿¡æ¯ ========== + + //订å•ç¼–å· + private Long id; + + //è®¢å•æµæ°´å· + private String no; + + //订å•类型 + private Integer type; // å‚è§ TradeOrderTypeEnum 枚举 + + //è®¢å•æ¥æº + private Integer terminal; // å‚è§ TerminalEnum 枚举 + + //ç”¨æˆ·ç¼–å· + private Long userId; + + //用户 IP + private String userIp; + + //用户备注 + private String userRemark; + + //订å•çŠ¶æ€ + private Integer status; // å‚è§ TradeOrderStatusEnum 枚举 + + //è´­ä¹°çš„å•†å“æ•°é‡ + private Integer productCount; + + //订å•å®Œæˆæ—¶é—´ + private LocalDateTime finishTime; + + //订å•å–æ¶ˆæ—¶é—´ + private LocalDateTime cancelTime; + + //å–æ¶ˆç±»åž‹ + private Integer cancelType; // å‚è§ TradeOrderCancelTypeEnum 枚举 + + //商家备注 + private String remark; + + //是å¦è¯„ä»· + private Boolean commentStatus; + + // ========== ä»·æ ¼ + æ”¯ä»˜åŸºæœ¬ä¿¡æ¯ ========== + + //支付订å•ç¼–å· + private Long payOrderId; + + //是å¦å·²æ”¯ä»˜ + private Boolean payStatus; + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/package-info.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/package-info.java new file mode 100644 index 0000000..33ff7ee --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/api/package-info.java @@ -0,0 +1 @@ +package com.tashow.cloud.tradeapi.api; \ No newline at end of file diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/ApiConstants.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/ApiConstants.java new file mode 100644 index 0000000..2082491 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/ApiConstants.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.tradeapi.enums; + + +import com.tashow.cloud.common.enums.RpcConstants; + +/** + * API 相关的枚举 + * + * @author èŠ‹é“æºç  + */ +public class ApiConstants { + + /** + * æœåŠ¡å + * + * 注æ„,需è¦ä¿è¯å’Œ spring.application.name ä¿æŒä¸€è‡´ + */ + public static final String NAME = "trade-server"; + + public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/trade"; + + public static final String VERSION = "1.0.0"; + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/DictTypeConstants.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/DictTypeConstants.java new file mode 100644 index 0000000..cec7a1a --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/DictTypeConstants.java @@ -0,0 +1,12 @@ +package com.tashow.cloud.tradeapi.enums; + +/** + * Trade 字典类型的枚举类 + * + * @author owen + */ +public interface DictTypeConstants { + + String BROKERAGE_WITHDRAW_STATUS = "brokerage_withdraw_status"; // 佣金æçŽ°çŠ¶æ€ + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/ErrorCodeConstants.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/ErrorCodeConstants.java new file mode 100644 index 0000000..3f0a983 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/ErrorCodeConstants.java @@ -0,0 +1,106 @@ +package com.tashow.cloud.tradeapi.enums; + + +import com.tashow.cloud.common.exception.ErrorCode; + +/** + * Trade é”™è¯¯ç æžšä¸¾ç±» + * trade 系统,使用 1-011-000-000 段 + * + * @author LeeYan9 + * @since 2022-08-26 + */ +public interface ErrorCodeConstants { + + // ========== Order æ¨¡å— 1-011-000-000 ========== + ErrorCode ORDER_ITEM_NOT_FOUND = new ErrorCode(1_011_000_010, "交易订å•项ä¸å­˜åœ¨"); + ErrorCode ORDER_NOT_FOUND = new ErrorCode(1_011_000_011, "交易订å•ä¸å­˜åœ¨"); + ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1_011_000_012, "交易订å•项更新售åŽçжæ€å¤±è´¥ï¼Œè¯·é‡è¯•"); + ErrorCode ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1_011_000_013, "äº¤æ˜“è®¢å•æ›´æ–°æ”¯ä»˜çжæ€å¤±è´¥ï¼Œè®¢å•䏿˜¯ã€æœªæ”¯ä»˜ã€‘状æ€"); + ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1_011_000_014, "äº¤æ˜“è®¢å•æ›´æ–°æ”¯ä»˜çжæ€å¤±è´¥ï¼Œæ”¯ä»˜å•ç¼–å·ä¸åŒ¹é…"); + ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_015, "äº¤æ˜“è®¢å•æ›´æ–°æ”¯ä»˜çжæ€å¤±è´¥ï¼Œæ”¯ä»˜å•状æ€ä¸æ˜¯ã€æ”¯ä»˜æˆåŠŸã€‘çŠ¶æ€"); + ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1_011_000_016, "äº¤æ˜“è®¢å•æ›´æ–°æ”¯ä»˜çжæ€å¤±è´¥ï¼Œæ”¯ä»˜å•金é¢ä¸åŒ¹é…"); + ErrorCode ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED = new ErrorCode(1_011_000_017, "交易订å•å‘货失败,订å•䏿˜¯ã€å¾…å‘货】状æ€"); + ErrorCode ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_018, "äº¤æ˜“è®¢å•æ”¶è´§å¤±è´¥ï¼Œè®¢å•䏿˜¯ã€å¾…收货】状æ€"); + ErrorCode ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED = new ErrorCode(1_011_000_019, "创建交易订å•项的评价失败,订å•䏿˜¯ã€å·²å®Œæˆã€‘状æ€"); + ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1_011_000_020, "创建交易订å•项的评价失败,订å•已评价"); + ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1_011_000_021, "交易订å•å‘货失败,订å•已退款或部分退款"); + ErrorCode ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_022, "交易订å•å‘货失败,拼团未æˆåŠŸ"); + ErrorCode ORDER_DELIVERY_FAIL_BARGAIN_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_023, "交易订å•å‘货失败,ç ä»·æœªæˆåŠŸ"); + ErrorCode ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS = new ErrorCode(1_011_000_024, "交易订å•å‘货失败,å‘è´§ç±»åž‹ä¸æ˜¯å¿«é€’"); + ErrorCode ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID = new ErrorCode(1_011_000_025, "交易订å•å–æ¶ˆå¤±è´¥ï¼Œè®¢å•䏿˜¯ã€å¾…支付】状æ€"); + ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1_011_000_026, "支付订å•调价失败,原因:支付订å•已付款,ä¸èƒ½è°ƒä»·"); + ErrorCode ORDER_UPDATE_PRICE_FAIL_ALREADY = new ErrorCode(1_011_000_027, "支付订å•调价失败,原因:已ç»ä¿®æ”¹è¿‡ä»·æ ¼"); + ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1_011_000_028, "支付订å•è°ƒä»·å¤±è´¥ï¼ŒåŽŸå› ï¼šè°ƒæ•´åŽæ”¯ä»˜ä»·æ ¼ä¸èƒ½å°äºŽ 0.01 å…ƒ"); + ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1_011_000_029, "交易订å•删除失败,订å•䏿˜¯ã€å·²å–消】状æ€"); + ErrorCode ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP = new ErrorCode(1_011_000_030, "交易订å•自æå¤±è´¥ï¼Œæ”¶è´§æ–¹å¼ä¸æ˜¯ã€ç”¨æˆ·è‡ªæã€‘"); + ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订å•修改收货地å€å¤±è´¥ï¼ŒåŽŸå› ï¼šè®¢å•䏿˜¯ã€å¾…å‘货】状æ€"); + ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, "交易订å•创建失败,原因:存在未付款订å•"); + ErrorCode ORDER_CANCEL_PAID_FAIL = new ErrorCode(1_011_000_033, "交易订å•å–æ¶ˆæ”¯ä»˜å¤±è´¥ï¼ŒåŽŸå› ï¼šè®¢å•䏿˜¯ã€{}】状æ€"); + ErrorCode ORDER_PICK_UP_FAIL_NOT_VERIFY_USER = new ErrorCode(1_011_000_034, "交易订å•自æå¤±è´¥ï¼ŒåŽŸå› ï¼šä½ æ²¡æœ‰æ ¸é”€è¯¥é—¨åº—è®¢å•çš„æƒé™"); + ErrorCode ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS = new ErrorCode(1_011_000_035, "交易订å•创建失败,原因:用户积分ä¸è¶³"); + + // ========== After Sale æ¨¡å— 1-011-000-100 ========== + ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "å”®åŽå•ä¸å­˜åœ¨"); + ErrorCode AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR = new ErrorCode(1_011_000_101, "申请退款金é¢é”™è¯¯"); + ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED = new ErrorCode(1_011_000_102, "订å•已关闭,无法申请售åŽ"); + ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID = new ErrorCode(1_011_000_103, "è®¢å•æœªæ”¯ä»˜ï¼Œæ— æ³•申请售åŽ"); + ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED = new ErrorCode(1_011_000_104, "è®¢å•æœªå‘货,无法申请ã€é€€è´§é€€æ¬¾ã€‘å”®åŽ"); + ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED = new ErrorCode(1_011_000_105, "订å•项已申请售åŽï¼Œæ— æ³•é‡å¤ç”³è¯·"); + ErrorCode AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY = new ErrorCode(1_011_000_106, "审批失败,售åŽçжæ€ä¸å¤„于审批中"); + ErrorCode AFTER_SALE_UPDATE_STATUS_FAIL = new ErrorCode(1_011_000_107, "æ“作售åŽå•失败,请刷新åŽé‡è¯•"); + ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1_011_000_108, "退货失败,售åŽå•状æ€ä¸å¤„于ã€å¾…买家退货】"); + ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1_011_000_109, "确认收货失败,售åŽå•状æ€ä¸å¤„于ã€å¾…确认收货】"); + ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, "退款失败,售åŽå•状æ€ä¸æ˜¯ã€å¾…退款】"); + ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY = + new ErrorCode(1_011_000_111, "å–æ¶ˆå”®åŽå•失败,售åŽå•状æ€ä¸æ˜¯ã€å¾…审核】或ã€å–å®¶åŒæ„】或ã€å•†å®¶å¾…收货】"); + ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_COMBINATION_IN_PROGRESS = new ErrorCode(1_011_000_112, "è®¢å•æ‹¼å›¢ä¸­ï¼Œæ— æ³•申请售åŽ"); + + // ========== Cart æ¨¡å— 1-011-002-000 ========== + ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项ä¸å­˜åœ¨"); + + // ========== Price 相关 1-011-003-000 ============ + ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1_011_003_000, "支付价格计算异常,原因:价格å°äºŽç­‰äºŽ 0"); + ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1_011_003_001, "计算快递è¿è´¹å¼‚常,找ä¸åˆ°å¯¹åº”çš„è¿è´¹æ¨¡æ¿"); + ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_002, "å‚与秒æ€ã€æ‹¼å›¢ã€ç ä»·çš„è¥é”€å•†å“,无法使用优惠劵"); + ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_003, "å‚与秒æ€çš„商å“ï¼Œè¶…è¿‡äº†ç§’æ€æ€»é™è´­æ•°é‡"); + ErrorCode PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_004, "å‚与积分活动的商å“ï¼Œè¶…è¿‡äº†ç§¯åˆ†æ´»åŠ¨å•†å“æ€»é™è´­æ•°é‡"); + ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL = new ErrorCode(1_011_003_005, "计算快递è¿è´¹å¼‚常,é…逿–¹å¼ä¸åŒ¹é…"); + ErrorCode PRICE_CALCULATE_COUPON_CAN_NOT_USE = new ErrorCode(1_011_003_006, "该优惠劵无法使用,原因:{}ã€"); + + // ========== ç‰©æµ Express æ¨¡å— 1-011-004-000 ========== + ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公å¸ä¸å­˜åœ¨"); + ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1_011_004_001, "å·²ç»å­˜åœ¨è¯¥ç¼–ç çš„快递公å¸"); + ErrorCode EXPRESS_CLIENT_NOT_PROVIDE = new ErrorCode(1_011_004_002, "éœ€è¦æŽ¥å…¥å¿«é€’æœåŠ¡å•†ï¼Œæ¯”å¦‚ã€å¿«é€’100】"); + ErrorCode EXPRESS_STATUS_NOT_ENABLE = new ErrorCode(1_011_004_003, "å¿«é€’å…¬å¸æœªå¯ç”¨"); + + ErrorCode EXPRESS_API_QUERY_ERROR = new ErrorCode(1_011_004_101, "快递查询接å£å¼‚常"); + ErrorCode EXPRESS_API_QUERY_FAILED = new ErrorCode(1_011_004_102, "快递查询返回失败,原因:{}"); + + // ========== ç‰©æµ Template æ¨¡å— 1-011-005-000 ========== + ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1_011_005_000, "å·²ç»å­˜åœ¨è¯¥è¿è´¹æ¨¡æ¿å"); + ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_011_005_001, "è¿è´¹æ¨¡æ¿ä¸å­˜åœ¨"); + + // ========== ç‰©æµ PICK_UP æ¨¡å— 1-011-006-000 ========== + ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1_011_006_000, "自æé—¨åº—ä¸å­˜åœ¨"); + ErrorCode PICK_UP_STORE_STAFF_NOT_EXISTS = new ErrorCode(1_011_006_000, "自æé—¨åº—店员ä¸å­˜åœ¨"); + + // ========== 分销用户 æ¨¡å— 1-011-007-000 ========== + ErrorCode BROKERAGE_USER_NOT_EXISTS = new ErrorCode(1_011_007_000, "分销用户ä¸å­˜åœ¨"); + ErrorCode BROKERAGE_USER_FROZEN_PRICE_NOT_ENOUGH = new ErrorCode(1_011_007_001, "用户冻结佣金({})æ•°é‡ä¸è¶³"); + ErrorCode BROKERAGE_BIND_SELF = new ErrorCode(1_011_007_002, "ä¸èƒ½ç»‘定自己"); + ErrorCode BROKERAGE_BIND_USER_NOT_ENABLED = new ErrorCode(1_011_007_003, "绑定用户没有推广资格"); + ErrorCode BROKERAGE_BIND_CONDITION_ADMIN = new ErrorCode(1_011_007_004, "ä»…å¯åœ¨åŽå°ç»‘定推广员"); + ErrorCode BROKERAGE_BIND_MODE_REGISTER = new ErrorCode(1_011_007_005, "åªæœ‰åœ¨æ³¨å†Œæ—¶å¯ä»¥ç»‘定"); + ErrorCode BROKERAGE_BIND_OVERRIDE = new ErrorCode(1_011_007_006, "已绑定了推广人"); + ErrorCode BROKERAGE_BIND_LOOP = new ErrorCode(1_011_007_007, "下级ä¸èƒ½ç»‘定自己的上级"); + ErrorCode BROKERAGE_USER_LEVEL_NOT_SUPPORT = new ErrorCode(1_011_007_008, "ç›®å‰åªæ”¯æŒ level å°äºŽç­‰äºŽ 2"); + ErrorCode BROKERAGE_CREATE_USER_EXISTS = new ErrorCode(1_011_007_009, "分销用户已存在"); + + // ========== 分销æçް æ¨¡å— 1-011-008-000 ========== + ErrorCode BROKERAGE_WITHDRAW_NOT_EXISTS = new ErrorCode(1_011_008_000, "佣金æçŽ°è®°å½•ä¸å­˜åœ¨"); + ErrorCode BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING = new ErrorCode(1_011_008_001, "佣金æçŽ°è®°å½•çŠ¶æ€ä¸æ˜¯å®¡æ ¸ä¸­"); + ErrorCode BROKERAGE_WITHDRAW_MIN_PRICE = new ErrorCode(1_011_008_002, "æçް金é¢ä¸èƒ½ä½ŽäºŽ {} å…ƒ"); + ErrorCode BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH = new ErrorCode(1_011_008_003, "æ‚¨å½“å‰æœ€å¤šå¯æçް {} å…ƒ"); + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/MessageTemplateConstants.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/MessageTemplateConstants.java new file mode 100644 index 0000000..337ca78 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/MessageTemplateConstants.java @@ -0,0 +1,18 @@ +package com.tashow.cloud.tradeapi.enums; + +/** + * é€šçŸ¥æ¨¡æ¿æžšä¸¾ç±» + * + * @author HUIHUI + */ +public interface MessageTemplateConstants { + + // ======================= çŸ­ä¿¡æ¶ˆæ¯æ¨¡ç‰ˆ ======================= + + String SMS_ORDER_DELIVERY = "order_delivery"; // çŸ­ä¿¡æ¨¡ç‰ˆç¼–å· + + // ======================= å°ç¨‹åºè®¢é˜…æ¶ˆæ¯æ¨¡ç‰ˆ ======================= + + String WXA_ORDER_DELIVERY = "订å•å‘货通知"; + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleOperateTypeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleOperateTypeEnum.java new file mode 100644 index 0000000..54183cb --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleOperateTypeEnum.java @@ -0,0 +1,35 @@ +package com.tashow.cloud.tradeapi.enums.aftersale; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * å”®åŽæ“作类型的枚举 + * + * @author é™ˆè³ + * @since 2023/6/13 13:53 + */ +@RequiredArgsConstructor +@Getter +public enum AfterSaleOperateTypeEnum { + + MEMBER_CREATE(10, "会员申请退款"), + ADMIN_AGREE_APPLY(11, "å•†å®¶åŒæ„退款"), + ADMIN_DISAGREE_APPLY(12, "商家拒ç»é€€æ¬¾"), + MEMBER_DELIVERY(20, "会员填写退货物æµä¿¡æ¯ï¼Œå¿«é€’å…¬å¸ï¼š{deliveryName},快递å•å·ï¼š{logisticsNo}"), + ADMIN_AGREE_RECEIVE(21, "商家收货"), + ADMIN_DISAGREE_RECEIVE(22, "å•†å®¶æ‹’ç»æ”¶è´§ï¼ŒåŽŸå› ï¼š{reason}"), + ADMIN_REFUND(30, "商家退款"), + MEMBER_CANCEL(40, "ä¼šå‘˜å–æ¶ˆé€€æ¬¾"), + ; + + /** + * æ“作类型 + */ + private final Integer type; + /** + * æ“作æè¿° + */ + private final String content; + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleStatusEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleStatusEnum.java new file mode 100644 index 0000000..91834b3 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleStatusEnum.java @@ -0,0 +1,95 @@ +package com.tashow.cloud.tradeapi.enums.aftersale; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Collection; + +import static cn.hutool.core.util.ArrayUtil.firstMatch; + +/** + * å”®åŽçжæ€çš„æžšä¸¾ + * + * çŠ¶æ€æµè½¬ + * + * @author èŠ‹é“æºç  + */ +@AllArgsConstructor +@Getter +public enum AfterSaleStatusEnum implements ArrayValuable { + + /** + * ã€ç”³è¯·å”®åŽã€‘ + */ + APPLY(10,"申请中", "会员申请退款"), // æœ‰èµžçš„çŠ¶æ€æç¤ºï¼šé€€æ¬¾ç”³è¯·å¾…å•†å®¶å¤„ç† + /** + * å–家通过售åŽï¼›ã€å•†å“待退货】 + */ + SELLER_AGREE(20, "å–家通过", "å•†å®¶åŒæ„退款"), // æœ‰èµžçš„çŠ¶æ€æç¤ºï¼šè¯·é€€è´§å¹¶å¡«å†™ç‰©æµä¿¡æ¯ + /** + * 买家已退货,等待å–å®¶æ”¶è´§ï¼›ã€å•†å®¶å¾…收货】 + */ + BUYER_DELIVERY(30,"å¾…å–å®¶æ”¶è´§", "会员填写退货物æµä¿¡æ¯"), // æœ‰èµžçš„çŠ¶æ€æç¤ºï¼šé€€è´§é€€æ¬¾ç”³è¯·å¾…å•†å®¶å¤„ç† + /** + * å–家已收货,等待平å°é€€æ¬¾ï¼›ç­‰å¾…退款ã€ç­‰å¾…退款】 + */ + WAIT_REFUND(40, "等待平å°é€€æ¬¾", "商家收货"), // æœ‰èµžçš„çŠ¶æ€æç¤ºï¼šæ— ï¼ˆæœ‰èµžæ— è¯¥çŠ¶æ€ï¼‰ + /** + * 完æˆé€€æ¬¾ã€é€€æ¬¾æˆåŠŸã€‘ + */ + COMPLETE(50, "完æˆ", "商家确认退款"), // æœ‰èµžçš„çŠ¶æ€æç¤ºï¼šé€€æ¬¾æˆåŠŸ + /** + * ã€ä¹°å®¶å–消】 + */ + BUYER_CANCEL(61, "ä¹°å®¶å–æ¶ˆå”®åŽ", "ä¼šå‘˜å–æ¶ˆé€€æ¬¾"), // æœ‰èµžçš„çŠ¶æ€æç¤ºï¼šé€€æ¬¾å…³é—­ + /** + * å–å®¶æ‹’ç»å”®åŽï¼›å•†å®¶æ‹’ç»ã€å•†å®¶æ‹’ç»ã€‘ + */ + SELLER_DISAGREE(62,"å–å®¶æ‹’ç»", "商家拒ç»é€€æ¬¾"), // æœ‰èµžçš„çŠ¶æ€æç¤ºï¼šå•†å®¶ä¸åŒæ„退款申请 + /** + * å–å®¶æ‹’ç»æ”¶è´§ï¼Œç»ˆæ­¢å”®åŽï¼›ã€å•†å®¶æ‹’收货】 + */ + SELLER_REFUSE(63,"å–å®¶æ‹’ç»æ”¶è´§", "å•†å®¶æ‹’ç»æ”¶è´§"), // æœ‰èµžçš„çŠ¶æ€æç¤ºï¼šå•†å®¶æ‹’ç»æ”¶è´§ï¼Œä¸åŒæ„退款 + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(AfterSaleStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 进行中的售åŽçŠ¶æ€ + * + * ä¸åŒ…括已ç»ç»“æŸçš„çŠ¶æ€ + */ + public static final Collection APPLYING_STATUSES = Arrays.asList( + APPLY.getStatus(), + SELLER_AGREE.getStatus(), + BUYER_DELIVERY.getStatus(), + WAIT_REFUND.getStatus() + ); + + /** + * çŠ¶æ€ + */ + private final Integer status; + /** + * 状æ€å + */ + private final String name; + /** + * æ“作内容 + * + * ç›®çš„ï¼šè®°å½•å”®åŽæ—¥å¿—的内容 + */ + private final String content; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static AfterSaleStatusEnum valueOf(Integer status) { + return firstMatch(value -> value.getStatus().equals(status), values()); + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleTypeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleTypeEnum.java new file mode 100644 index 0000000..e3bb43d --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleTypeEnum.java @@ -0,0 +1,37 @@ +package com.tashow.cloud.tradeapi.enums.aftersale; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * äº¤æ˜“å”®åŽ - 类型 + * + * @author èŠ‹é“æºç  + */ +@RequiredArgsConstructor +@Getter +public enum AfterSaleTypeEnum implements ArrayValuable { + + IN_SALE(10, "售中退款"), // 交易完æˆå‰ä¹°å®¶ç”³è¯·é€€æ¬¾ + AFTER_SALE(20, "å”®åŽé€€æ¬¾"); // 交易完æˆåŽä¹°å®¶ç”³è¯·é€€æ¬¾ + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(AfterSaleTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * 类型å + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleWayEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleWayEnum.java new file mode 100644 index 0000000..f3e7016 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/aftersale/AfterSaleWayEnum.java @@ -0,0 +1,37 @@ +package com.tashow.cloud.tradeapi.enums.aftersale; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * äº¤æ˜“å”®åŽ - æ–¹å¼ + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum AfterSaleWayEnum implements ArrayValuable { + + REFUND(10, "仅退款"), + RETURN_AND_REFUND(20, "退货退款"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(AfterSaleWayEnum::getWay).toArray(Integer[]::new); + + /** + * æ–¹å¼ + */ + private final Integer way; + /** + * æ–¹å¼å + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageBindModeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageBindModeEnum.java new file mode 100644 index 0000000..9d0353f --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageBindModeEnum.java @@ -0,0 +1,48 @@ +package com.tashow.cloud.tradeapi.enums.brokerage; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * åˆ†é”€å…³ç³»ç»‘å®šæ¨¡å¼æžšä¸¾ + * + * @author owen + */ +@AllArgsConstructor +@Getter +public enum BrokerageBindModeEnum implements ArrayValuable { + + /** + * åªè¦ç”¨æˆ·æ²¡æœ‰æŽ¨å¹¿äººï¼Œéšæ—¶éƒ½å¯ä»¥ç»‘定分销关系 + */ + ANYTIME(1, "首次绑定"), + /** + * 仅新用户注册时æ‰èƒ½ç»‘定推广关系 + */ + REGISTER(2, "注册绑定"), + /** + * æ¯æ¬¡æ‰«ç éƒ½è¦†ç›– + */ + OVERRIDE(3, "覆盖绑定"), + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageBindModeEnum::getMode).toArray(Integer[]::new); + + /** + * æ¨¡å¼ + */ + private final Integer mode; + /** + * åå­— + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageEnabledConditionEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageEnabledConditionEnum.java new file mode 100644 index 0000000..5b58c2f --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageEnabledConditionEnum.java @@ -0,0 +1,44 @@ +package com.tashow.cloud.tradeapi.enums.brokerage; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * åˆ†ä½£æ¨¡å¼æžšä¸¾ + * + * @author owen + */ +@AllArgsConstructor +@Getter +public enum BrokerageEnabledConditionEnum implements ArrayValuable { + + /** + * 所有用户都å¯ä»¥åˆ†é”€ + */ + ALL(1, "人人分销"), + /** + * ä»…å¯åŽå°æ‰‹åŠ¨è®¾ç½®æŽ¨å¹¿å‘˜ + */ + ADMIN(2, "指定分销"), + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageEnabledConditionEnum::getCondition).toArray(Integer[]::new); + + /** + * æ¨¡å¼ + */ + private final Integer condition; + /** + * åå­— + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageRecordBizTypeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageRecordBizTypeEnum.java new file mode 100644 index 0000000..d10221a --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageRecordBizTypeEnum.java @@ -0,0 +1,47 @@ +package com.tashow.cloud.tradeapi.enums.brokerage; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 佣金记录业务类型枚举 + * + * @author owen + */ +@AllArgsConstructor +@Getter +public enum BrokerageRecordBizTypeEnum implements ArrayValuable { + + ORDER(1, "获得推广佣金", "获得推广佣金 {}", true), + WITHDRAW(2, "æçŽ°ç”³è¯·", "æçŽ°ç”³è¯·æ‰£é™¤ä½£é‡‘ {}", false), + WITHDRAW_REJECT(3, "æçŽ°ç”³è¯·é©³å›ž", "æçŽ°ç”³è¯·é©³å›žï¼Œè¿”è¿˜ä½£é‡‘ {}", true), + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageRecordBizTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * 标题 + */ + private final String title; + /** + * æè¿° + */ + private final String description; + /** + * 是å¦ä¸ºå¢žåР佣金 + */ + private final boolean add; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageRecordStatusEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageRecordStatusEnum.java new file mode 100644 index 0000000..22cdf1d --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageRecordStatusEnum.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.tradeapi.enums.brokerage; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * ä½£é‡‘è®°å½•çŠ¶æ€æžšä¸¾ + * + * @author owen + */ +@AllArgsConstructor +@Getter +public enum BrokerageRecordStatusEnum implements ArrayValuable { + + WAIT_SETTLEMENT(0, "待结算"), + SETTLEMENT(1, "已结算"), + CANCEL(2, "已喿¶ˆ"), + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageRecordStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * çŠ¶æ€ + */ + private final Integer status; + /** + * åå­— + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageWithdrawStatusEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageWithdrawStatusEnum.java new file mode 100644 index 0000000..6293f38 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageWithdrawStatusEnum.java @@ -0,0 +1,42 @@ +package com.tashow.cloud.tradeapi.enums.brokerage; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +// TODO 芋艿:æçŽ°çš„æ‰“é€šï¼Œåœ¨çº ç»“ä¸‹ï¼› +/** + * 佣金æçŽ°çŠ¶æ€æžšä¸¾ + * + * @author owen + */ +@AllArgsConstructor +@Getter +public enum BrokerageWithdrawStatusEnum implements ArrayValuable { + + AUDITING(0, "审核中"), + AUDIT_SUCCESS(10, "审核通过"), + WITHDRAW_SUCCESS(11, "æçްæˆåŠŸ"), + AUDIT_FAIL(20, "审核ä¸é€šè¿‡"), + WITHDRAW_FAIL(21, "æçŽ°å¤±è´¥"), + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageWithdrawStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * çŠ¶æ€ + */ + private final Integer status; + /** + * åå­— + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageWithdrawTypeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageWithdrawTypeEnum.java new file mode 100644 index 0000000..87a234e --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/brokerage/BrokerageWithdrawTypeEnum.java @@ -0,0 +1,51 @@ +package com.tashow.cloud.tradeapi.enums.brokerage; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 佣金æçŽ°ç±»åž‹æžšä¸¾ + * + * @author owen + */ +@AllArgsConstructor +@Getter +public enum BrokerageWithdrawTypeEnum implements ArrayValuable { + + WALLET(1, "钱包"), + BANK(2, "银行å¡"), + WECHAT(3, "微信"), // 手动打款 + ALIPAY(4, "支付å®"), + WECHAT_API(5, "微信零钱"), // 自动打款,通过微信转账 API + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BrokerageWithdrawTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * åå­— + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + /** + * 是å¦é€šè¿‡æ”¯ä»˜å¹³å°çš„ API 打款 + * + * @param type 类型 + * @return æ˜¯å¦ + */ + public static boolean isApi(Integer type) { + return WECHAT_API.getType().equals(type); + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/delivery/DeliveryExpressChargeModeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/delivery/DeliveryExpressChargeModeEnum.java new file mode 100644 index 0000000..be36fc0 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/delivery/DeliveryExpressChargeModeEnum.java @@ -0,0 +1,43 @@ +package com.tashow.cloud.tradeapi.enums.delivery; + +import cn.hutool.core.util.ArrayUtil; +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 快递é…é€è®¡è´¹æ–¹å¼æžšä¸¾ + * + * @author jason + */ +@AllArgsConstructor +@Getter +public enum DeliveryExpressChargeModeEnum implements ArrayValuable { + + COUNT(1, "按件"), + WEIGHT(2,"按é‡é‡"), + VOLUME(3, "按体积"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(DeliveryExpressChargeModeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * æè¿° + */ + private final String desc; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static DeliveryExpressChargeModeEnum valueOf(Integer value) { + return ArrayUtil.firstMatch(chargeMode -> chargeMode.getType().equals(value), DeliveryExpressChargeModeEnum.values()); + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/delivery/DeliveryTypeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/delivery/DeliveryTypeEnum.java new file mode 100644 index 0000000..121cf7d --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/delivery/DeliveryTypeEnum.java @@ -0,0 +1,37 @@ +package com.tashow.cloud.tradeapi.enums.delivery; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * é…逿–¹å¼æžšä¸¾ + * + * @author èŠ‹é“æºç  + */ +@Getter +@AllArgsConstructor +public enum DeliveryTypeEnum implements ArrayValuable { + + EXPRESS(1, "快递å‘è´§"), + PICK_UP(2, "用户自æ"),; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(DeliveryTypeEnum::getType).toArray(Integer[]::new); + + /** + * é…逿–¹å¼ + */ + private final Integer type; + /** + * 状æ€å + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/notify/TradeNotifyEnums.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/notify/TradeNotifyEnums.java new file mode 100644 index 0000000..d7c1eae --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/notify/TradeNotifyEnums.java @@ -0,0 +1,5 @@ +package com.tashow.cloud.tradeapi.enums.notify; + +// TODO @芋艿:这个枚举的作用? +public interface TradeNotifyEnums { +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderCancelTypeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderCancelTypeEnum.java new file mode 100644 index 0000000..99fec84 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderCancelTypeEnum.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.tradeapi.enums.order; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * äº¤æ˜“è®¢å• - 关闭类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderCancelTypeEnum implements ArrayValuable { + + PAY_TIMEOUT(10, "超时未支付"), + AFTER_SALE_CLOSE(20, "退款关闭"), + MEMBER_CANCEL(30, "ä¹°å®¶å–æ¶ˆ"), + COMBINATION_CLOSE(40, "拼团关闭"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderCancelTypeEnum::getType).toArray(Integer[]::new); + + /** + * 关闭类型 + */ + private final Integer type; + /** + * 关闭类型å + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderItemAfterSaleStatusEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderItemAfterSaleStatusEnum.java new file mode 100644 index 0000000..5a7f6a9 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderItemAfterSaleStatusEnum.java @@ -0,0 +1,49 @@ +package com.tashow.cloud.tradeapi.enums.order; + +import cn.hutool.core.util.ObjectUtil; +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * 交易订å•项 - å”®åŽçŠ¶æ€ + * + * @author èŠ‹é“æºç  + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderItemAfterSaleStatusEnum implements ArrayValuable { + + NONE(0, "未售åŽ"), + APPLY(10, "å”®åŽä¸­"), + SUCCESS(20, "å”®åŽæˆåŠŸ"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderItemAfterSaleStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状æ€å€¼ + */ + private final Integer status; + /** + * 状æ€å + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + /** + * 判断指定状æ€ï¼Œæ˜¯å¦æ­£å¤„äºŽã€æœªç”³è¯·ã€‘çŠ¶æ€ + * + * @param status æŒ‡å®šçŠ¶æ€ + * @return æ˜¯å¦ + */ + public static boolean isNone(Integer status) { + return ObjectUtil.equals(status, NONE.getStatus()); + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderOperateTypeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderOperateTypeEnum.java new file mode 100644 index 0000000..44cbd88 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderOperateTypeEnum.java @@ -0,0 +1,42 @@ +package com.tashow.cloud.tradeapi.enums.order; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * è®¢å•æ“作类型的枚举 + * + * @author é™ˆè³ + * @since 2023/7/6 15:31 + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderOperateTypeEnum { + + MEMBER_CREATE(1, "用户下å•"), + ADMIN_UPDATE_PRICE(2, "订å•ä»·æ ¼ {oldPayPrice} 修改,调整价格 {adjustPrice},实际支付金é¢ä¸º {newPayPrice} å…ƒ"), + MEMBER_PAY(10, "用户付款æˆåŠŸ"), + ADMIN_UPDATE_ADDRESS(11, "收货地å€ä¿®æ”¹"), + ADMIN_DELIVERY(20, "å·²å‘货,快递公å¸ï¼š{expressName},快递å•å·ï¼š{logisticsNo}"), + MEMBER_RECEIVE(30, "用户已收货"), + SYSTEM_RECEIVE(31, "到期未收货,系统自动确认收货"), + ADMIN_PICK_UP_RECEIVE(32, "管ç†å‘˜è‡ªææ”¶è´§"), + MEMBER_COMMENT(33, "用户评价"), + SYSTEM_COMMENT(34, "到期未评价,系统自动评价"), + MEMBER_CANCEL(40, "å–æ¶ˆè®¢å•"), + SYSTEM_CANCEL(41, "åˆ°æœŸæœªæ”¯ä»˜ï¼Œç³»ç»Ÿè‡ªåŠ¨å–æ¶ˆè®¢å•"), + // 42 预留:管ç†å‘˜å–æ¶ˆè®¢å• + ADMIN_CANCEL_AFTER_SALE(43, "订å•全部售åŽï¼Œç®¡ç†å‘˜è‡ªåЍ喿¶ˆè®¢å•"), + MEMBER_DELETE(49, "删除订å•"), + ; + + /** + * æ“作类型 + */ + private final Integer type; + /** + * æ“作æè¿° + */ + private final String content; + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderRefundStatusEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderRefundStatusEnum.java new file mode 100644 index 0000000..b7702e6 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderRefundStatusEnum.java @@ -0,0 +1,38 @@ +package com.tashow.cloud.tradeapi.enums.order; + +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * äº¤æ˜“è®¢å• - é€€æ¬¾çŠ¶æ€ + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderRefundStatusEnum implements ArrayValuable { + + NONE(0, "未退款"), + PART(10, "部分退款"), + ALL(20, "全部退款"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderRefundStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状æ€å€¼ + */ + private final Integer status; + /** + * 状æ€å + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderStatusEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderStatusEnum.java new file mode 100644 index 0000000..fa1a997 --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderStatusEnum.java @@ -0,0 +1,116 @@ +package com.tashow.cloud.tradeapi.enums.order; + +import cn.hutool.core.util.ObjectUtil; +import com.tashow.cloud.common.core.ArrayValuable; +import com.tashow.cloud.common.util.object.ObjectUtils; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * äº¤æ˜“è®¢å• - çŠ¶æ€ + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderStatusEnum implements ArrayValuable { + + UNPAID(0, "待支付"), + UNDELIVERED(10, "å¾…å‘è´§"), + DELIVERED(20, "å·²å‘è´§"), + COMPLETED(30, "已完æˆ"), + CANCELED(40, "已喿¶ˆ"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状æ€å€¼ + */ + private final Integer status; + /** + * 状æ€å + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + // ========== 问:为什么写了很多 isXXX å’Œ haveXXX 的判断逻辑呢? ========== + // ========== 答:方便找到æŸä¸€ç±»åˆ¤æ–­ï¼Œå“ªäº›ä¸šåŠ¡æ­£åœ¨ä½¿ç”¨ ========== + + /** + * 判断指定状æ€ï¼Œæ˜¯å¦æ­£å¤„äºŽã€æœªä»˜æ¬¾ã€‘çŠ¶æ€ + * + * @param status æŒ‡å®šçŠ¶æ€ + * @return æ˜¯å¦ + */ + public static boolean isUnpaid(Integer status) { + return ObjectUtil.equal(UNPAID.getStatus(), status); + } + + /** + * 判断指定状æ€ï¼Œæ˜¯å¦æ­£å¤„于ã€å¾…å‘è´§ã€‘çŠ¶æ€ + * + * @param status æŒ‡å®šçŠ¶æ€ + * @return æ˜¯å¦ + */ + public static boolean isUndelivered(Integer status) { + return ObjectUtil.equal(UNDELIVERED.getStatus(), status); + } + + /** + * 判断指定状æ€ï¼Œæ˜¯å¦æ­£å¤„于ã€å·²å‘è´§ã€‘çŠ¶æ€ + * + * @param status æŒ‡å®šçŠ¶æ€ + * @return æ˜¯å¦ + */ + public static boolean isDelivered(Integer status) { + return ObjectUtil.equals(status, DELIVERED.getStatus()); + } + + /** + * 判断指定状æ€ï¼Œæ˜¯å¦æ­£å¤„于ã€å·²å–æ¶ˆã€‘çŠ¶æ€ + * + * @param status æŒ‡å®šçŠ¶æ€ + * @return æ˜¯å¦ + */ + public static boolean isCanceled(Integer status) { + return ObjectUtil.equals(status, CANCELED.getStatus()); + } + + /** + * 判断指定状æ€ï¼Œæ˜¯å¦æ­£å¤„于ã€å·²å®Œæˆã€‘çŠ¶æ€ + * + * @param status æŒ‡å®šçŠ¶æ€ + * @return æ˜¯å¦ + */ + public static boolean isCompleted(Integer status) { + return ObjectUtil.equals(status, COMPLETED.getStatus()); + } + + /** + * 判断指定状æ€ï¼Œæ˜¯å¦æœ‰è¿‡ã€å·²ä»˜æ¬¾ã€‘çŠ¶æ€ + * + * @param status æŒ‡å®šçŠ¶æ€ + * @return æ˜¯å¦ + */ + public static boolean havePaid(Integer status) { + return ObjectUtils.equalsAny(status, UNDELIVERED.getStatus(), + DELIVERED.getStatus(), COMPLETED.getStatus()); + } + + /** + * 判断指定状æ€ï¼Œæ˜¯å¦æœ‰è¿‡ã€å·²å‘è´§ã€‘çŠ¶æ€ + * + * @param status æŒ‡å®šçŠ¶æ€ + * @return æ˜¯å¦ + */ + public static boolean haveDelivered(Integer status) { + return ObjectUtils.equalsAny(status, DELIVERED.getStatus(), COMPLETED.getStatus()); + } + +} diff --git a/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderTypeEnum.java b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderTypeEnum.java new file mode 100644 index 0000000..531f3cb --- /dev/null +++ b/tashow-feign/tashow-trade-api/src/main/java/com/tashow/cloud/tradeapi/enums/order/TradeOrderTypeEnum.java @@ -0,0 +1,62 @@ +package com.tashow.cloud.tradeapi.enums.order; + +import cn.hutool.core.util.ObjectUtil; +import com.tashow.cloud.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * äº¤æ˜“è®¢å• - 类型 + * + * @author Sin + */ +@RequiredArgsConstructor +@Getter +public enum TradeOrderTypeEnum implements ArrayValuable { + + NORMAL(0, "普通订å•"), + SECKILL(1, "ç§’æ€è®¢å•"), + BARGAIN(2, "ç ä»·è®¢å•"), + COMBINATION(3, "拼团订å•"), + POINT(4, "积分商城"), + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(TradeOrderTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * 类型å + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static boolean isNormal(Integer type) { + return ObjectUtil.equal(type, NORMAL.getType()); + } + + public static boolean isSeckill(Integer type) { + return ObjectUtil.equal(type, SECKILL.getType()); + } + + public static boolean isBargain(Integer type) { + return ObjectUtil.equal(type, BARGAIN.getType()); + } + + public static boolean isCombination(Integer type) { + return ObjectUtil.equal(type, COMBINATION.getType()); + } + + public static boolean isPoint(Integer type) { + return ObjectUtil.equal(type, POINT.getType()); + } + +} diff --git a/tashow-module/pom.xml b/tashow-module/pom.xml index 37c811b..0ec0ea6 100644 --- a/tashow-module/pom.xml +++ b/tashow-module/pom.xml @@ -14,6 +14,8 @@ tashow-module-system tashow-module-infra tashow-module-app + tashow-module-pay + tashow-module-trade diff --git a/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml b/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml index 15b2156..da745cc 100644 --- a/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml +++ b/tashow-module/tashow-module-infra/src/main/resources/application-local.yaml @@ -7,11 +7,11 @@ spring: username: nacos # Nacos è´¦å· password: nacos # Nacos å¯†ç  discovery: # ã€é…置中心】é…置项 - namespace: 76667956-2ac2-4e05-906b-4bca4ebcc5f0 # 命å空间。这里使用 dev å¼€å‘环境 + namespace: liwq # 命å空间。这里使用 dev å¼€å‘环境 group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # æœåŠ¡å®žä¾‹çš„ç‰ˆæœ¬å·ï¼Œå¯ç”¨äºŽç°åº¦å‘布 config: # ã€æ³¨å†Œä¸­å¿ƒã€‘é…置项 - namespace: 76667956-2ac2-4e05-906b-4bca4ebcc5f0 # 命å空间。这里使用 dev å¼€å‘环境 + namespace: liwq # 命å空间。这里使用 dev å¼€å‘环境 group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP diff --git a/tashow-module/tashow-module-infra/src/main/resources/application.yaml b/tashow-module/tashow-module-infra/src/main/resources/application.yaml index e4e14f6..2fdefa0 100644 --- a/tashow-module/tashow-module-infra/src/main/resources/application.yaml +++ b/tashow-module/tashow-module-infra/src/main/resources/application.yaml @@ -10,4 +10,4 @@ spring: - optional:classpath:application-${spring.profiles.active}.yaml # åŠ è½½ã€æœ¬åœ°ã€‘é…ç½® - optional:nacos:application.yaml # 加载ã€Nacos】的é…ç½® - optional:nacos:tenant.yaml # 加载ã€Nacos】的é…ç½® - - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载ã€Nacos】的é…ç½® + - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载ã€Nacos】的é…ç½® \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/controller.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/controller.vm deleted file mode 100644 index e47b61e..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/controller.vm +++ /dev/null @@ -1,253 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}; - -import org.springframework.web.bind.annotation.*; -import ${jakartaPackage}.annotation.Resource; -import org.springframework.validation.annotation.Validated; -#if ($sceneEnum.scene == 1)import org.springframework.security.access.prepost.PreAuthorize;#end - -import ${jakartaPackage}.validation.constraints.*; -import ${jakartaPackage}.validation.*; -import ${jakartaPackage}.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import ${PageParamClassName}; -import ${PageResultClassName}; -import ${CommonResultClassName}; -import ${BeanUtils}; -import static ${CommonResultClassName}.success; - -import ${ExcelUtilsClassName}; - -import ${ApiAccessLogClassName}; -import static ${OperateTypeEnumClassName}.*; - -import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*; -import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO; -#end -import ${basePackage}.module.${table.moduleName}.service.${table.businessName}.${table.className}Service; - -/** - * ${sceneEnum.name} - ${table.classComment} - */ -@RestController -##二级的 businessName 暂时ä¸ç®—在 HTTP 路径上,å¯ä»¥æ ¹æ®éœ€è¦å†™ -@RequestMapping("/${table.moduleName}/${simpleClassName_strikeCase}") -@Validated -public class ${sceneEnum.prefixClass}${table.className}Controller { - - @Resource - private ${table.className}Service ${classNameVar}Service; - - /** - * 创建${table.classComment} - */ - @PostMapping("/create") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:create')") -#end - public CommonResult<${primaryColumn.javaType}> create${simpleClassName}(@Valid @RequestBody ${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO) { - return success(${classNameVar}Service.create${simpleClassName}(createReqVO)); - } - - /** - * æ›´æ–°${table.classComment} - */ - @PutMapping("/update") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:update')") -#end - public CommonResult update${simpleClassName}(@Valid @RequestBody ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO) { - ${classNameVar}Service.update${simpleClassName}(updateReqVO); - return success(true); - } - - /** - * 删除${table.classComment} - */ - @DeleteMapping("/delete") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')") -#end - public CommonResult delete${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) { - ${classNameVar}Service.delete${simpleClassName}(id); - return success(true); - } - - /** - * 获得${table.classComment} - */ - @GetMapping("/get") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')") -#end - public CommonResult<${sceneEnum.prefixClass}${table.className}RespVO> get${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) { - ${table.className}DO ${classNameVar} = ${classNameVar}Service.get${simpleClassName}(id); - return success(BeanUtils.toBean(${classNameVar}, ${sceneEnum.prefixClass}${table.className}RespVO.class)); - } - -#if ( $table.templateType != 2 ) - - /** - * 获得${table.classComment}分页 - */ - @GetMapping("/page") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')") -#end - public CommonResult> get${simpleClassName}Page(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) { - PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(pageReqVO); - return success(BeanUtils.toBean(pageResult, ${sceneEnum.prefixClass}${table.className}RespVO.class)); - } - -## 特殊:树表专属逻辑(树ä¸éœ€è¦åˆ†é¡µæŽ¥å£ï¼‰ -#else - /** - * 获得${table.classComment}列表 - */ - @GetMapping("/list") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')") -#end - public CommonResult> get${simpleClassName}List(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) { - List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO); - return success(BeanUtils.toBean(list, ${sceneEnum.prefixClass}${table.className}RespVO.class)); - } - -#end - /** - * 导出${table.classComment} Excel - */ - @GetMapping("/export-excel") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:export')") -#end - @ApiAccessLog(operateType = EXPORT) -#if ( $table.templateType != 2 ) - public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}Page(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "${table.classComment}.xls", "æ•°æ®", ${sceneEnum.prefixClass}${table.className}RespVO.class, - BeanUtils.toBean(list, ${sceneEnum.prefixClass}${table.className}RespVO.class)); - } -## 特殊:树表专属逻辑(树ä¸éœ€è¦åˆ†é¡µæŽ¥å£ï¼‰ -#else - public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO, - HttpServletResponse response) throws IOException { - List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO); - // 导出 Excel - ExcelUtils.write(response, "${table.classComment}.xls", "æ•°æ®", ${table.className}RespVO.class, - BeanUtils.toBean(list, ${table.className}RespVO.class)); - } -#end - -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) -#set ($subSimpleClassName = $subSimpleClassNames.get($index)) -#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##å½“å‰ primary 字段 -#set ($subJoinColumn = $subJoinColumns.get($index))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ -#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index)) -#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index)) -#set ($subClassNameVar = $subClassNameVars.get($index)) - // ==================== å­è¡¨ï¼ˆ$subTable.classComment) ==================== - -## 情况一:MASTER_ERP 时,需è¦åˆ†æŸ¥è¯¢é¡µå­è¡¨ -#if ( $table.templateType == 11 ) - /** - * 获得${subTable.classComment}分页 - */ - @GetMapping("/${subSimpleClassName_strikeCase}/page") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')") -#end - public CommonResult> get${subSimpleClassName}Page(PageParam pageReqVO, - @RequestParam("${subJoinColumn.javaField}") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return success(${classNameVar}Service.get${subSimpleClassName}Page(pageReqVO, ${subJoinColumn.javaField})); - } - -## æƒ…å†µäºŒï¼šéž MASTER_ERP 时,需è¦åˆ—表查询å­è¡¨ -#else - #if ( $subTable.subJoinMany ) - /** - * 获得${subTable.classComment}列表 - */ - @GetMapping("/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')") -#end - public CommonResult> get${subSimpleClassName}ListBy${SubJoinColumnName}(@RequestParam("${subJoinColumn.javaField}") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return success(${classNameVar}Service.get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField})); - } - - #else - /** - * 获得${subTable.classComment} - */ - @GetMapping("/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')") -#end - public CommonResult<${subTable.className}DO> get${subSimpleClassName}By${SubJoinColumnName}(@RequestParam("${subJoinColumn.javaField}") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return success(${classNameVar}Service.get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField})); - } - - #end -#end -## 特殊:MASTER_ERP 时,支æŒå•个的新增ã€ä¿®æ”¹ã€åˆ é™¤æ“作 -#if ( $table.templateType == 11 ) - /** - * 创建${subTable.classComment} - */ - @PostMapping("/${subSimpleClassName_strikeCase}/create") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:create')") -#end - public CommonResult<${subPrimaryColumn.javaType}> create${subSimpleClassName}(@Valid @RequestBody ${subTable.className}DO ${subClassNameVar}) { - return success(${classNameVar}Service.create${subSimpleClassName}(${subClassNameVar})); - } - - /** - * æ›´æ–°${subTable.classComment} - */ - @PutMapping("/${subSimpleClassName_strikeCase}/update") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:update')") -#end - public CommonResult update${subSimpleClassName}(@Valid @RequestBody ${subTable.className}DO ${subClassNameVar}) { - ${classNameVar}Service.update${subSimpleClassName}(${subClassNameVar}); - return success(true); - } - - /** - * 删除${subTable.classComment} - */ - @DeleteMapping("/${subSimpleClassName_strikeCase}/delete") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')") -#end - public CommonResult delete${subSimpleClassName}(@RequestParam("id") ${subPrimaryColumn.javaType} id) { - ${classNameVar}Service.delete${subSimpleClassName}(id); - return success(true); - } - - /** - * 获得${subTable.classComment} - */ - @GetMapping("/${subSimpleClassName_strikeCase}/get") -#if ($sceneEnum.scene == 1) - @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')") -#end - public CommonResult<${subTable.className}DO> get${subSimpleClassName}(@RequestParam("id") ${subPrimaryColumn.javaType} id) { - return success(${classNameVar}Service.get${subSimpleClassName}(id)); - } - -#end -#end -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/listReqVO.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/listReqVO.vm deleted file mode 100644 index 4797c0b..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/listReqVO.vm +++ /dev/null @@ -1,51 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo; - -import lombok.*; -import java.util.*; - -import ${PageParamClassName}; -#foreach ($column in $columns) -#if (${column.javaType} == "BigDecimal") -import java.math.BigDecimal; -#break -#end -#end -## å¤„ç† LocalDateTime 字段的引入 -#foreach ($column in $columns) -#if (${column.listOperation} && ${column.javaType} == "LocalDateTime") -import java.time.LocalDateTime; -import org.springframework.format.annotation.DateTimeFormat; - -import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -#break -#end -#end -## å­—æ®µæ¨¡æ¿ -#macro(columnTpl $prefix $prefixStr) - /** - * ${prefixStr}${column.columnComment}"#if ("$!column.example" != " - */, example = "${column.example}"#end) - private ${column.javaType}#if ("$!prefix" != "") ${prefix}${JavaField}#else ${column.javaField}#end; -#end - -/** - * ${sceneEnum.name} - ${table.classComment}列表 Request VO - */ -@Data -public class ${sceneEnum.prefixClass}${table.className}ListReqVO { - -#foreach ($column in $columns) -#if (${column.listOperation})##查询æ“作 -#if (${column.listOperationCondition} == "BETWEEN")## 情况一,Between 的时候 - /** - * ${column.columnComment}"#if ("$!column.example" != " - */, example = "${column.example}"#end) - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private ${column.javaType}[] ${column.javaField}; -#else##æƒ…å†µäºŒï¼Œéž Between 的时间 - #columnTpl('', '') -#end - -#end -#end -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/pageReqVO.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/pageReqVO.vm deleted file mode 100644 index f7841c0..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/pageReqVO.vm +++ /dev/null @@ -1,53 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo; - -import lombok.*; -import java.util.*; - -import ${PageParamClassName}; -#foreach ($column in $columns) -#if (${column.javaType} == "BigDecimal") -import java.math.BigDecimal; -#break -#end -#end -## å¤„ç† LocalDateTime 字段的引入 -#foreach ($column in $columns) -#if (${column.listOperationCondition} && ${column.javaType} == "LocalDateTime") -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -#break -#end -#end -## å­—æ®µæ¨¡æ¿ -#macro(columnTpl $prefix $prefixStr) - /** - * ${prefixStr}${column.columnComment}"#if ("$!column.example" != " - */, example = "${column.example}"#end) - private ${column.javaType}#if ("$!prefix" != "") ${prefix}${JavaField}#else ${column.javaField}#end; -#end - -/** - * ${sceneEnum.name} - ${table.classComment}分页 Request VO - */ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class ${sceneEnum.prefixClass}${table.className}PageReqVO extends PageParam { - -#foreach ($column in $columns) -#if (${column.listOperation})##查询æ“作 -#if (${column.listOperationCondition} == "BETWEEN")## 情况一,Between 的时候 - /** - * ${column.columnComment}"#if ("$!column.example" != " - */, example = "${column.example}"#end) - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private ${column.javaType}[] ${column.javaField}; -#else##æƒ…å†µäºŒï¼Œéž Between 的时间 - #columnTpl('', '') -#end - -#end -#end -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm deleted file mode 100644 index e4fcf6f..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm +++ /dev/null @@ -1,57 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo; - - -import lombok.*; -import java.util.*; -## å¤„ç† BigDecimal 字段的引入 -#foreach ($column in $columns) -#if (${column.javaType} == "BigDecimal") -import java.math.BigDecimal; -#break -#end -#end -## å¤„ç† LocalDateTime 字段的引入 -#foreach ($column in $columns) -#if (${column.listOperationResult} && ${column.javaType} == "LocalDateTime") -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -#break -#end -#end -## å¤„ç† Excel 导出 -import com.alibaba.excel.annotation.*; -#foreach ($column in $columns) -#if ("$!column.dictType" != "")## 有设置数æ®å­—å…¸ -import ${DictFormatClassName}; -import ${DictConvertClassName}; -#break -#end -#end - -/** - * ${sceneEnum.name} - ${table.classComment} Response VO - */ -@Data -@ExcelIgnoreUnannotated -public class ${sceneEnum.prefixClass}${table.className}RespVO { - -## é€ä¸ªå¤„ç†å­—段 -#foreach ($column in $columns) -#if (${column.listOperationResult}) -## 1. å¤„ç† Swagger 注解 - /** - * ${column.columnComment}"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if ("$!column.example" != " - */, example = "${column.example}"#end) -## 2. å¤„ç† Excel 导出 -#if ("$!column.dictType" != "")##å¤„ç†æžšä¸¾å€¼ - @ExcelProperty(value = "${column.columnComment}", converter = DictConvert.class) - @DictFormat("${column.dictType}") // TODO 代ç ä¼˜åŒ–:建议设置到对应的 DictTypeConstants 枚举类中 -#else - @ExcelProperty("${column.columnComment}") -#end -## 3. 处ç†å­—段定义 - private ${column.javaType} ${column.javaField}; - -#end -#end -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/saveReqVO.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/saveReqVO.vm deleted file mode 100644 index ba32472..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/controller/vo/saveReqVO.vm +++ /dev/null @@ -1,72 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo; - - -import lombok.*; -import java.util.*; -import ${jakartaPackage}.validation.constraints.*; -## å¤„ç† BigDecimal 字段的引入 -#foreach ($column in $columns) -#if (${column.javaType} == "BigDecimal") -import java.math.BigDecimal; -#break -#end -#end -## å¤„ç† LocalDateTime 字段的引入 -#foreach ($column in $columns) -#if ((${column.createOperation} || ${column.updateOperation}) && ${column.javaType} == "LocalDateTime") -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -#break -#end -#end -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO; -#end - -/** - * ${sceneEnum.name} - ${table.classComment}新增/修改 Request VO - */ -@Data -public class ${sceneEnum.prefixClass}${table.className}SaveReqVO { - -## é€ä¸ªå¤„ç†å­—段 -#foreach ($column in $columns) -#if (${column.createOperation} || ${column.updateOperation}) -## 1. å¤„ç† Swagger 注解 - /** - * ${column.columnComment}"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if ("$!column.example" != " - */, example = "${column.example}"#end) -## 2. å¤„ç† Validator 傿•°æ ¡éªŒ -#if (!${column.nullable} && !${column.primaryKey}) -#if (${column.javaType} == 'String') - @NotEmpty(message = "${column.columnComment}ä¸èƒ½ä¸ºç©º") -#else - @NotNull(message = "${column.columnComment}ä¸èƒ½ä¸ºç©º") -#end -#end -## 3. 处ç†å­—段定义 - private ${column.javaType} ${column.javaField}; - -#end -#end -## 特殊:主å­è¡¨ä¸“å±žé€»è¾‘ï¼ˆéž ERP 模å¼ï¼‰ -#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 ) -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) - #if ( $subTable.subJoinMany) - /** - * ${subTable.classComment}列表 - */ - private List<${subTable.className}DO> ${subClassNameVars.get($index)}s; - - #else - /** - * ${subTable.classComment} - */ - private ${subTable.className}DO ${subClassNameVars.get($index)}; - - #end -#end -#end -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/do.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/do.vm deleted file mode 100644 index b019d6e..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/do.vm +++ /dev/null @@ -1,52 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}; - -import lombok.*; -import java.util.*; -#foreach ($column in $columns) -#if (${column.javaType} == "BigDecimal") -import java.math.BigDecimal; -#end -#if (${column.javaType} == "LocalDateTime") -import java.time.LocalDateTime; -#end -#end -import com.baomidou.mybatisplus.annotation.*; -import ${BaseDOClassName}; - -/** - * ${table.classComment} DO - * - * @author ${table.author} - */ -@TableName("${table.tableName.toLowerCase()}") -@KeySequence("${table.tableName.toLowerCase()}_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ${table.className}DO extends BaseDO { - -## 特殊:树表专属逻辑 -#if ( $table.templateType == 2 ) - public static final Long ${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT = 0L; - -#end -#foreach ($column in $columns) -#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段 - /** - * ${column.columnComment} - #if ("$!column.dictType" != "")##å¤„ç†æžšä¸¾å€¼ - * - * 枚举 {@link TODO ${column.dictType} 对应的类} - #end - */ - #if (${column.primaryKey})##处ç†ä¸»é”® - @TableId#if (${column.javaType} == 'String')(type = IdType.INPUT)#end - #end - private ${column.javaType} ${column.javaField}; -#end -#end - -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/do_sub.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/do_sub.vm deleted file mode 100644 index 16be55e..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/do_sub.vm +++ /dev/null @@ -1,49 +0,0 @@ -#set ($subTable = $subTables.get($subIndex))##当å‰è¡¨ -#set ($subColumns = $subColumnsList.get($subIndex))##当å‰å­—段数组 -package ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}; - -import lombok.*; -import java.util.*; -#foreach ($column in $subColumns) -#if (${column.javaType} == "BigDecimal") -import java.math.BigDecimal; -#end -#if (${column.javaType} == "LocalDateTime") -import java.time.LocalDateTime; -#end -#end -import com.baomidou.mybatisplus.annotation.*; -import ${BaseDOClassName}; - -/** - * ${subTable.classComment} DO - * - * @author ${subTable.author} - */ -@TableName("${subTable.tableName.toLowerCase()}") -@KeySequence("${subTable.tableName.toLowerCase()}_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ${subTable.className}DO extends BaseDO { - -#foreach ($column in $subColumns) -#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段 - /** - * ${column.columnComment} - #if ("$!column.dictType" != "")##å¤„ç†æžšä¸¾å€¼ - * - * 枚举 {@link TODO ${column.dictType} 对应的类} - #end - */ - #if (${column.primaryKey})##处ç†ä¸»é”® - @TableId#if (${column.javaType} == 'String')(type = IdType.INPUT)#end - #end - private ${column.javaType} ${column.javaField}; -#end -#end - -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper.vm deleted file mode 100644 index b98b471..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper.vm +++ /dev/null @@ -1,82 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}; - -import java.util.*; - -import ${PageResultClassName}; -import ${QueryWrapperClassName}; -import ${BaseMapperClassName}; -import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; -import org.apache.ibatis.annotations.Mapper; -import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*; - -## å­—æ®µæ¨¡æ¿ -#macro(listCondition) -#foreach ($column in $columns) -#if (${column.listOperation}) -#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字æ¯å¤§å†™ -#if (${column.listOperationCondition} == "=")##情况一,= 的时候 - .eqIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}()) -#end -#if (${column.listOperationCondition} == "!=")##情况二,!= 的时候 - .neIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}()) -#end -#if (${column.listOperationCondition} == ">")##情况三,> 的时候 - .gtIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}()) -#end -#if (${column.listOperationCondition} == ">=")##情况四,>= 的时候 - .geIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}()) -#end -#if (${column.listOperationCondition} == "<")##情况五,< 的时候 - .ltIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}()) -#end -#if (${column.listOperationCondition} == "<=")##情况五,<= 的时候 - .leIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}()) -#end -#if (${column.listOperationCondition} == "LIKE")##情况七,Like 的时候 - .likeIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}()) -#end -#if (${column.listOperationCondition} == "BETWEEN")##情况八,Between 的时候 - .betweenIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}()) -#end -#end -#end -#end -/** - * ${table.classComment} Mapper - * - * @author ${table.author} - */ -@Mapper -public interface ${table.className}Mapper extends BaseMapperX<${table.className}DO> { - -## 特殊:树表专属逻辑(树ä¸éœ€è¦åˆ†é¡µæŽ¥å£ï¼‰ -#if ( $table.templateType != 2 ) - default PageResult<${table.className}DO> selectPage(${sceneEnum.prefixClass}${table.className}PageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX<${table.className}DO>() - #listCondition() - .orderByDesc(${table.className}DO::getId));## 大多数情况下,id å€’åº - - } -#else - default List<${table.className}DO> selectList(${sceneEnum.prefixClass}${table.className}ListReqVO reqVO) { - return selectList(new LambdaQueryWrapperX<${table.className}DO>() - #listCondition() - .orderByDesc(${table.className}DO::getId));## 大多数情况下,id å€’åº - - } -#end - -## 特殊:树表专属逻辑 -#if ( $table.templateType == 2 ) -#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字æ¯å¤§å†™ -#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - default ${table.className}DO selectBy${TreeParentJavaField}And${TreeNameJavaField}(Long ${treeParentColumn.javaField}, String ${treeNameColumn.javaField}) { - return selectOne(${table.className}DO::get${TreeParentJavaField}, ${treeParentColumn.javaField}, ${table.className}DO::get${TreeNameJavaField}, ${treeNameColumn.javaField}); - } - - default Long selectCountBy${TreeParentJavaField}(${treeParentColumn.javaType} ${treeParentColumn.javaField}) { - return selectCount(${table.className}DO::get${TreeParentJavaField}, ${treeParentColumn.javaField}); - } - -#end -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper.xml.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper.xml.vm deleted file mode 100644 index 290378d..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper.xml.vm +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper_sub.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper_sub.vm deleted file mode 100644 index 6ccaea7..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/dal/mapper_sub.vm +++ /dev/null @@ -1,57 +0,0 @@ -#set ($subTable = $subTables.get($subIndex))##当å‰è¡¨ -#set ($subColumns = $subJoinColumnsList.get($subIndex))##当å‰å­—段数组 -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ -package ${basePackage}.module.${subTable.moduleName}.dal.mysql.${subTable.businessName}; - -import java.util.*; - -import ${PageResultClassName}; -import ${PageParamClassName}; -import ${QueryWrapperClassName}; -import ${BaseMapperClassName}; -import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO; -import org.apache.ibatis.annotations.Mapper; - -/** - * ${subTable.classComment} Mapper - * - * @author ${subTable.author} - */ -@Mapper -public interface ${subTable.className}Mapper extends BaseMapperX<${subTable.className}DO> { - -## 情况一:MASTER_ERP 时,需è¦åˆ†æŸ¥è¯¢é¡µå­è¡¨ -#if ( $table.templateType == 11 ) - default PageResult<${subTable.className}DO> selectPage(PageParam reqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return selectPage(reqVO, new LambdaQueryWrapperX<${subTable.className}DO>() - .eq(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField}) - .orderByDesc(${subTable.className}DO::getId));## 大多数情况下,id å€’åº - - } -## 主表与å­è¡¨æ˜¯ä¸€å¯¹ä¸€æ—¶ - #if (!$subTable.subJoinMany) - default ${subTable.className}DO selectBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return selectOne(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField}); - } - #end - -## æƒ…å†µäºŒï¼šéž MASTER_ERP 时,需è¦åˆ—表查询å­è¡¨ -#else - #if ( $subTable.subJoinMany) - default List<${subTable.className}DO> selectListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return selectList(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField}); - } - - #else - default ${subTable.className}DO selectBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return selectOne(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField}); - } - - #end - #end - default int deleteBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return delete(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField}); - } - -} diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/enums/errorcode.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/enums/errorcode.vm deleted file mode 100644 index b7e21e6..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/enums/errorcode.vm +++ /dev/null @@ -1,22 +0,0 @@ -// TODO 待办:请将下é¢çš„错误ç å¤åˆ¶åˆ° yudao-module-${table.moduleName}-api 模å—çš„ ErrorCodeConstants 类中。注æ„,请给“TODO 补充编å·â€è®¾ç½®ä¸€ä¸ªé”™è¯¯ç ç¼–å·ï¼ï¼ï¼ -// ========== ${table.classComment} TODO è¡¥å……ç¼–å· ========== -ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编å·, "${table.classComment}ä¸å­˜åœ¨"); -## 特殊:树表专属逻辑 -#if ( $table.templateType == 2 ) -ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN = new ErrorCode(TODO 补充编å·, "存在存在å­${table.classComment},无法删除"); -ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编å·,"父级${table.classComment}ä¸å­˜åœ¨"); -ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_ERROR = new ErrorCode(TODO 补充编å·, "ä¸èƒ½è®¾ç½®è‡ªå·±ä¸ºçˆ¶${table.classComment}"); -ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE = new ErrorCode(TODO 补充编å·, "å·²ç»å­˜åœ¨è¯¥${treeNameColumn.columnComment}çš„${table.classComment}"); -ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_IS_CHILD = new ErrorCode(TODO 补充编å·, "ä¸èƒ½è®¾ç½®è‡ªå·±çš„å­${table.className}为父${table.className}"); -#end -## 特殊:主å­è¡¨ä¸“属逻辑 -#if ( $table.templateType == 11 )## 特殊:ERP 情况 -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) -#set ($simpleClassNameUnderlineCase = $simpleClassNameUnderlineCases.get($index)) -ErrorCode ${simpleClassNameUnderlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编å·, "${subTable.classComment}ä¸å­˜åœ¨"); -#if ( !$subTable.subJoinMany ) -ErrorCode ${simpleClassNameUnderlineCase.toUpperCase()}_EXISTS = new ErrorCode(TODO 补充编å·, "${subTable.classComment}已存在"); -#end -#end -#end \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/service/service.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/service/service.vm deleted file mode 100644 index c4ee4f0..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/service/service.vm +++ /dev/null @@ -1,147 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.service.${table.businessName}; - -import java.util.*; -import ${jakartaPackage}.validation.*; -import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*; -import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO; -#end -import ${PageResultClassName}; -import ${PageParamClassName}; - -/** - * ${table.classComment} Service æŽ¥å£ - * - * @author ${table.author} - */ -public interface ${table.className}Service { - - /** - * 创建${table.classComment} - * - * @param createReqVO åˆ›å»ºä¿¡æ¯ - * @return ç¼–å· - */ - ${primaryColumn.javaType} create${simpleClassName}(@Valid ${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO); - - /** - * æ›´æ–°${table.classComment} - * - * @param updateReqVO æ›´æ–°ä¿¡æ¯ - */ - void update${simpleClassName}(@Valid ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO); - - /** - * 删除${table.classComment} - * - * @param id ç¼–å· - */ - void delete${simpleClassName}(${primaryColumn.javaType} id); - - /** - * 获得${table.classComment} - * - * @param id ç¼–å· - * @return ${table.classComment} - */ - ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id); - -## 特殊:树表专属逻辑(树ä¸éœ€è¦åˆ†é¡µæŽ¥å£ï¼‰ -#if ( $table.templateType != 2 ) - /** - * 获得${table.classComment}分页 - * - * @param pageReqVO 分页查询 - * @return ${table.classComment}分页 - */ - PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO); -#else - /** - * 获得${table.classComment}列表 - * - * @param listReqVO 查询æ¡ä»¶ - * @return ${table.classComment}列表 - */ - List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO); -#end - -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) -#set ($subSimpleClassName = $subSimpleClassNames.get($index)) -#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##å½“å‰ primary 字段 -#set ($subJoinColumn = $subJoinColumns.get($index))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ -#set ($subClassNameVar = $subClassNameVars.get($index)) - // ==================== å­è¡¨ï¼ˆ$subTable.classComment) ==================== - -## 情况一:MASTER_ERP 时,需è¦åˆ†æŸ¥è¯¢é¡µå­è¡¨ -#if ( $table.templateType == 11 ) - /** - * 获得${subTable.classComment}分页 - * - * @param pageReqVO 分页查询 - * @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment} - * @return ${subTable.classComment}分页 - */ - PageResult<${subTable.className}DO> get${subSimpleClassName}Page(PageParam pageReqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField}); - -## æƒ…å†µäºŒï¼šéž MASTER_ERP 时,需è¦åˆ—表查询å­è¡¨ -#else - #if ( $subTable.subJoinMany ) - /** - * 获得${subTable.classComment}列表 - * - * @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment} - * @return ${subTable.classComment}列表 - */ - List<${subTable.className}DO> get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}); - - #else - /** - * 获得${subTable.classComment} - * - * @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment} - * @return ${subTable.classComment} - */ - ${subTable.className}DO get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}); - - #end -#end -## 特殊:MASTER_ERP 时,支æŒå•个的新增ã€ä¿®æ”¹ã€åˆ é™¤æ“作 -#if ( $table.templateType == 11 ) - /** - * 创建${subTable.classComment} - * - * @param ${subClassNameVar} åˆ›å»ºä¿¡æ¯ - * @return ç¼–å· - */ - ${subPrimaryColumn.javaType} create${subSimpleClassName}(@Valid ${subTable.className}DO ${subClassNameVar}); - - /** - * æ›´æ–°${subTable.classComment} - * - * @param ${subClassNameVar} æ›´æ–°ä¿¡æ¯ - */ - void update${subSimpleClassName}(@Valid ${subTable.className}DO ${subClassNameVar}); - - /** - * 删除${subTable.classComment} - * - * @param id ç¼–å· - */ - void delete${subSimpleClassName}(${subPrimaryColumn.javaType} id); - - /** - * 获得${subTable.classComment} - * - * @param id ç¼–å· - * @return ${subTable.classComment} - */ - ${subTable.className}DO get${subSimpleClassName}(${subPrimaryColumn.javaType} id); - -#end -#end -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm deleted file mode 100644 index 80bc71b..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm +++ /dev/null @@ -1,351 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.service.${table.businessName}; - -import org.springframework.stereotype.Service; -import ${jakartaPackage}.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*; -import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO; -#end -import ${PageResultClassName}; -import ${PageParamClassName}; -import ${BeanUtils}; - -import ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper; -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) -import ${basePackage}.module.${subTable.moduleName}.dal.mysql.${subTable.businessName}.${subTable.className}Mapper; -#end - -import static ${ServiceExceptionUtilClassName}.exception; -import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*; - -/** - * ${table.classComment} Service 实现类 - * - * @author ${table.author} - */ -@Service -@Validated -public class ${table.className}ServiceImpl implements ${table.className}Service { - - @Resource - private ${table.className}Mapper ${classNameVar}Mapper; -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) - @Resource - private ${subTable.className}Mapper ${subClassNameVars.get($index)}Mapper; -#end - - @Override -## 特殊:主å­è¡¨ä¸“å±žé€»è¾‘ï¼ˆéž ERP 模å¼ï¼‰ -#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 ) - @Transactional(rollbackFor = Exception.class) -#end - public ${primaryColumn.javaType} create${simpleClassName}(${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO) { -## 特殊:树表专属逻辑 -#if ( $table.templateType == 2 ) -#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字æ¯å¤§å†™ -#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - // 校验${treeParentColumn.columnComment}的有效性 - validateParent${simpleClassName}(null, createReqVO.get${TreeParentJavaField}()); - // 校验${treeNameColumn.columnComment}的唯一性 - validate${simpleClassName}${TreeNameJavaField}Unique(null, createReqVO.get${TreeParentJavaField}(), createReqVO.get${TreeNameJavaField}()); - -#end - // æ’å…¥ - ${table.className}DO ${classNameVar} = BeanUtils.toBean(createReqVO, ${table.className}DO.class); - ${classNameVar}Mapper.insert(${classNameVar}); -## 特殊:主å­è¡¨ä¸“å±žé€»è¾‘ï¼ˆéž ERP 模å¼ï¼‰ -#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 ) - - // æ’å…¥å­è¡¨ -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) -#set ($subSimpleClassName = $subSimpleClassNames.get($index)) -#set ($subJoinColumn = $subJoinColumns.get($index))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - #if ( $subTable.subJoinMany) - create${subSimpleClassName}List(${classNameVar}.getId(), createReqVO.get${subSimpleClassNames.get($index)}s()); - #else - create${subSimpleClassName}(${classNameVar}.getId(), createReqVO.get${subSimpleClassNames.get($index)}()); - #end -#end -#end - // 返回 - return ${classNameVar}.getId(); - } - - @Override -## 特殊:主å­è¡¨ä¸“å±žé€»è¾‘ï¼ˆéž ERP 模å¼ï¼‰ -#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 ) - @Transactional(rollbackFor = Exception.class) -#end - public void update${simpleClassName}(${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO) { - // 校验存在 - validate${simpleClassName}Exists(updateReqVO.getId()); -## 特殊:树表专属逻辑 -#if ( $table.templateType == 2 ) -#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字æ¯å¤§å†™ -#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - // 校验${treeParentColumn.columnComment}的有效性 - validateParent${simpleClassName}(updateReqVO.getId(), updateReqVO.get${TreeParentJavaField}()); - // 校验${treeNameColumn.columnComment}的唯一性 - validate${simpleClassName}${TreeNameJavaField}Unique(updateReqVO.getId(), updateReqVO.get${TreeParentJavaField}(), updateReqVO.get${TreeNameJavaField}()); - -#end - // æ›´æ–° - ${table.className}DO updateObj = BeanUtils.toBean(updateReqVO, ${table.className}DO.class); - ${classNameVar}Mapper.updateById(updateObj); -## 特殊:主å­è¡¨ä¸“å±žé€»è¾‘ï¼ˆéž ERP 模å¼ï¼‰ -#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11) - - // æ›´æ–°å­è¡¨ -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) -#set ($subSimpleClassName = $subSimpleClassNames.get($index)) -#set ($subJoinColumn = $subJoinColumns.get($index))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - #if ( $subTable.subJoinMany) - update${subSimpleClassName}List(updateReqVO.getId(), updateReqVO.get${subSimpleClassNames.get($index)}s()); - #else - update${subSimpleClassName}(updateReqVO.getId(), updateReqVO.get${subSimpleClassNames.get($index)}()); - #end -#end -#end - } - - @Override -## 特殊:主å­è¡¨ä¸“属逻辑 -#if ( $subTables && $subTables.size() > 0) - @Transactional(rollbackFor = Exception.class) -#end - public void delete${simpleClassName}(${primaryColumn.javaType} id) { - // 校验存在 - validate${simpleClassName}Exists(id); -## 特殊:树表专属逻辑 -#if ( $table.templateType == 2 ) -#set ($ParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - // æ ¡éªŒæ˜¯å¦æœ‰å­${table.classComment} - if (${classNameVar}Mapper.selectCountBy${ParentJavaField}(id) > 0) { - throw exception(${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN); - } -#end - // 删除 - ${classNameVar}Mapper.deleteById(id); -## 特殊:主å­è¡¨ä¸“属逻辑 -#if ( $subTables && $subTables.size() > 0) - - // 删除å­è¡¨ -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) -#set ($subSimpleClassName = $subSimpleClassNames.get($index)) -#set ($subJoinColumn = $subJoinColumns.get($index))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - delete${subSimpleClassName}By${SubJoinColumnName}(id); -#end -#end - } - - private void validate${simpleClassName}Exists(${primaryColumn.javaType} id) { - if (${classNameVar}Mapper.selectById(id) == null) { - throw exception(${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS); - } - } - -## 特殊:树表专属逻辑 -#if ( $table.templateType == 2 ) -#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字æ¯å¤§å†™ -#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - private void validateParent${simpleClassName}(Long id, Long ${treeParentColumn.javaField}) { - if (${treeParentColumn.javaField} == null || ${simpleClassName}DO.${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT.equals(${treeParentColumn.javaField})) { - return; - } - // 1. ä¸èƒ½è®¾ç½®è‡ªå·±ä¸ºçˆ¶${table.classComment} - if (Objects.equals(id, ${treeParentColumn.javaField})) { - throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_ERROR); - } - // 2. 父${table.classComment}ä¸å­˜åœ¨ - ${simpleClassName}DO parent${simpleClassName} = ${classNameVar}Mapper.selectById(${treeParentColumn.javaField}); - if (parent${simpleClassName} == null) { - throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_NOT_EXITS); - } - // 3. 递归校验父${table.classComment},如果父${table.classComment}是自己的å­${table.classComment},则报错,é¿å…å½¢æˆçŽ¯è·¯ - if (id == null) { // id 为空,说明新增,ä¸éœ€è¦è€ƒè™‘环路 - return; - } - for (int i = 0; i < Short.MAX_VALUE; i++) { - // 3.1 校验环路 - ${treeParentColumn.javaField} = parent${simpleClassName}.get${TreeParentJavaField}(); - if (Objects.equals(id, ${treeParentColumn.javaField})) { - throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_IS_CHILD); - } - // 3.2 继续递归下一级父${table.classComment} - if (${treeParentColumn.javaField} == null || ${simpleClassName}DO.${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT.equals(${treeParentColumn.javaField})) { - break; - } - parent${simpleClassName} = ${classNameVar}Mapper.selectById(${treeParentColumn.javaField}); - if (parent${simpleClassName} == null) { - break; - } - } - } - - private void validate${simpleClassName}${TreeNameJavaField}Unique(Long id, Long ${treeParentColumn.javaField}, String ${treeNameColumn.javaField}) { - ${simpleClassName}DO ${classNameVar} = ${classNameVar}Mapper.selectBy${TreeParentJavaField}And${TreeNameJavaField}(${treeParentColumn.javaField}, ${treeNameColumn.javaField}); - if (${classNameVar} == null) { - return; - } - // 如果 id 为空,说明ä¸ç”¨æ¯”较是å¦ä¸ºç›¸åŒ id çš„${table.classComment} - if (id == null) { - throw exception(${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE); - } - if (!Objects.equals(${classNameVar}.getId(), id)) { - throw exception(${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE); - } - } - -#end - @Override - public ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id) { - return ${classNameVar}Mapper.selectById(id); - } - -## 特殊:树表专属逻辑(树ä¸éœ€è¦åˆ†é¡µæŽ¥å£ï¼‰ -#if ( $table.templateType != 2 ) - @Override - public PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) { - return ${classNameVar}Mapper.selectPage(pageReqVO); - } -#else - @Override - public List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) { - return ${classNameVar}Mapper.selectList(listReqVO); - } -#end - -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) -#set ($subSimpleClassName = $subSimpleClassNames.get($index)) -#set ($simpleClassNameUnderlineCase = $simpleClassNameUnderlineCases.get($index)) -#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##å½“å‰ primary 字段 -#set ($subJoinColumn = $subJoinColumns.get($index))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ -#set ($subClassNameVar = $subClassNameVars.get($index)) - // ==================== å­è¡¨ï¼ˆ$subTable.classComment) ==================== - -## 情况一:MASTER_ERP 时,需è¦åˆ†æŸ¥è¯¢é¡µå­è¡¨ -#if ( $table.templateType == 11 ) - @Override - public PageResult<${subTable.className}DO> get${subSimpleClassName}Page(PageParam pageReqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return ${subClassNameVars.get($index)}Mapper.selectPage(pageReqVO, ${subJoinColumn.javaField}); - } - -## æƒ…å†µäºŒï¼šéž MASTER_ERP 时,需è¦åˆ—表查询å­è¡¨ -#else - #if ( $subTable.subJoinMany ) - @Override - public List<${subTable.className}DO> get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return ${subClassNameVars.get($index)}Mapper.selectListBy${SubJoinColumnName}(${subJoinColumn.javaField}); - } - - #else - @Override - public ${subTable.className}DO get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) { - return ${subClassNameVars.get($index)}Mapper.selectBy${SubJoinColumnName}(${subJoinColumn.javaField}); - } - - #end -#end -## 情况一:MASTER_ERP 时,支æŒå•个的新增ã€ä¿®æ”¹ã€åˆ é™¤æ“作 -#if ( $table.templateType == 11 ) - @Override - public ${subPrimaryColumn.javaType} create${subSimpleClassName}(${subTable.className}DO ${subClassNameVar}) { -## 特殊:一对一时,需è¦ä¿è¯åªæœ‰ä¸€æ¡ï¼Œä¸èƒ½é‡å¤æ’å…¥ -#if ( !$subTable.subJoinMany) - // 校验是å¦å·²ç»å­˜åœ¨ - if (${subClassNameVars.get($index)}Mapper.selectBy${SubJoinColumnName}(${subClassNameVar}.get${SubJoinColumnName}()) != null) { - throw exception(${simpleClassNameUnderlineCase.toUpperCase()}_EXISTS); - } - // æ’å…¥ -#end - ${subClassNameVars.get($index)}Mapper.insert(${subClassNameVar}); - return ${subClassNameVar}.getId(); - } - - @Override - public void update${subSimpleClassName}(${subTable.className}DO ${subClassNameVar}) { - // 校验存在 - validate${subSimpleClassName}Exists(${subClassNameVar}.getId()); - // æ›´æ–° - ${subClassNameVar}.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 䏿›´æ–° - ${subClassNameVars.get($index)}Mapper.updateById(${subClassNameVar}); - } - - @Override - public void delete${subSimpleClassName}(${subPrimaryColumn.javaType} id) { - // 校验存在 - validate${subSimpleClassName}Exists(id); - // 删除 - ${subClassNameVars.get($index)}Mapper.deleteById(id); - } - - @Override - public ${subTable.className}DO get${subSimpleClassName}(${subPrimaryColumn.javaType} id) { - return ${subClassNameVars.get($index)}Mapper.selectById(id); - } - - private void validate${subSimpleClassName}Exists(${subPrimaryColumn.javaType} id) { - if (${subClassNameVar}Mapper.selectById(id) == null) { - throw exception(${simpleClassNameUnderlineCase.toUpperCase()}_NOT_EXISTS); - } - } - -## æƒ…å†µäºŒï¼šéž MASTER_ERP æ—¶ï¼Œæ”¯æŒæ‰¹é‡çš„æ–°å¢žã€ä¿®æ”¹æ“作 -#else - #if ( $subTable.subJoinMany) - private void create${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) { - list.forEach(o -> o.set$SubJoinColumnName(${subJoinColumn.javaField})); - ${subClassNameVars.get($index)}Mapper.insertBatch(list); - } - - private void update${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) { - delete${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}); - list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲çªï¼›2)updateTime 䏿›´æ–° - create${subSimpleClassName}List(${subJoinColumn.javaField}, list); - } - - #else - private void create${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) { - if (${subClassNameVar} == null) { - return; - } - ${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField}); - ${subClassNameVars.get($index)}Mapper.insert(${subClassNameVar}); - } - - private void update${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) { - if (${subClassNameVar} == null) { - return; - } - ${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField}); - ${subClassNameVar}.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 䏿›´æ–° - ${subClassNameVars.get($index)}Mapper.insertOrUpdate(${subClassNameVar}); - } - - #end -#end - private void delete${subSimpleClassName}By${SubJoinColumnName}(${primaryColumn.javaType} ${subJoinColumn.javaField}) { - ${subClassNameVars.get($index)}Mapper.deleteBy${SubJoinColumnName}(${subJoinColumn.javaField}); - } - -#end -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/test/serviceTest.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/java/test/serviceTest.vm deleted file mode 100644 index bfd4600..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/java/test/serviceTest.vm +++ /dev/null @@ -1,168 +0,0 @@ -package ${basePackage}.module.${table.moduleName}.service.${table.businessName}; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import ${jakartaPackage}.annotation.Resource; - -import ${baseFrameworkPackage}.test.core.ut.BaseDbUnitTest; - -import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*; -import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; -import ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper; -import ${PageResultClassName}; - -import ${jakartaPackage}.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*; -import static ${baseFrameworkPackage}.test.core.util.AssertUtils.*; -import static ${baseFrameworkPackage}.test.core.util.RandomUtils.*; -import static ${LocalDateTimeUtilsClassName}.*; -import static ${ObjectUtilsClassName}.*; -import static ${DateUtilsClassName}.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -## å­—æ®µæ¨¡æ¿ -#macro(getPageCondition $VO) - // mock æ•°æ® - ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class, o -> { // 等会查询到 - #foreach ($column in $columns) - #if (${column.listOperation}) - #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字æ¯å¤§å†™ - o.set$JavaField(null); - #end - #end - }); - ${classNameVar}Mapper.insert(db${simpleClassName}); - #foreach ($column in $columns) - #if (${column.listOperation}) - #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字æ¯å¤§å†™ - // 测试 ${column.javaField} ä¸åŒ¹é… - ${classNameVar}Mapper.insert(cloneIgnoreId(db${simpleClassName}, o -> o.set$JavaField(null))); - #end - #end - // 准备傿•° - ${sceneEnum.prefixClass}${table.className}${VO} reqVO = new ${sceneEnum.prefixClass}${table.className}${VO}(); - #foreach ($column in $columns) - #if (${column.listOperation}) - #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字æ¯å¤§å†™ - #if (${column.listOperationCondition} == "BETWEEN")## BETWEEN 的情况 - reqVO.set${JavaField}(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - #else - reqVO.set$JavaField(null); - #end - #end - #end -#end -/** - * {@link ${table.className}ServiceImpl} çš„å•元测试类 - * - * @author ${table.author} - */ -@Import(${table.className}ServiceImpl.class) -public class ${table.className}ServiceImplTest extends BaseDbUnitTest { - - @Resource - private ${table.className}ServiceImpl ${classNameVar}Service; - - @Resource - private ${table.className}Mapper ${classNameVar}Mapper; - - @Test - public void testCreate${simpleClassName}_success() { - // 准备傿•° - ${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class).setId(null); - - // 调用 - ${primaryColumn.javaType} ${classNameVar}Id = ${classNameVar}Service.create${simpleClassName}(createReqVO); - // 断言 - assertNotNull(${classNameVar}Id); - // æ ¡éªŒè®°å½•çš„å±žæ€§æ˜¯å¦æ­£ç¡® - ${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(${classNameVar}Id); - assertPojoEquals(createReqVO, ${classNameVar}, "id"); - } - - @Test - public void testUpdate${simpleClassName}_success() { - // mock æ•°æ® - ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class); - ${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® - // 准备傿•° - ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class, o -> { - o.setId(db${simpleClassName}.getId()); // 设置更新的 ID - }); - - // 调用 - ${classNameVar}Service.update${simpleClassName}(updateReqVO); - // æ ¡éªŒæ˜¯å¦æ›´æ–°æ­£ç¡® - ${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(updateReqVO.getId()); // èŽ·å–æœ€æ–°çš„ - assertPojoEquals(updateReqVO, ${classNameVar}); - } - - @Test - public void testUpdate${simpleClassName}_notExists() { - // 准备傿•° - ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> ${classNameVar}Service.update${simpleClassName}(updateReqVO), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS); - } - - @Test - public void testDelete${simpleClassName}_success() { - // mock æ•°æ® - ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class); - ${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® - // 准备傿•° - ${primaryColumn.javaType} id = db${simpleClassName}.getId(); - - // 调用 - ${classNameVar}Service.delete${simpleClassName}(id); - // 校验数æ®ä¸å­˜åœ¨äº† - assertNull(${classNameVar}Mapper.selectById(id)); - } - - @Test - public void testDelete${simpleClassName}_notExists() { - // 准备傿•° - ${primaryColumn.javaType} id = random${primaryColumn.javaType}Id(); - - // 调用, 并断言异常 - assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS); - } - -## 特殊:树表专属逻辑(树ä¸éœ€è¦åˆ†é¡µæŽ¥å£ï¼‰ -#if ( $table.templateType != 2 ) - @Test - @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 - public void testGet${simpleClassName}Page() { - #getPageCondition("PageReqVO") - - // 调用 - PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(db${simpleClassName}, pageResult.getList().get(0)); - } -#else - @Test - @Disabled // TODO 请修改 null 为需è¦çš„值,然åŽåˆ é™¤ @Disabled 注解 - public void testGet${simpleClassName}List() { - #getPageCondition("ListReqVO") - - // 调用 - List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(reqVO); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(db${simpleClassName}, list.get(0)); - } -#end - -} \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/sql/h2.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/sql/h2.vm deleted file mode 100644 index a073fdb..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/sql/h2.vm +++ /dev/null @@ -1,37 +0,0 @@ --- 将该建表 SQL 语å¥ï¼Œæ·»åŠ åˆ° yudao-module-${table.moduleName}-biz 模å—çš„ test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "${table.tableName.toLowerCase()}" ( -#foreach ($column in $columns) -#if (${column.javaType} == 'Long') - #set ($dataType='bigint') -#elseif (${column.javaType} == 'Integer') - #set ($dataType='int') -#elseif (${column.javaType} == 'Boolean') - #set ($dataType='bit') -#elseif (${column.javaType} == 'Date') - #set ($dataType='datetime') -#else - #set ($dataType='varchar') -#end - #if (${column.primaryKey})##处ç†ä¸»é”® - "${column.javaField}"#if (${column.javaType} == 'String') ${dataType} NOT NULL#else ${dataType} NOT NULL GENERATED BY DEFAULT AS IDENTITY#end, - #else - #if (${column.columnName} == 'create_time') - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - #elseif (${column.columnName} == 'update_time') - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - #elseif (${column.columnName} == 'creator' || ${column.columnName} == 'updater') - "${column.columnName}" ${dataType} DEFAULT '', - #elseif (${column.columnName} == 'deleted') - "deleted" bit NOT NULL DEFAULT FALSE, - #elseif (${column.columnName} == 'tenant_id') - "tenant_id" bigint NOT NULL DEFAULT 0, - #else - "${column.columnName.toLowerCase()}" ${dataType}#if (${column.nullable} == false) NOT NULL#end, - #end - #end -#end - PRIMARY KEY ("${primaryColumn.columnName.toLowerCase()}") -) COMMENT '${table.tableComment}'; - --- 将该删表 SQL 语å¥ï¼Œæ·»åŠ åˆ° yudao-module-${table.moduleName}-biz 模å—çš„ test/resources/sql/clean.sql 文件里 -DELETE FROM "${table.tableName}"; \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/sql/sql.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/sql/sql.vm deleted file mode 100644 index 41b107d..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/sql/sql.vm +++ /dev/null @@ -1,28 +0,0 @@ --- èœå• SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '${table.classComment}管ç†', '', 2, 0, ${table.parentMenuId}, - '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}' -); - --- 按钮父èœå•ID --- æš‚æ—¶åªæ”¯æŒ MySQL。如果你是 Oracleã€PostgreSQLã€SQLServer çš„è¯ï¼Œéœ€è¦æ‰‹åŠ¨ä¿®æ”¹ @parentId çš„éƒ¨åˆ†çš„ä»£ç  -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -#set ($functionNames = ['查询', '创建', 'æ›´æ–°', '删除', '导出']) -#set ($functionOps = ['query', 'create', 'update', 'delete', 'export']) -#foreach ($functionName in $functionNames) -#set ($index = $foreach.count - 1) -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId, - '', '', '', 0 -); -#end \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/api/api.js.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/api/api.js.vm deleted file mode 100644 index 835c019..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/api/api.js.vm +++ /dev/null @@ -1,141 +0,0 @@ -import request from '@/utils/request' -#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}") - -// 创建${table.classComment} -export function create${simpleClassName}(data) { - return request({ - url: '${baseURL}/create', - method: 'post', - data: data - }) -} - -// æ›´æ–°${table.classComment} -export function update${simpleClassName}(data) { - return request({ - url: '${baseURL}/update', - method: 'put', - data: data - }) -} - -// 删除${table.classComment} -export function delete${simpleClassName}(id) { - return request({ - url: '${baseURL}/delete?id=' + id, - method: 'delete' - }) -} - -// 获得${table.classComment} -export function get${simpleClassName}(id) { - return request({ - url: '${baseURL}/get?id=' + id, - method: 'get' - }) -} - -#if ( $table.templateType != 2 ) -// 获得${table.classComment}分页 -export function get${simpleClassName}Page(params) { - return request({ - url: '${baseURL}/page', - method: 'get', - params - }) -} -#else -// 获得${table.classComment}列表 -export function get${simpleClassName}List(params) { - return request({ - url: '${baseURL}/list', - method: 'get', - params - }) -} -#end -// 导出${table.classComment} Excel -export function export${simpleClassName}Excel(params) { - return request({ - url: '${baseURL}/export-excel', - method: 'get', - params, - responseType: 'blob' - }) -} -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) - #set ($index = $foreach.count - 1) - #set ($subSimpleClassName = $subSimpleClassNames.get($index)) - #set ($subPrimaryColumn = $subPrimaryColumns.get($index))##å½“å‰ primary 字段 - #set ($subJoinColumn = $subJoinColumns.get($index))##å½“å‰ join 字段 - #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index)) - #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index)) - #set ($subClassNameVar = $subClassNameVars.get($index)) - -// ==================== å­è¡¨ï¼ˆ$subTable.classComment) ==================== - ## 情况一:MASTER_ERP 时,需è¦åˆ†æŸ¥è¯¢é¡µå­è¡¨ - #if ($table.templateType == 11) - // 获得${subTable.classComment}分页 - export function get${subSimpleClassName}Page(params) { - return request({ - url: '${baseURL}/${subSimpleClassName_strikeCase}/page', - method: 'get', - params - }) - } - ## æƒ…å†µäºŒï¼šéž MASTER_ERP 时,需è¦åˆ—表查询å­è¡¨ - #else - #if ($subTable.subJoinMany) - // 获得${subTable.classComment}列表 - export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}) { - return request({ - url: '${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=' + ${subJoinColumn.javaField}, - method: 'get' - }) - } - #else - // 获得${subTable.classComment} - export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}) { - return request({ - url: '${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=' + ${subJoinColumn.javaField}, - method: 'get' - }) - } - #end - #end - ## 特殊:MASTER_ERP 时,支æŒå•个的新增ã€ä¿®æ”¹ã€åˆ é™¤æ“作 - #if ($table.templateType == 11) - // 新增${subTable.classComment} - export function create${subSimpleClassName}(data) { - return request({ - url: '${baseURL}/${subSimpleClassName_strikeCase}/create', - method: 'post', - data - }) - } - // 修改${subTable.classComment} - export function update${subSimpleClassName}(data) { - return request({ - url: '${baseURL}/${subSimpleClassName_strikeCase}/update', - method: 'post', - data - }) - } - // 删除${subTable.classComment} - export function delete${subSimpleClassName}(id) { - return request({ - url: '${baseURL}/${subSimpleClassName_strikeCase}/delete?id=' + id, - method: 'delete' - }) - } - // 获得${subTable.classComment} - export function get${subSimpleClassName}(id) { - return request({ - url: '${baseURL}/${subSimpleClassName_strikeCase}/get?id=' + id, - method: 'get' - }) - } - #end -#end \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm deleted file mode 100644 index 99aa91a..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm +++ /dev/null @@ -1,205 +0,0 @@ -#set ($subTable = $subTables.get($subIndex))##当å‰è¡¨ -#set ($subColumns = $subColumnsList.get($subIndex))##当å‰å­—段数组 -#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 - - - diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm deleted file mode 100644 index ca266be..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm +++ /dev/null @@ -1,2 +0,0 @@ -## 主表的 normal å’Œ inner 使用相åŒçš„ form è¡¨å• -#parse("codegen/vue/views/components/form_sub_normal.vue.vm") \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm deleted file mode 100644 index 48a404a..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm +++ /dev/null @@ -1,347 +0,0 @@ -#set ($subTable = $subTables.get($subIndex))##当å‰è¡¨ -#set ($subColumns = $subColumnsList.get($subIndex))##当å‰å­—段数组 -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 -#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - - - diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm deleted file mode 100644 index 589736b..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm +++ /dev/null @@ -1,165 +0,0 @@ -#set ($subTable = $subTables.get($subIndex))##当å‰è¡¨ -#set ($subColumns = $subColumnsList.get($subIndex))##当å‰å­—段数组 -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 -#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - - - diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm deleted file mode 100644 index 90b8e41..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm +++ /dev/null @@ -1,4 +0,0 @@ -## å­è¡¨çš„ erp å’Œ inner 使用相似的 list 列表,差异主è¦ä¸¤ç‚¹ï¼š -## 1)inner 使用 list ä¸åˆ†é¡µï¼Œerp 使用 page 分页 -## 2)erp 支æŒå•个å­è¡¨çš„æ–°å¢žã€ä¿®æ”¹ã€åˆ é™¤ï¼Œinner 䏿”¯æŒ -#parse("codegen/vue/views/components/list_sub_erp.vue.vm") \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/form.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/form.vue.vm deleted file mode 100644 index 634d05d..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/form.vue.vm +++ /dev/null @@ -1,320 +0,0 @@ - - - diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/index.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/index.vue.vm deleted file mode 100644 index 9c1e124..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue/views/index.vue.vm +++ /dev/null @@ -1,340 +0,0 @@ - - - diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/api/api.ts.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/api/api.ts.vm deleted file mode 100644 index c3044fb..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/api/api.ts.vm +++ /dev/null @@ -1,115 +0,0 @@ -import request from '@/config/axios' -#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}") - -// ${table.classComment} VO -export interface ${simpleClassName}VO { -#foreach ($column in $columns) -#if ($column.createOperation || $column.updateOperation) -#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal") - ${column.javaField}: number // ${column.columnComment} -#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime") - ${column.javaField}: Date // ${column.columnComment} -#else - ${column.javaField}: ${column.javaType.toLowerCase()} // ${column.columnComment} -#end -#end -#end -} - -// ${table.classComment} API -export const ${simpleClassName}Api = { -#if ( $table.templateType != 2 ) - // 查询${table.classComment}分页 - get${simpleClassName}Page: async (params: any) => { - return await request.get({ url: `${baseURL}/page`, params }) - }, -#else - // 查询${table.classComment}列表 - get${simpleClassName}List: async (params) => { - return await request.get({ url: `${baseURL}/list`, params }) - }, -#end - - // 查询${table.classComment}详情 - get${simpleClassName}: async (id: number) => { - return await request.get({ url: `${baseURL}/get?id=` + id }) - }, - - // 新增${table.classComment} - create${simpleClassName}: async (data: ${simpleClassName}VO) => { - return await request.post({ url: `${baseURL}/create`, data }) - }, - - // 修改${table.classComment} - update${simpleClassName}: async (data: ${simpleClassName}VO) => { - return await request.put({ url: `${baseURL}/update`, data }) - }, - - // 删除${table.classComment} - delete${simpleClassName}: async (id: number) => { - return await request.delete({ url: `${baseURL}/delete?id=` + id }) - }, - - // 导出${table.classComment} Excel - export${simpleClassName}: async (params) => { - return await request.download({ url: `${baseURL}/export-excel`, params }) - }, -## 特殊:主å­è¡¨ä¸“属逻辑 -#foreach ($subTable in $subTables) -#set ($index = $foreach.count - 1) -#set ($subSimpleClassName = $subSimpleClassNames.get($index)) -#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##å½“å‰ primary 字段 -#set ($subJoinColumn = $subJoinColumns.get($index))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ -#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index)) -#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index)) -#set ($subClassNameVar = $subClassNameVars.get($index)) - -// ==================== å­è¡¨ï¼ˆ$subTable.classComment) ==================== -## 情况一:MASTER_ERP 时,需è¦åˆ†æŸ¥è¯¢é¡µå­è¡¨ -#if ( $table.templateType == 11 ) - - // 获得${subTable.classComment}分页 - get${subSimpleClassName}Page: async (params) => { - return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/page`, params }) - }, -## æƒ…å†µäºŒï¼šéž MASTER_ERP 时,需è¦åˆ—表查询å­è¡¨ -#else - #if ( $subTable.subJoinMany ) - - // 获得${subTable.classComment}列表 - get${subSimpleClassName}ListBy${SubJoinColumnName}: async (${subJoinColumn.javaField}) => { - return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField} }) - }, - #else - - // 获得${subTable.classComment} - get${subSimpleClassName}By${SubJoinColumnName}: async (${subJoinColumn.javaField}) => { - return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField} }) - }, - #end -#end -## 特殊:MASTER_ERP 时,支æŒå•个的新增ã€ä¿®æ”¹ã€åˆ é™¤æ“作 -#if ( $table.templateType == 11 ) - // 新增${subTable.classComment} - create${subSimpleClassName}: async (data) => { - return await request.post({ url: `${baseURL}/${subSimpleClassName_strikeCase}/create`, data }) - }, - - // 修改${subTable.classComment} - update${subSimpleClassName}: async (data) => { - return await request.put({ url: `${baseURL}/${subSimpleClassName_strikeCase}/update`, data }) - }, - - // 删除${subTable.classComment} - delete${subSimpleClassName}: async (id: number) => { - return await request.delete({ url: `${baseURL}/${subSimpleClassName_strikeCase}/delete?id=` + id }) - }, - - // 获得${subTable.classComment} - get${subSimpleClassName}: async (id: number) => { - return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get?id=` + id }) - }, -#end -#end -} diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm deleted file mode 100644 index 81cd977..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm +++ /dev/null @@ -1,204 +0,0 @@ -#set ($subColumns = $subColumnsList.get($subIndex))##当å‰å­—段数组 -#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 - - \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm deleted file mode 100644 index d8542c3..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm +++ /dev/null @@ -1,2 +0,0 @@ -## 主表的 normal å’Œ inner 使用相åŒçš„ form è¡¨å• -#parse("codegen/vue3/views/components/form_sub_normal.vue.vm") \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm deleted file mode 100644 index 3fa1eff..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm +++ /dev/null @@ -1,360 +0,0 @@ -#set ($subTable = $subTables.get($subIndex))##当å‰è¡¨ -#set ($subColumns = $subColumnsList.get($subIndex))##当å‰å­—段数组 -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 -#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - - \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm deleted file mode 100644 index 3f0710e..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm +++ /dev/null @@ -1,184 +0,0 @@ -#set ($subTable = $subTables.get($subIndex))##当å‰è¡¨ -#set ($subColumns = $subColumnsList.get($subIndex))##当å‰å­—段数组 -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 -#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##å½“å‰ join 字段 -#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字æ¯å¤§å†™ - - \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm deleted file mode 100644 index 3fe6488..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm +++ /dev/null @@ -1,4 +0,0 @@ -## å­è¡¨çš„ erp å’Œ inner 使用相似的 list 列表,差异主è¦ä¸¤ç‚¹ï¼š -## 1)inner 使用 list ä¸åˆ†é¡µï¼Œerp 使用 page 分页 -## 2)erp 支æŒå•个å­è¡¨çš„æ–°å¢žã€ä¿®æ”¹ã€åˆ é™¤ï¼Œinner 䏿”¯æŒ -#parse("codegen/vue3/views/components/list_sub_erp.vue.vm") \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/form.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/form.vue.vm deleted file mode 100644 index e37474b..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/form.vue.vm +++ /dev/null @@ -1,300 +0,0 @@ - - \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/index.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/index.vue.vm deleted file mode 100644 index 399b58e..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3/views/index.vue.vm +++ /dev/null @@ -1,374 +0,0 @@ - - - \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/api/api.ts.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/api/api.ts.vm deleted file mode 100644 index b7f2651..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/api/api.ts.vm +++ /dev/null @@ -1,32 +0,0 @@ -import { defHttp } from '@/utils/http/axios' -#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}") - -// 查询${table.classComment}列表 -export function get${simpleClassName}Page(params) { - return defHttp.get({ url: '${baseURL}/page', params }) -} - -// 查询${table.classComment}详情 -export function get${simpleClassName}(id: number) { - return defHttp.get({ url: `${baseURL}/get?id=${id}` }) -} - -// 新增${table.classComment} -export function create${simpleClassName}(data) { - return defHttp.post({ url: '${baseURL}/create', data }) -} - -// 修改${table.classComment} -export function update${simpleClassName}(data) { - return defHttp.put({ url: '${baseURL}/update', data }) -} - -// 删除${table.classComment} -export function delete${simpleClassName}(id: number) { - return defHttp.delete({ url: `${baseURL}/delete?id=${id}` }) -} - -// 导出${table.classComment} Excel -export function export${simpleClassName}(params) { - return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls') -} diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/data.ts.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/data.ts.vm deleted file mode 100644 index 56f4e82..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/data.ts.vm +++ /dev/null @@ -1,260 +0,0 @@ -import type {BasicColumn, FormSchema} from '@/components/Table' -import {useRender} from '@/components/Table' -import {DICT_TYPE, getDictOptions} from '@/utils/dict' - -export const columns: BasicColumn[] = [ -#foreach($column in $columns) -#if ($column.listOperationResult) - #set ($dictType=$column.dictType) - #set ($javaField = $column.javaField) - #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) - #set ($comment=$column.columnComment) -#if ($column.javaType == "LocalDateTime")## 时间类型 - { - title: '${comment}', - dataIndex: '${javaField}', - width: 180, - customRender: ({ text }) => { - return useRender.renderDate(text) - }, - }, -#elseif("" != $column.dictType)## æ•°æ®å­—å…¸ - { - title: '${comment}', - dataIndex: '${javaField}', - width: 180, - customRender: ({ text }) => { - return useRender.renderDict(text, DICT_TYPE.$dictType.toUpperCase()) - }, - }, -#else - { - title: '${comment}', - dataIndex: '${javaField}', - width: 160, - }, -#end -#end -#end -] - -export const searchFormSchema: FormSchema[] = [ -#foreach($column in $columns) -#if ($column.listOperation) - #set ($dictType=$column.dictType) - #set ($javaType = $column.javaType) - #set ($javaField = $column.javaField) - #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) - #set ($comment=$column.columnComment) - #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") - #set ($dictMethod = "number") - #elseif ($javaType == "String") - #set ($dictMethod = "string") - #elseif ($javaType == "Boolean") - #set ($dictMethod = "boolean") - #end - { - label: '${comment}', - field: '${javaField}', - #if ($column.htmlType == "input") - component: 'Input', - #elseif ($column.htmlType == "select") - component: 'Select', - componentProps: { - #if ("" != $dictType)## 设置了 dictType æ•°æ®å­—典的情况 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else## 未设置 dictType æ•°æ®å­—典的情况 - options: [], - #end - }, - #elseif ($column.htmlType == "radio") - component: 'RadioButtonGroup', - componentProps: { - #if ("" != $dictType)## 设置了 dictType æ•°æ®å­—典的情况 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else## 未设置 dictType æ•°æ®å­—典的情况 - options: [], - #end - }, - #elseif($column.htmlType == "datetime") - component: 'RangePicker', - #end - colProps: { span: 8 }, - }, -#end -#end -] - -export const createFormSchema: FormSchema[] = [ - { - label: 'ç¼–å·', - field: 'id', - show: false, - component: 'Input', - }, -#foreach($column in $columns) -#if ($column.createOperation) - #set ($dictType = $column.dictType) - #set ($javaType = $column.javaType) - #set ($javaField = $column.javaField) - #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) - #set ($comment = $column.columnComment) - #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") - #set ($dictMethod = "number") - #elseif ($javaType == "String") - #set ($dictMethod = "string") - #elseif ($javaType == "Boolean") - #set ($dictMethod = "boolean") - #end -#if (!$column.primaryKey)## 忽略主键,ä¸ç”¨åœ¨è¡¨å•里 - { - label: '${comment}', - field: '${javaField}', - #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新æ“作 && è¦æ±‚éžç©º && éžä¸»é”® - required: true, - #end - #if ($column.htmlType == "input") - component: 'Input', - #elseif($column.htmlType == "imageUpload")## 图片上传 - component: 'FileUpload', - componentProps: { - fileType: 'image', - maxCount: 1, - }, - #elseif($column.htmlType == "fileUpload")## 文件上传 - component: 'FileUpload', - componentProps: { - fileType: 'file', - maxCount: 1, - }, - #elseif($column.htmlType == "editor")## 文本编辑器 - component: 'Editor', - #elseif($column.htmlType == "select")## 下拉框 - component: 'Select', - componentProps: { - #if ("" != $dictType)## 有数æ®å­—å…¸ - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else##没数æ®å­—å…¸ - options:[], - #end - }, - #elseif($column.htmlType == "checkbox")## 多选框 - component: 'Checkbox', - componentProps: { - #if ("" != $dictType)## 有数æ®å­—å…¸ - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else##没数æ®å­—å…¸ - options:[], - #end - }, - #elseif($column.htmlType == "radio")## å•选框 - component: 'RadioButtonGroup', - componentProps: { - #if ("" != $dictType)## 有数æ®å­—å…¸ - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else##没数æ®å­—å…¸ - options:[], - #end - }, - #elseif($column.htmlType == "datetime")## 时间框 - component: 'DatePicker', - componentProps: { - showTime: true, - format: 'YYYY-MM-DD HH:mm:ss', - valueFormat: 'x', - }, - #elseif($column.htmlType == "textarea")## 文本域 - component: 'InputTextArea', - #end - }, -#end -#end -#end -] - -export const updateFormSchema: FormSchema[] = [ - { - label: 'ç¼–å·', - field: 'id', - show: false, - component: 'Input', - }, -#foreach($column in $columns) -#if ($column.updateOperation) -#set ($dictType = $column.dictType) -#set ($javaType = $column.javaType) -#set ($javaField = $column.javaField) -#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) -#set ($comment = $column.columnComment) -#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") - #set ($dictMethod = "number") -#elseif ($javaType == "String") - #set ($dictMethod = "string") -#elseif ($javaType == "Boolean") - #set ($dictMethod = "boolean") -#end - #if (!$column.primaryKey)## 忽略主键,ä¸ç”¨åœ¨è¡¨å•里 - { - label: '${comment}', - field: '${javaField}', - #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新æ“作 && è¦æ±‚éžç©º && éžä¸»é”® - required: true, - #end - #if ($column.htmlType == "input") - component: 'Input', - #elseif($column.htmlType == "imageUpload")## 图片上传 - component: 'FileUpload', - componentProps: { - fileType: 'image', - maxCount: 1, - }, - #elseif($column.htmlType == "fileUpload")## 文件上传 - component: 'FileUpload', - componentProps: { - fileType: 'file', - maxCount: 1, - }, - #elseif($column.htmlType == "editor")## 文本编辑器 - component: 'Editor', - #elseif($column.htmlType == "select")## 下拉框 - component: 'Select', - componentProps: { - #if ("" != $dictType)## 有数æ®å­—å…¸ - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else##没数æ®å­—å…¸ - options:[], - #end - }, - #elseif($column.htmlType == "checkbox")## 多选框 - component: 'Checkbox', - componentProps: { - #if ("" != $dictType)## 有数æ®å­—å…¸ - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else##没数æ®å­—å…¸ - options:[], - #end - }, - #elseif($column.htmlType == "radio")## å•选框 - component: 'RadioButtonGroup', - componentProps: { - #if ("" != $dictType)## 有数æ®å­—å…¸ - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else##没数æ®å­—å…¸ - options:[], - #end - }, - #elseif($column.htmlType == "datetime")## 时间框 - component: 'DatePicker', - componentProps: { - showTime: true, - format: 'YYYY-MM-DD HH:mm:ss', - valueFormat: 'x', - }, - #elseif($column.htmlType == "textarea")## 文本域 - component: 'InputTextArea', - #end - }, - #end -#end -#end -] \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/form.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/form.vue.vm deleted file mode 100644 index 1804365..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/form.vue.vm +++ /dev/null @@ -1,58 +0,0 @@ - - \ No newline at end of file diff --git a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/index.vue.vm b/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/index.vue.vm deleted file mode 100644 index 9e59b12..0000000 --- a/tashow-module/tashow-module-infra/src/main/resources/codegen/vue3_vben/views/index.vue.vm +++ /dev/null @@ -1,92 +0,0 @@ - - diff --git a/tashow-module/tashow-module-pay/Dockerfile b/tashow-module/tashow-module-pay/Dockerfile new file mode 100644 index 0000000..97ad4a0 --- /dev/null +++ b/tashow-module/tashow-module-pay/Dockerfile @@ -0,0 +1,20 @@ +## AdoptOpenJDK åœæ­¢å‘布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,æä¾›æ›´å¥½çš„稳定性 +## æ„Ÿè°¢å¤æ—¦æ ¸åšå£«çš„建议ï¼ç°å­å“¥ï¼Œç‰›çš®ï¼ +FROM eclipse-temurin:21-jre + + +## 创建目录,并使用它作为工作目录 +RUN mkdir -p /yudao-module-pay-biz +WORKDIR /yudao-module-pay-biz +## å°†åŽç«¯é¡¹ç›®çš„ Jar 文件,å¤åˆ¶åˆ°é•œåƒä¸­ +COPY ./target/yudao-module-pay-biz.jar app.jar + +## 设置 TZ 时区 +## 设置 JAVA_OPTS 环境å˜é‡ï¼Œå¯é€šè¿‡ docker run -e "JAVA_OPTS=" 进行覆盖 +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m" + +## 暴露åŽç«¯é¡¹ç›®çš„ 48080 ç«¯å£ +EXPOSE 48085 + +## å¯åЍåŽç«¯é¡¹ç›® +CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar diff --git a/tashow-module/tashow-module-pay/pom.xml b/tashow-module/tashow-module-pay/pom.xml new file mode 100644 index 0000000..4b7c884 --- /dev/null +++ b/tashow-module/tashow-module-pay/pom.xml @@ -0,0 +1,97 @@ + + + + com.tashow.cloud + tashow-module + ${revision} + + 4.0.0 + tashow-module-pay + jar + + ${project.artifactId} + + pay 模å—,我们放支付业务,æä¾›ä¸šåŠ¡çš„æ”¯ä»˜èƒ½åŠ›ã€‚ + 例如说:商户ã€åº”ç”¨ã€æ”¯ä»˜ã€é€€æ¬¾ç­‰ç­‰ + + + + + + com.tashow.cloud + tashow-framework-env + + + + + com.tashow.cloud + tashow-pay-api + + + + + com.tashow.cloud + tashow-framework-security + + + + + com.tashow.cloud + tashow-data-mybatis + + + + com.tashow.cloud + tashow-data-redis + + + + + com.tashow.cloud + tashow-framework-rpc + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + + com.tashow.cloud + tashow-data-excel + + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + + diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/PayServerApplication.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/PayServerApplication.java new file mode 100644 index 0000000..fe1defd --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/PayServerApplication.java @@ -0,0 +1,30 @@ +package com.tashow.cloud.pay; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 项目的å¯åŠ¨ç±» + * + * 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + * 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + * 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + * + * @author èŠ‹é“æºç  + */ +@SpringBootApplication +public class PayServerApplication { + + public static void main(String[] args) { + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + + SpringApplication.run(PayServerApplication.class, args); + + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/order/PayOrderApiImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/order/PayOrderApiImpl.java new file mode 100644 index 0000000..2cb7d25 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/order/PayOrderApiImpl.java @@ -0,0 +1,40 @@ +package com.tashow.cloud.pay.api.order; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; +import com.tashow.cloud.pay.convert.order.PayOrderConvert; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.service.order.PayOrderService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@RestController // æä¾› RESTful API 接å£ï¼Œç»™ Feign 调用 +@Validated +public class PayOrderApiImpl implements PayOrderApi { + + @Resource + private PayOrderService payOrderService; + + @Override + public CommonResult createOrder(PayOrderCreateReqDTO reqDTO) { + return success(payOrderService.createOrder(reqDTO)); + } + + @Override + public CommonResult getOrder(Long id) { + PayOrderDO order = payOrderService.getOrder(id); + return success(PayOrderConvert.INSTANCE.convert2(order)); + } + + @Override + public CommonResult updatePayOrderPrice(Long id, Integer payPrice) { + payOrderService.updatePayOrderPrice(id, payPrice); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/refund/PayRefundApiImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/refund/PayRefundApiImpl.java new file mode 100644 index 0000000..4a890bc --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/refund/PayRefundApiImpl.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.pay.api.refund; + +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.pay.convert.refund.PayRefundConvert; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import com.tashow.cloud.payapi.api.refund.PayRefundApi; +import com.tashow.cloud.sdk.payment.dto.refund.PayRefundRespDTO; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import static com.tashow.cloud.common.pojo.CommonResult.success; + + +@RestController // æä¾› RESTful API 接å£ï¼Œç»™ Feign 调用 +@Validated +public class PayRefundApiImpl implements PayRefundApi { + + @Resource + private PayRefundService payRefundService; + + @Override + public CommonResult createRefund(PayRefundCreateReqDTO reqDTO) { + return success(payRefundService.createPayRefund(reqDTO)); + } + + @Override + public CommonResult getRefund(Long id) { + return success(PayRefundConvert.INSTANCE.convert02(payRefundService.getRefund(id))); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/transfer/PayTransferApiImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/transfer/PayTransferApiImpl.java new file mode 100644 index 0000000..019e1fb --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/transfer/PayTransferApiImpl.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.pay.api.transfer; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO; +import com.tashow.cloud.pay.dal.dataobject.transfer.PayTransferDO; +import com.tashow.cloud.pay.service.transfer.PayTransferService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +/** + * è½¬è´¦å• API 实现类 + * + * @author jason + */ +@RestController // æä¾› RESTful API 接å£ï¼Œç»™ Feign 调用 +@Validated +public class PayTransferApiImpl implements PayTransferApi { + + @Resource + private PayTransferService payTransferService; + + @Override + public CommonResult createTransfer(PayTransferCreateReqDTO reqDTO) { + return success(payTransferService.createTransfer(reqDTO)); + } + + @Override + public CommonResult getTransfer(Long id) { + PayTransferDO transfer = payTransferService.getTransfer(id); + return success(BeanUtils.toBean(transfer, PayTransferRespDTO.class)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/wallet/PayWalletApiImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/wallet/PayWalletApiImpl.java new file mode 100644 index 0000000..959ccce --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/api/wallet/PayWalletApiImpl.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.pay.api.wallet; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletAddBalanceReqDTO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletDO; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import com.tashow.cloud.pay.service.wallet.PayWalletService; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.*; + +/** + * 钱包 API 实现类 + * + * @author jason + */ +@RestController // æä¾› RESTful API 接å£ï¼Œç»™ Feign 调用 +@Validated +public class PayWalletApiImpl implements PayWalletApi { + + @Resource + private PayWalletService payWalletService; + + @Override + public CommonResult addWalletBalance(PayWalletAddBalanceReqDTO reqDTO) { + // 创建或获å–钱包 + PayWalletDO wallet = payWalletService.getOrCreateWallet(reqDTO.getUserId(), reqDTO.getUserType()); + Assert.notNull(wallet, "钱包({}/{})ä¸å­˜åœ¨", reqDTO.getUserId(), reqDTO.getUserType()); + + // å¢žåŠ ä½™é¢ + PayWalletBizTypeEnum bizType = PayWalletBizTypeEnum.valueOf(reqDTO.getBizType()); + payWalletService.addWalletBalance(wallet.getId(), reqDTO.getBizId(), bizType, reqDTO.getPrice()); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/PayAppController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/PayAppController.java new file mode 100644 index 0000000..31dd2ec --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/PayAppController.java @@ -0,0 +1,110 @@ +package com.tashow.cloud.pay.controller.admin.app; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.pay.controller.admin.app.vo.*; +import com.tashow.cloud.pay.controller.admin.app.vo.*; +import com.tashow.cloud.payapi.controller.admin.app.vo.*; +import com.tashow.cloud.pay.convert.app.PayAppConvert; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.service.app.PayAppService; +import com.tashow.cloud.pay.service.channel.PayChannelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Slf4j +@Tag(name = "管ç†åŽå° - 支付应用信æ¯") +@RestController +@RequestMapping("/pay/app") +@Validated +public class PayAppController { + + @Resource + private PayAppService appService; + @Resource + private PayChannelService channelService; + + @PostMapping("/create") + @Operation(summary = "创建支付应用信æ¯") + @PreAuthorize("@ss.hasPermission('pay:app:create')") + public CommonResult createApp(@Valid @RequestBody PayAppCreateReqVO createReqVO) { + return success(appService.createApp(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新支付应用信æ¯") + @PreAuthorize("@ss.hasPermission('pay:app:update')") + public CommonResult updateApp(@Valid @RequestBody PayAppUpdateReqVO updateReqVO) { + appService.updateApp(updateReqVO); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "更新支付应用状æ€") + @PreAuthorize("@ss.hasPermission('pay:app:update')") + public CommonResult updateAppStatus(@Valid @RequestBody PayAppUpdateStatusReqVO updateReqVO) { + appService.updateAppStatus(updateReqVO.getId(), updateReqVO.getStatus()); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除支付应用信æ¯") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('pay:app:delete')") + public CommonResult deleteApp(@RequestParam("id") Long id) { + appService.deleteApp(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得支付应用信æ¯") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('pay:app:query')") + public CommonResult getApp(@RequestParam("id") Long id) { + PayAppDO app = appService.getApp(id); + return success(PayAppConvert.INSTANCE.convert(app)); + } + + @GetMapping("/page") + @Operation(summary = "获得支付应用信æ¯åˆ†é¡µ") + @PreAuthorize("@ss.hasPermission('pay:app:query')") + public CommonResult> getAppPage(@Valid PayAppPageReqVO pageVO) { + // 得到应用分页列表 + PageResult pageResult = appService.getAppPage(pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 得到所有的应用编å·ï¼ŒæŸ¥å‡ºæ‰€æœ‰çš„æ¸ é“,并移除未å¯ç”¨çš„æ¸ é“ + List channels = channelService.getChannelListByAppIds( + convertList(pageResult.getList(), PayAppDO::getId)); + channels.removeIf(channel -> !CommonStatusEnum.ENABLE.getStatus().equals(channel.getStatus())); + + // 拼接åŽè¿”回 + return success(PayAppConvert.INSTANCE.convertPage(pageResult, channels)); + } + + @GetMapping("/list") + @Operation(summary = "获得应用列表") + @PreAuthorize("@ss.hasPermission('pay:merchant:query')") + public CommonResult> getAppList() { + List appListDO = appService.getAppList(); + return success(PayAppConvert.INSTANCE.convertList(appListDO)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppBaseVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppBaseVO.java new file mode 100644 index 0000000..75ed259 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppBaseVO.java @@ -0,0 +1,47 @@ +package com.tashow.cloud.pay.controller.admin.app.vo; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.constraints.*; + +/** + * æ”¯ä»˜åº”ç”¨ä¿¡æ¯ Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class PayAppBaseVO { + + @Schema(description = "应用标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @NotEmpty(message = "应用标识ä¸èƒ½ä¸ºç©º") + private String appKey; + + @Schema(description = "应用å", requiredMode = Schema.RequiredMode.REQUIRED, example = "å°è±†") + @NotNull(message = "应用åä¸èƒ½ä¸ºç©º") + private String name; + + @Schema(description = "å¼€å¯çжæ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @NotNull(message = "å¼€å¯çжæ€ä¸èƒ½ä¸ºç©º") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "备注", example = "我是一个测试应用") + private String remark; + + @Schema(description = "支付结果的回调地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://127.0.0.1:48080/pay-callback") + @NotNull(message = "支付结果的回调地å€ä¸èƒ½ä¸ºç©º") + @URL(message = "支付结果的回调地å€å¿…须为 URL æ ¼å¼") + private String orderNotifyUrl; + + @Schema(description = "退款结果的回调地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://127.0.0.1:48080/refund-callback") + @NotNull(message = "退款结果的回调地å€ä¸èƒ½ä¸ºç©º") + @URL(message = "退款结果的回调地å€å¿…须为 URL æ ¼å¼") + private String refundNotifyUrl; + + @Schema(description = "转账结果的回调地å€", example = "http://127.0.0.1:48080/transfer-callback") + @URL(message = "转账结果的回调地å€å¿…须为 URL æ ¼å¼") + private String transferNotifyUrl; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppCreateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppCreateReqVO.java new file mode 100644 index 0000000..aa9e4f3 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppCreateReqVO.java @@ -0,0 +1,14 @@ +package com.tashow.cloud.pay.controller.admin.app.vo; + +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 PayAppCreateReqVO extends PayAppBaseVO { + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppPageItemRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppPageItemRespVO.java new file mode 100644 index 0000000..ef684f5 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppPageItemRespVO.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.pay.controller.admin.app.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; +import java.util.Set; + +@Schema(description = "管ç†åŽå° - 支付应用信æ¯åˆ†é¡µæŸ¥è¯¢ Response VO,相比于支付信æ¯ï¼Œè¿˜ä¼šå¤šå‡ºåº”用渠é“的开关信æ¯") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayAppPageItemRespVO extends PayAppBaseVO { + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "å·²é…置的支付渠é“ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "[alipay_pc, alipay_wap]") + private Set channelCodes; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppPageReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppPageReqVO.java new file mode 100644 index 0000000..fb4b066 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppPageReqVO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.pay.controller.admin.app.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 PayAppPageReqVO extends PageParam { + + @Schema(description = "应用å", example = "å°è±†") + private String name; + + @Schema(description = "应用标识", example = "yudao") + private String appKey; + + @Schema(description = "å¼€å¯çжæ€", example = "0") + private Integer status; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppRespVO.java new file mode 100644 index 0000000..464386c --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppRespVO.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.pay.controller.admin.app.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - æ”¯ä»˜åº”ç”¨ä¿¡æ¯ Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayAppRespVO extends PayAppBaseVO { + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "应用标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + private String appKey; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppUpdateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppUpdateReqVO.java new file mode 100644 index 0000000..f82d469 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppUpdateReqVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.pay.controller.admin.app.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - æ”¯ä»˜åº”ç”¨ä¿¡æ¯æ›´æ–° Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayAppUpdateReqVO extends PayAppBaseVO { + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "应用编å·ä¸èƒ½ä¸ºç©º") + private Long id; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppUpdateStatusReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppUpdateStatusReqVO.java new file mode 100644 index 0000000..5c8ebcf --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/app/vo/PayAppUpdateStatusReqVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.pay.controller.admin.app.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - åº”ç”¨æ›´æ–°çŠ¶æ€ Request VO") +@Data +public class PayAppUpdateStatusReqVO { + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "应用编å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "状æ€ï¼Œè§ SysCommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状æ€ä¸èƒ½ä¸ºç©º") + private Integer status; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/PayChannelController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/PayChannelController.java new file mode 100644 index 0000000..d7c9493 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/PayChannelController.java @@ -0,0 +1,82 @@ +package com.tashow.cloud.pay.controller.admin.channel; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelCreateReqVO; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelRespVO; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelUpdateReqVO; +import com.tashow.cloud.pay.convert.channel.PayChannelConvert; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.service.channel.PayChannelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管ç†åŽå° - 支付渠é“") +@RestController +@RequestMapping("/pay/channel") +@Validated +public class PayChannelController { + + @Resource + private PayChannelService channelService; + + @PostMapping("/create") + @Operation(summary = "åˆ›å»ºæ”¯ä»˜æ¸ é“ ") + @PreAuthorize("@ss.hasPermission('pay:channel:create')") + public CommonResult createChannel(@Valid @RequestBody PayChannelCreateReqVO createReqVO) { + return success(channelService.createChannel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "æ›´æ–°æ”¯ä»˜æ¸ é“ ") + @PreAuthorize("@ss.hasPermission('pay:channel:update')") + public CommonResult updateChannel(@Valid @RequestBody PayChannelUpdateReqVO updateReqVO) { + channelService.updateChannel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "åˆ é™¤æ”¯ä»˜æ¸ é“ ") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('pay:channel:delete')") + public CommonResult deleteChannel(@RequestParam("id") Long id) { + channelService.deleteChannel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得支付渠é“") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('pay:channel:query')") + public CommonResult getChannel(@RequestParam(value = "id", required = false) Long id, + @RequestParam(value = "appId", required = false) Long appId, + @RequestParam(value = "code", required = false) String code) { + PayChannelDO channel = null; + if (id != null) { + channel = channelService.getChannel(id); + } else if (appId != null && code != null) { + channel = channelService.getChannelByAppIdAndCode(appId, code); + } + return success(PayChannelConvert.INSTANCE.convert(channel)); + } + + @GetMapping("/get-enable-code-list") + @Operation(summary = "获得指定应用的开å¯çš„æ”¯ä»˜æ¸ é“ç¼–ç åˆ—表") + @Parameter(name = "appId", description = "应用编å·", required = true, example = "1") + public CommonResult> getEnableChannelCodeList(@RequestParam("appId") Long appId) { + List channels = channelService.getEnableChannelList(appId); + return success(convertSet(channels, PayChannelDO::getCode)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelBaseVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelBaseVO.java new file mode 100644 index 0000000..45aa07a --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelBaseVO.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.pay.controller.admin.channel.vo; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** +* æ”¯ä»˜æ¸ é“ Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 +* å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ +*/ +@Data +public class PayChannelBaseVO { + + @Schema(description = "å¼€å¯çжæ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "å¼€å¯çжæ€ä¸èƒ½ä¸ºç©º") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "备注", example = "我是å°å¤‡æ³¨") + private String remark; + + @Schema(description = "渠é“费率,å•ä½ï¼šç™¾åˆ†æ¯”", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "渠é“费率,å•ä½ï¼šç™¾åˆ†æ¯”ä¸èƒ½ä¸ºç©º") + private Double feeRate; + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "应用编å·ä¸èƒ½ä¸ºç©º") + private Long appId; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelCreateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelCreateReqVO.java new file mode 100644 index 0000000..45b5a23 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelCreateReqVO.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.pay.controller.admin.channel.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - æ”¯ä»˜æ¸ é“ åˆ›å»º Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayChannelCreateReqVO extends PayChannelBaseVO { + + @Schema(description = "渠é“ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_pc") + @NotNull(message = "渠é“ç¼–ç ä¸èƒ½ä¸ºç©º") + private String code; + + @Schema(description = "渠é“é…置的 json 字符串") + @NotBlank(message = "渠é“é…ç½®ä¸èƒ½ä¸ºç©º") + private String config; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelRespVO.java new file mode 100644 index 0000000..ca32181 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelRespVO.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.pay.controller.admin.channel.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - æ”¯ä»˜æ¸ é“ Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayChannelRespVO extends PayChannelBaseVO { + + @Schema(description = "商户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private LocalDateTime createTime; + + @Schema(description = "渠é“ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_pc") + private String code; + + @Schema(description = "é…ç½®", requiredMode = Schema.RequiredMode.REQUIRED) + private String config; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelUpdateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelUpdateReqVO.java new file mode 100644 index 0000000..88da502 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/channel/vo/PayChannelUpdateReqVO.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.pay.controller.admin.channel.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - æ”¯ä»˜æ¸ é“ æ›´æ–° Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayChannelUpdateReqVO extends PayChannelBaseVO { + + @Schema(description = "商户编å·", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "商户编å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "渠é“é…置的json字符串") + @NotBlank(message = "渠é“é…ç½®ä¸èƒ½ä¸ºç©º") + private String config; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/PayDemoOrderController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/PayDemoOrderController.java new file mode 100644 index 0000000..e2b30a8 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/PayDemoOrderController.java @@ -0,0 +1,74 @@ +package com.tashow.cloud.pay.controller.admin.demo; + +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.pay.api.notify.dto.PayOrderNotifyReqDTO; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; +import com.tashow.cloud.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO; +import com.tashow.cloud.pay.controller.admin.demo.vo.order.PayDemoOrderRespVO; +import com.tashow.cloud.pay.convert.demo.PayDemoOrderConvert; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoOrderDO; +import com.tashow.cloud.pay.service.demo.PayDemoOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.validation.Valid; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管ç†åŽå° - 示例订å•") +@RestController +@RequestMapping("/pay/demo-order") +@Validated +public class PayDemoOrderController { + + @Resource + private PayDemoOrderService payDemoOrderService; + + @PostMapping("/create") + @Operation(summary = "创建示例订å•") + public CommonResult createDemoOrder(@Valid @RequestBody PayDemoOrderCreateReqVO createReqVO) { + return success(payDemoOrderService.createDemoOrder(getLoginUserId(), createReqVO)); + } + + @GetMapping("/page") + @Operation(summary = "获得示例订å•分页") + public CommonResult> getDemoOrderPage(@Valid PageParam pageVO) { + PageResult pageResult = payDemoOrderService.getDemoOrderPage(pageVO); + return success(PayDemoOrderConvert.INSTANCE.convertPage(pageResult)); + } + + @PostMapping("/update-paid") + @Operation(summary = "更新示例订å•为已支付") // ç”± pay-module 支付æœåŠ¡ï¼Œè¿›è¡Œå›žè°ƒï¼Œå¯è§ PayNotifyJob + @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 + public CommonResult updateDemoOrderPaid(@RequestBody PayOrderNotifyReqDTO notifyReqDTO) { + payDemoOrderService.updateDemoOrderPaid(Long.valueOf(notifyReqDTO.getMerchantOrderId()), + notifyReqDTO.getPayOrderId()); + return success(true); + } + + @PutMapping("/refund") + @Operation(summary = "å‘起示例订å•的退款") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + public CommonResult refundDemoOrder(@RequestParam("id") Long id) { + payDemoOrderService.refundDemoOrder(id, getClientIP()); + return success(true); + } + + @PostMapping("/update-refunded") + @Operation(summary = "更新示例订å•为已退款") // ç”± pay-module 支付æœåŠ¡ï¼Œè¿›è¡Œå›žè°ƒï¼Œå¯è§ PayNotifyJob + @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 + public CommonResult updateDemoOrderRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { + payDemoOrderService.updateDemoOrderRefunded(Long.valueOf(notifyReqDTO.getMerchantOrderId()), + notifyReqDTO.getPayRefundId()); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/PayDemoTransferController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/PayDemoTransferController.java new file mode 100644 index 0000000..3711c7b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/PayDemoTransferController.java @@ -0,0 +1,51 @@ +package com.tashow.cloud.pay.controller.admin.demo; + +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.pay.api.notify.dto.PayTransferNotifyReqDTO; +import com.tashow.cloud.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; +import com.tashow.cloud.pay.controller.admin.demo.vo.transfer.PayDemoTransferRespVO; +import com.tashow.cloud.pay.convert.demo.PayDemoTransferConvert; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoTransferDO; +import com.tashow.cloud.pay.service.demo.PayDemoTransferService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.validation.Valid; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管ç†åŽå° - 示例转账å•") +@RestController +@RequestMapping("/pay/demo-transfer") +@Validated +public class PayDemoTransferController { + @Resource + private PayDemoTransferService demoTransferService; + + @PostMapping("/create") + @Operation(summary = "创建示例转账订å•") + public CommonResult createDemoTransfer(@Valid @RequestBody PayDemoTransferCreateReqVO createReqVO) { + return success(demoTransferService.createDemoTransfer(createReqVO)); + } + + @GetMapping("/page") + @Operation(summary = "获得示例转账订å•分页") + public CommonResult> getDemoTransferPage(@Valid PageParam pageVO) { + PageResult pageResult = demoTransferService.getDemoTransferPage(pageVO); + return success(PayDemoTransferConvert.INSTANCE.convertPage(pageResult)); + } + + @PostMapping("/update-status") + @Operation(summary = "更新示例转账订å•的转账状æ€") // ç”± pay-module 转账æœåŠ¡ï¼Œè¿›è¡Œå›žè°ƒ + @PermitAll // 无需登录,安全由 PayDemoTransferService 内部校验实现 + public CommonResult updateDemoTransferStatus(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) { + demoTransferService.updateDemoTransferStatus(Long.valueOf(notifyReqDTO.getMerchantTransferId()), + notifyReqDTO.getPayTransferId()); + return success(true); + } +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/order/PayDemoOrderCreateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/order/PayDemoOrderCreateReqVO.java new file mode 100644 index 0000000..8391d84 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/order/PayDemoOrderCreateReqVO.java @@ -0,0 +1,15 @@ +package com.tashow.cloud.pay.controller.admin.demo.vo.order; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - 示例订å•创建 Request VO") +@Data +public class PayDemoOrderCreateReqVO { + + @Schema(description = "商å“ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "17682") + @NotNull(message = "商å“ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long spuId; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/order/PayDemoOrderRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/order/PayDemoOrderRespVO.java new file mode 100644 index 0000000..24bd088 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/order/PayDemoOrderRespVO.java @@ -0,0 +1,54 @@ +package com.tashow.cloud.pay.controller.admin.demo.vo.order; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** +* ç¤ºä¾‹è®¢å• Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 +* å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ +*/ +@Data +public class PayDemoOrderRespVO { + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "23199") + private Long userId; + + @Schema(description = "商å“ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "17682") + private Long spuId; + + @Schema(description = "商家备注", example = "æŽå››") + private String spuName; + + @Schema(description = "价格,å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "30381") + private Integer price; + + @Schema(description = "是å¦å·²æ”¯ä»˜", requiredMode = Schema.RequiredMode.REQUIRED) + private Boolean payStatus; + + @Schema(description = "支付订å•ç¼–å·", example = "16863") + private Long payOrderId; + + @Schema(description = "è®¢å•æ”¯ä»˜æ—¶é—´") + private LocalDateTime payTime; + + @Schema(description = "支付渠é“", example = "alipay_qr") + private String payChannelCode; + + @Schema(description = "支付退款编å·", example = "23366") + private Long payRefundId; + + @Schema(description = "退款金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "14039") + private Integer refundPrice; + + @Schema(description = "退款时间") + private LocalDateTime refundTime; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java new file mode 100644 index 0000000..e39908d --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java @@ -0,0 +1,67 @@ +package com.tashow.cloud.pay.controller.admin.demo.vo.transfer; + +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Validator; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum.Alipay; +import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum.WxPay; + +/** + * @author jason + */ +@Schema(description = "管ç†åŽå° - 示例转账å•创建 Request VO") +@Data +public class PayDemoTransferCreateReqVO { + + @Schema(description = "转账类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "转账类型ä¸èƒ½ä¸ºç©º") + @InEnum(PayTransferTypeEnum.class) + private Integer type; + + @Schema(description = "转账金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "转账金é¢ä¸èƒ½ä¸ºç©º") + @Min(value = 1, message = "转账金é¢å¿…须大于零") + private Integer price; + + @Schema(description = "收款人姓å", example = "test1") + @NotBlank(message = "收款人姓åä¸èƒ½ä¸ºç©º", groups = {Alipay.class}) + private String userName; + + // ========== 支付å®è½¬è´¦ç›¸å…³å­—段 ========== + @Schema(description = "支付å®ç™»å½•å·,支æŒé‚®ç®±å’Œæ‰‹æœºå·æ ¼å¼", example = "test1@@sandbox.com") + @NotBlank(message = "支付å®ç™»å½•å·ä¸èƒ½ä¸ºç©º", groups = {Alipay.class}) + private String alipayLogonId; + + // ========== 微信转账相关字段 ========== + @Schema(description = "微信 openId", example = "oLefc4g5Gxx") + @NotBlank(message = "微信 openId ä¸èƒ½ä¸ºç©º", groups = {WxPay.class}) + private String openid; + + + // ========== 转账到银行å¡å’Œé’±åŒ…相关字段 待补充 ========== + + public void validate(Validator validator) { + PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(type); + switch (transferType) { + case ALIPAY_BALANCE: { + ValidationUtils.validate(validator, this, Alipay.class); + break; + } + case WX_BALANCE: { + ValidationUtils.validate(validator, this, WxPay.class); + break; + } + default: { + throw new UnsupportedOperationException("待实现"); + } + } + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/transfer/PayDemoTransferRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/transfer/PayDemoTransferRespVO.java new file mode 100644 index 0000000..d8876f4 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/demo/vo/transfer/PayDemoTransferRespVO.java @@ -0,0 +1,47 @@ +package com.tashow.cloud.pay.controller.admin.demo.vo.transfer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * ç¤ºä¾‹ä¸šåŠ¡è½¬è´¦è®¢å• Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class PayDemoTransferRespVO { + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long appId; + + @Schema(description = "转账金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "22338") + private Integer price; + + @Schema(description = "转账类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "收款人姓å", example = "test") + private String userName; + + @Schema(description = "支付å®ç™»å½•å·", example = "32167") + private String alipayLogonId; + + @Schema(description = "微信 openId", example = "31139") + private String openid; + + @Schema(description = "转账状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer transferStatus; + + @Schema(description = "转账订å•ç¼–å·", example = "23695") + private Long payTransferId; + + @Schema(description = "转账支付æˆåŠŸæ¸ é“") + private String payChannelCode; + + @Schema(description = "转账支付时间") + private LocalDateTime transferTime; +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/PayNotifyController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/PayNotifyController.java new file mode 100644 index 0000000..5335ec5 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/PayNotifyController.java @@ -0,0 +1,150 @@ +package com.tashow.cloud.pay.controller.admin.notify; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; +import com.tashow.cloud.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO; +import com.tashow.cloud.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; +import com.tashow.cloud.pay.controller.admin.notify.vo.PayNotifyTaskRespVO; +import com.tashow.cloud.pay.convert.notify.PayNotifyTaskConvert; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyLogDO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyTaskDO; +import com.tashow.cloud.pay.service.app.PayAppService; +import com.tashow.cloud.pay.service.channel.PayChannelService; +import com.tashow.cloud.pay.service.notify.PayNotifyService; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import com.tashow.cloud.pay.service.transfer.PayTransferService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.validation.Valid; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_FOUND; + +@Tag(name = "管ç†åŽå° - 回调通知") +@RestController +@RequestMapping("/pay/notify") +@Validated +@Slf4j +public class PayNotifyController { + + @Resource + private PayOrderService orderService; + @Resource + private PayRefundService refundService; + @Resource + private PayTransferService payTransferService; + @Resource + private PayNotifyService notifyService; + @Resource + private PayAppService appService; + @Resource + private PayChannelService channelService; + + @PostMapping(value = "/order/{channelId}") + @Operation(summary = "支付渠é“çš„ç»Ÿä¸€ã€æ”¯ä»˜ã€‘回调") + @PermitAll + public String notifyOrder(@PathVariable("channelId") Long channelId, + @RequestParam(required = false) Map params, + @RequestBody(required = false) String body) { + log.info("[notifyOrder][channelId({}) 回调数æ®({}/{})]", channelId, params, body); + // 1. æ ¡éªŒæ”¯ä»˜æ¸ é“æ˜¯å¦å­˜åœ¨ + PayClient payClient = channelService.getPayClient(channelId); + if (payClient == null) { + log.error("[notifyOrder][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", channelId); + throw exception(CHANNEL_NOT_FOUND); + } + + // 2. è§£æžé€šçŸ¥æ•°æ® + PayOrderRespDTO notify = payClient.parseOrderNotify(params, body); + orderService.notifyOrder(channelId, notify); + return "success"; + } + + @PostMapping(value = "/refund/{channelId}") + @Operation(summary = "支付渠é“的统一ã€é€€æ¬¾ã€‘回调") + @PermitAll + public String notifyRefund(@PathVariable("channelId") Long channelId, + @RequestParam(required = false) Map params, + @RequestBody(required = false) String body) { + log.info("[notifyRefund][channelId({}) 回调数æ®({}/{})]", channelId, params, body); + // 1. æ ¡éªŒæ”¯ä»˜æ¸ é“æ˜¯å¦å­˜åœ¨ + PayClient payClient = channelService.getPayClient(channelId); + if (payClient == null) { + log.error("[notifyRefund][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", channelId); + throw exception(CHANNEL_NOT_FOUND); + } + + // 2. è§£æžé€šçŸ¥æ•°æ® + PayRefundRespDTO notify = payClient.parseRefundNotify(params, body); + refundService.notifyRefund(channelId, notify); + return "success"; + } + + @PostMapping(value = "/transfer/{channelId}") + @Operation(summary = "支付渠é“的统一ã€è½¬è´¦ã€‘回调") + @PermitAll + public String notifyTransfer(@PathVariable("channelId") Long channelId, + @RequestParam(required = false) Map params, + @RequestBody(required = false) String body) { + log.info("[notifyTransfer][channelId({}) 回调数æ®({}/{})]", channelId, params, body); + // 1. æ ¡éªŒæ”¯ä»˜æ¸ é“æ˜¯å¦å­˜åœ¨ + PayClient payClient = channelService.getPayClient(channelId); + if (payClient == null) { + log.error("[notifyTransfer][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", channelId); + throw exception(CHANNEL_NOT_FOUND); + } + + // 2. è§£æžé€šçŸ¥æ•°æ® + PayTransferRespDTO notify = payClient.parseTransferNotify(params, body); + payTransferService.notifyTransfer(channelId, notify); + return "success"; + } + + @GetMapping("/get-detail") + @Operation(summary = "获得回调通知的明细") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('pay:notify:query')") + public CommonResult getNotifyTaskDetail(@RequestParam("id") Long id) { + PayNotifyTaskDO task = notifyService.getNotifyTask(id); + if (task == null) { + return success(null); + } + // 拼接返回 + PayAppDO app = appService.getApp(task.getAppId()); + List logs = notifyService.getNotifyLogList(id); + return success(PayNotifyTaskConvert.INSTANCE.convert(task, app, logs)); + } + + @GetMapping("/page") + @Operation(summary = "获得回调通知分页") + @PreAuthorize("@ss.hasPermission('pay:notify:query')") + public CommonResult> getNotifyTaskPage(@Valid PayNotifyTaskPageReqVO pageVO) { + PageResult pageResult = notifyService.getNotifyTaskPage(pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + // 拼接返回 + Map appMap = appService.getAppMap(convertList(pageResult.getList(), PayNotifyTaskDO::getAppId)); + return success(PayNotifyTaskConvert.INSTANCE.convertPage(pageResult, appMap)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskBaseVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskBaseVO.java new file mode 100644 index 0000000..c204898 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskBaseVO.java @@ -0,0 +1,45 @@ +package com.tashow.cloud.pay.controller.admin.notify.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 回调通知 Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class PayNotifyTaskBaseVO { + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10636") + private Long appId; + + @Schema(description = "通知类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Byte type; + + @Schema(description = "æ•°æ®ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "6722") + private Long dataId; + + @Schema(description = "通知状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Byte status; + + @Schema(description = "商户订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "26697") + private String merchantOrderId; + + @Schema(description = "下一次通知时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime nextNotifyTime; + + @Schema(description = "最åŽä¸€æ¬¡æ‰§è¡Œæ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime lastExecuteTime; + + @Schema(description = "当å‰é€šçŸ¥æ¬¡æ•°", requiredMode = Schema.RequiredMode.REQUIRED) + private Byte notifyTimes; + + @Schema(description = "最大å¯é€šçŸ¥æ¬¡æ•°", requiredMode = Schema.RequiredMode.REQUIRED) + private Byte maxNotifyTimes; + + @Schema(description = "异步通知地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") + private String notifyUrl; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskDetailRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskDetailRespVO.java new file mode 100644 index 0000000..b8467a1 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskDetailRespVO.java @@ -0,0 +1,54 @@ + +package com.tashow.cloud.pay.controller.admin.notify.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 回调通知的明细 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayNotifyTaskDetailRespVO extends PayNotifyTaskBaseVO { + + @Schema(description = "任务编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "æ›´æ–°æ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "应用åç§°", example = "wx_pay") + private String appName; + + @Schema(description = "回调日志列表") + private List logs; + + @Schema(description = "管ç†åŽå° - 回调日志") + @Data + public static class Log { + + @Schema(description = "日志编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "8848") + private Long id; + + @Schema(description = "通知状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Byte status; + + @Schema(description = "当å‰é€šçŸ¥æ¬¡æ•°", requiredMode = Schema.RequiredMode.REQUIRED) + private Byte notifyTimes; + + @Schema(description = "HTTP å“应结果", requiredMode = Schema.RequiredMode.REQUIRED) + private String response; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java new file mode 100644 index 0000000..bc275bc --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.pay.controller.admin.notify.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 PayNotifyTaskPageReqVO extends PageParam { + + @Schema(description = "应用编å·", example = "10636") + private Long appId; + + @Schema(description = "通知类型", example = "2") + private Integer type; + + @Schema(description = "æ•°æ®ç¼–å·", example = "6722") + private Long dataId; + + @Schema(description = "通知状æ€", example = "1") + private Integer status; + + @Schema(description = "商户订å•ç¼–å·", example = "26697") + private String merchantOrderId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskRespVO.java new file mode 100644 index 0000000..3000767 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/notify/vo/PayNotifyTaskRespVO.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.pay.controller.admin.notify.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 回调通知 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayNotifyTaskRespVO extends PayNotifyTaskBaseVO { + + @Schema(description = "任务编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "应用åç§°", example = "wx_pay") + private String appName; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/PayOrderController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/PayOrderController.java new file mode 100644 index 0000000..9345f15 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/PayOrderController.java @@ -0,0 +1,143 @@ +package com.tashow.cloud.pay.controller.admin.order; + +import cn.hutool.core.collection.CollectionUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*; +import com.tashow.cloud.pay.controller.admin.order.vo.*; +import com.tashow.cloud.payapi.controller.admin.order.vo.*; +import com.tashow.cloud.pay.convert.order.PayOrderConvert; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderExtensionDO; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import com.tashow.cloud.pay.framework.pay.core.WalletPayClient; +import com.tashow.cloud.pay.service.app.PayAppService; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.google.common.collect.Maps; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType; + +@Tag(name = "管ç†åŽå° - 支付订å•") +@RestController +@RequestMapping("/pay/order") +@Validated +public class PayOrderController { + + @Resource + private PayOrderService orderService; + @Resource + private PayAppService appService; + + @GetMapping("/get") + @Operation(summary = "获得支付订å•") + @Parameters({ + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024"), + @Parameter(name = "sync", description = "是å¦åŒæ­¥", example = "true") + }) + @PreAuthorize("@ss.hasPermission('pay:order:query')") + public CommonResult getOrder(@RequestParam("id") Long id, + @RequestParam(value = "sync", required = false) Boolean sync) { + PayOrderDO order = orderService.getOrder(id); + // sync 仅在等待支付 + if (Boolean.TRUE.equals(sync) && PayOrderStatusEnum.isWaiting(order.getStatus())) { + orderService.syncOrderQuietly(order.getId()); + // 釿–°æŸ¥è¯¢ï¼Œå› ä¸ºåŒæ­¥åŽï¼Œå¯èƒ½ä¼šæœ‰å˜åŒ– + order = orderService.getOrder(id); + } + return success(BeanUtils.toBean(order, PayOrderRespVO.class)); + } + + @GetMapping("/get-detail") + @Operation(summary = "获得支付订å•详情") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('pay:order:query')") + public CommonResult getOrderDetail(@RequestParam("id") Long id) { + PayOrderDO order = orderService.getOrder(id); + if (order == null) { + return success(null); + } + + // 拼接返回 + PayAppDO app = appService.getApp(order.getAppId()); + PayOrderExtensionDO orderExtension = orderService.getOrderExtension(order.getExtensionId()); + return success(PayOrderConvert.INSTANCE.convert(order, orderExtension, app)); + } + + @PostMapping("/submit") + @Operation(summary = "æäº¤æ”¯ä»˜è®¢å•") + public CommonResult submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) { + // 1. 钱包支付事,需è¦é¢å¤–ä¼  user_id å’Œ user_type + if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) { + Map channelExtras = reqVO.getChannelExtras() == null ? + Maps.newHashMapWithExpectedSize(2) : reqVO.getChannelExtras(); + channelExtras.put(WalletPayClient.USER_ID_KEY, String.valueOf(getLoginUserId())); + channelExtras.put(WalletPayClient.USER_TYPE_KEY, String.valueOf(getLoginUserType())); + reqVO.setChannelExtras(channelExtras); + } + + // 2. æäº¤æ”¯ä»˜ + PayOrderSubmitRespVO respVO = orderService.submitOrder(reqVO, getClientIP()); + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得支付订å•分页") + @PreAuthorize("@ss.hasPermission('pay:order:query')") + public CommonResult> getOrderPage(@Valid PayOrderPageReqVO pageVO) { + PageResult pageResult = orderService.getOrderPage(pageVO); + if (CollectionUtil.isEmpty(pageResult.getList())) { + return success(new PageResult<>(pageResult.getTotal())); + } + + // 拼接返回 + Map appMap = appService.getAppMap(convertList(pageResult.getList(), PayOrderDO::getAppId)); + return success(PayOrderConvert.INSTANCE.convertPage(pageResult, appMap)); + } + + @GetMapping("/export-excel") + @Operation(summary = "å¯¼å‡ºæ”¯ä»˜è®¢å• Excel") + @PreAuthorize("@ss.hasPermission('pay:order:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportOrderExcel(@Valid PayOrderExportReqVO exportReqVO, + HttpServletResponse response) throws IOException { + List list = orderService.getOrderList(exportReqVO); + if (CollectionUtil.isEmpty(list)) { + ExcelUtils.write(response, "支付订å•.xls", "æ•°æ®", + PayOrderExcelVO.class, new ArrayList<>()); + return; + } + + // 拼接返回 + Map appMap = appService.getAppMap(convertList(list, PayOrderDO::getAppId)); + List excelList = PayOrderConvert.INSTANCE.convertList(list, appMap); + // 导出 Excel + ExcelUtils.write(response, "支付订å•.xls", "æ•°æ®", PayOrderExcelVO.class, excelList); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderBaseVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderBaseVO.java new file mode 100644 index 0000000..9279961 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderBaseVO.java @@ -0,0 +1,90 @@ +package com.tashow.cloud.pay.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +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; + +/** + * æ”¯ä»˜è®¢å• Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + * + * @author aquan + */ +@Data +public class PayOrderBaseVO { + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "应用编å·ä¸èƒ½ä¸ºç©º") + private Long appId; + + @Schema(description = "渠é“ç¼–å·", example = "2048") + private Long channelId; + + @Schema(description = "渠é“ç¼–ç ", example = "wx_app") + private String channelCode; + + @Schema(description = "商户订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") + @NotNull(message = "商户订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private String merchantOrderId; + + @Schema(description = "商哿 ‡é¢˜", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") + @NotNull(message = "商哿 ‡é¢˜ä¸èƒ½ä¸ºç©º") + private String subject; + + @Schema(description = "å•†å“æè¿°", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是土豆") + @NotNull(message = "å•†å“æè¿°ä¸èƒ½ä¸ºç©º") + private String body; + + @Schema(description = "异步通知地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://127.0.0.1:48080/pay/notify") + @NotNull(message = "异步通知地å€ä¸èƒ½ä¸ºç©º") + private String notifyUrl; + + @Schema(description = "支付金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "支付金é¢ï¼Œå•ä½ï¼šåˆ†ä¸èƒ½ä¸ºç©º") + private Long price; + + @Schema(description = "æ¸ é“æ‰‹ç»­è´¹ï¼Œå•ä½ï¼šç™¾åˆ†æ¯”", example = "10") + private Double channelFeeRate; + + @Schema(description = "æ¸ é“æ‰‹ç»­é‡‘é¢ï¼Œå•ä½ï¼šåˆ†", example = "100") + private Integer channelFeePrice; + + @Schema(description = "支付状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "支付状æ€ä¸èƒ½ä¸ºç©º") + private Integer status; + + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") + @NotNull(message = "用户 IPä¸èƒ½ä¸ºç©º") + private String userIp; + + @Schema(description = "订å•失效时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "订å•失效时间ä¸èƒ½ä¸ºç©º") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime expireTime; + + @Schema(description = "è®¢å•æ”¯ä»˜æˆåŠŸæ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime successTime; + + @Schema(description = "支付æˆåŠŸçš„è®¢å•æ‹“展å•ç¼–å·", example = "50") + private Long extensionId; + + @Schema(description = "支付订å•å·", example = "2048888") + private String no; + + @Schema(description = "退款总金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "退款总金é¢ï¼Œå•ä½ï¼šåˆ†ä¸èƒ½ä¸ºç©º") + private Long refundPrice; + + @Schema(description = "渠é“用户编å·", example = "2048") + private String channelUserId; + + @Schema(description = "渠é“订å•å·", example = "4096") + private String channelOrderNo; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderDetailsRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderDetailsRespVO.java new file mode 100644 index 0000000..0cda935 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderDetailsRespVO.java @@ -0,0 +1,45 @@ +package com.tashow.cloud.pay.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 支付订å•è¯¦ç»†ä¿¡æ¯ Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayOrderDetailsRespVO extends PayOrderBaseVO { + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "应用åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "èŠ‹é“æºç ") + private String appName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "æ›´æ–°æ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + /** + * æ”¯ä»˜è®¢å•æ‰©å±• + */ + private PayOrderExtension extension; + + @Data + @Schema(description = "æ”¯ä»˜è®¢å•æ‰©å±•") + public static class PayOrderExtension { + + @Schema(description = "支付订å•å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String no; + + @Schema(description = "支付异步通知的内容") + private String channelNotifyData; + + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderExcelVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderExcelVO.java new file mode 100644 index 0000000..8f9b422 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderExcelVO.java @@ -0,0 +1,67 @@ +package com.tashow.cloud.pay.controller.admin.order.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert; +import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * æ”¯ä»˜è®¢å• Excel VO + * + * @author aquan + */ +@Data +public class PayOrderExcelVO { + + @ExcelProperty("ç¼–å·") + private Long id; + + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @ExcelProperty(value = "支付金é¢", converter = MoneyConvert.class) + private Integer price; + + @ExcelProperty(value = "退款金é¢", converter = MoneyConvert.class) + private Integer refundPrice; + + @ExcelProperty(value = "手续金é¢", converter = MoneyConvert.class) + private Integer channelFeePrice; + + @ExcelProperty("商户å•å·") + private String merchantOrderId; + + @ExcelProperty(value = "支付å•å·") + private String no; + + @ExcelProperty("渠é“å•å·") + private String channelOrderNo; + + @ExcelProperty(value = "支付状æ€", converter = DictConvert.class) + @DictFormat(DictTypeConstants.ORDER_STATUS) + private Integer status; + + @ExcelProperty(value = "渠é“ç¼–å·åç§°", converter = DictConvert.class) + @DictFormat(DictTypeConstants.CHANNEL_CODE) + private String channelCode; + + @ExcelProperty("è®¢å•æ”¯ä»˜æˆåŠŸæ—¶é—´") + private LocalDateTime successTime; + + @ExcelProperty("订å•失效时间") + private LocalDateTime expireTime; + + @ExcelProperty(value = "应用åç§°") + private String appName; + + @ExcelProperty("商哿 ‡é¢˜") + private String subject; + + @ExcelProperty("å•†å“æè¿°") + private String body; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderExportReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderExportReqVO.java new file mode 100644 index 0000000..566fdd8 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderExportReqVO.java @@ -0,0 +1,37 @@ +package com.tashow.cloud.pay.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +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 = "管ç†åŽå° - æ”¯ä»˜è®¢å• Excel 导出 Request VOï¼Œå‚æ•°å’Œ PayOrderPageReqVO 是一致的") +@Data +public class PayOrderExportReqVO { + + @Schema(description = "应用编å·", example = "1024") + private Long appId; + + @Schema(description = "渠é“ç¼–ç ", example = "wx_app") + private String channelCode; + + @Schema(description = "商户订å•ç¼–å·", example = "4096") + private String merchantOrderId; + + @Schema(description = "渠é“ç¼–å·", example = "1888") + private String channelOrderNo; + + @Schema(description = "支付å•å·", example = "2014888") + private String no; + + @Schema(description = "支付状æ€", example = "0") + private Integer status; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderPageItemRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderPageItemRespVO.java new file mode 100644 index 0000000..ae35a7c --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderPageItemRespVO.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.pay.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 支付订å•分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayOrderPageItemRespVO extends PayOrderBaseVO { + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "应用åç§°", example = "wx_pay") + private String appName; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderPageReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderPageReqVO.java new file mode 100644 index 0000000..d400287 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderPageReqVO.java @@ -0,0 +1,42 @@ +package com.tashow.cloud.pay.controller.admin.order.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 PayOrderPageReqVO extends PageParam { + + @Schema(description = "应用编å·", example = "1024") + private Long appId; + + @Schema(description = "渠é“ç¼–ç ", example = "wx_app") + private String channelCode; + + @Schema(description = "商户订å•ç¼–å·", example = "4096") + private String merchantOrderId; + + @Schema(description = "渠é“ç¼–å·", example = "1888") + private String channelOrderNo; + + @Schema(description = "支付å•å·", example = "2014888") + private String no; + + @Schema(description = "支付状æ€", example = "0") + private Integer status; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderRespVO.java new file mode 100644 index 0000000..3e1fbc1 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.pay.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - æ”¯ä»˜è®¢å• Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayOrderRespVO extends PayOrderBaseVO { + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED) + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderSubmitReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderSubmitReqVO.java new file mode 100644 index 0000000..963d14f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderSubmitReqVO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.pay.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import java.util.Map; + +@Schema(description = "管ç†åŽå° - æ”¯ä»˜è®¢å•æäº¤ Request VO") +@Data +public class PayOrderSubmitReqVO { + + @Schema(description = "支付å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "支付å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "支付渠é“", requiredMode = Schema.RequiredMode.REQUIRED, example = "wx_pub") + @NotEmpty(message = "支付渠é“ä¸èƒ½ä¸ºç©º") + private String channelCode; + + @Schema(description = "支付渠é“çš„é¢å¤–傿•°ï¼Œä¾‹å¦‚说,微信公众å·éœ€è¦ä¼ é€’ openid 傿•°") + private Map channelExtras; + + @Schema(description = "展示模å¼", example = "url") // å‚è§ {@link PayDisplayModeEnum} 枚举。如果ä¸ä¼ é€’,则æ¯ä¸ªæ”¯ä»˜æ¸ é“ä½¿ç”¨é»˜è®¤çš„æ–¹å¼ + private String displayMode; + + @Schema(description = "回跳地å€") + @URL(message = "回跳地å€çš„æ ¼å¼å¿…须是 URL") + private String returnUrl; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderSubmitRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderSubmitRespVO.java new file mode 100644 index 0000000..dd19139 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/order/vo/PayOrderSubmitRespVO.java @@ -0,0 +1,18 @@ +package com.tashow.cloud.pay.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - æ”¯ä»˜è®¢å•æäº¤ Response VO") +@Data +public class PayOrderSubmitRespVO { + + @Schema(description = "支付状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") // å‚è§ PayOrderStatusEnum 枚举 + private Integer status; + + @Schema(description = "展示模å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "url") // å‚è§ PayDisplayModeEnum 枚举 + private String displayMode; + @Schema(description = "展示内容", requiredMode = Schema.RequiredMode.REQUIRED) + private String displayContent; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/PayRefundController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/PayRefundController.java new file mode 100644 index 0000000..37e894b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/PayRefundController.java @@ -0,0 +1,98 @@ +package com.tashow.cloud.pay.controller.admin.refund; + +import cn.hutool.core.collection.CollectionUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.*; +import com.tashow.cloud.pay.controller.admin.refund.vo.*; +import com.tashow.cloud.payapi.controller.admin.refund.vo.*; +import com.tashow.cloud.pay.convert.refund.PayRefundConvert; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import com.tashow.cloud.pay.service.app.PayAppService; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +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 java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管ç†åŽå° - 退款订å•") +@RestController +@RequestMapping("/pay/refund") +@Validated +public class PayRefundController { + + @Resource + private PayRefundService refundService; + @Resource + private PayAppService appService; + + @GetMapping("/get") + @Operation(summary = "获得退款订å•") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('pay:refund:query')") + public CommonResult getRefund(@RequestParam("id") Long id) { + PayRefundDO refund = refundService.getRefund(id); + if (refund == null) { + return success(new PayRefundDetailsRespVO()); + } + + // æ‹¼æŽ¥æ•°æ® + PayAppDO app = appService.getApp(refund.getAppId()); + return success(PayRefundConvert.INSTANCE.convert(refund, app)); + } + + @GetMapping("/page") + @Operation(summary = "获得退款订å•分页") + @PreAuthorize("@ss.hasPermission('pay:refund:query')") + public CommonResult> getRefundPage(@Valid PayRefundPageReqVO pageVO) { + PageResult pageResult = refundService.getRefundPage(pageVO); + if (CollectionUtil.isEmpty(pageResult.getList())) { + return success(new PageResult<>(pageResult.getTotal())); + } + + // 处ç†åº”用IDæ•°æ® + Map appMap = appService.getAppMap(convertList(pageResult.getList(), PayRefundDO::getAppId)); + return success(PayRefundConvert.INSTANCE.convertPage(pageResult, appMap)); + } + + @GetMapping("/export-excel") + @Operation(summary = "å¯¼å‡ºé€€æ¬¾è®¢å• Excel") + @PreAuthorize("@ss.hasPermission('pay:refund:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportRefundExcel(@Valid PayRefundExportReqVO exportReqVO, + HttpServletResponse response) throws IOException { + List list = refundService.getRefundList(exportReqVO); + if (CollectionUtil.isEmpty(list)) { + ExcelUtils.write(response, "退款订å•.xls", "æ•°æ®", + PayRefundExcelVO.class, new ArrayList<>()); + return; + } + + // 拼接返回 + Map appMap = appService.getAppMap(convertList(list, PayRefundDO::getAppId)); + List excelList = PayRefundConvert.INSTANCE.convertList(list, appMap); + // 导出 Excel + ExcelUtils.write(response, "退款订å•.xls", "æ•°æ®", PayRefundExcelVO.class, excelList); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundBaseVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundBaseVO.java new file mode 100644 index 0000000..12b4084 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundBaseVO.java @@ -0,0 +1,78 @@ +package com.tashow.cloud.pay.controller.admin.refund.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** +* é€€æ¬¾è®¢å• Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 +* å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ +*/ +@Data +public class PayRefundBaseVO { + + @Schema(description = "外部退款å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "110") + private String no; + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long appId; + + @Schema(description = "渠é“ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long channelId; + + @Schema(description = "渠é“ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "wx_app") + private String channelCode; + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderId; + + // ========== 商户相关字段 ========== + + @Schema(description = "商户订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "225") + private String merchantOrderId; + + @Schema(description = "商户退款订å•å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "512") + private String merchantRefundId; + + @Schema(description = "异步通知地å€", requiredMode = Schema.RequiredMode.REQUIRED) + private String notifyUrl; + + // ========== 退款相关字段 ========== + + @Schema(description = "退款状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer status; + + @Schema(description = "支付金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Long payPrice; + + @Schema(description = "退款金é¢,å•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") + private Long refundPrice; + + @Schema(description = "退款原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "我è¦é€€äº†") + private String reason; + + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") + private String userIp; + + // ========== 渠é“相关字段 ========== + + @Schema(description = "渠é“订å•å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "233") + private String channelOrderNo; + + @Schema(description = "渠é“退款å•å·", example = "2022") + private String channelRefundNo; + + @Schema(description = "退款æˆåŠŸæ—¶é—´") + private LocalDateTime successTime; + + @Schema(description = "调用渠é“的错误ç ") + private String channelErrorCode; + + @Schema(description = "调用渠é“的错误æç¤º") + private String channelErrorMsg; + + @Schema(description = "支付渠é“çš„é¢å¤–傿•°") + private String channelNotifyData; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundDetailsRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundDetailsRespVO.java new file mode 100644 index 0000000..ab0ca7d --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundDetailsRespVO.java @@ -0,0 +1,40 @@ +package com.tashow.cloud.pay.controller.admin.refund.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 退款订å•详情 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayRefundDetailsRespVO extends PayRefundBaseVO { + + @Schema(description = "支付退款编å·", requiredMode = Schema.RequiredMode.REQUIRED) + private Long id; + + @Schema(description = "应用åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是芋艿") + private String appName; + + @Schema(description = "支付订å•", requiredMode = Schema.RequiredMode.REQUIRED) + private Order order; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "æ›´æ–°æ—¶é—´") + private LocalDateTime updateTime; + + @Schema(description = "管ç†åŽå° - 支付订å•") + @Data + public static class Order { + + @Schema(description = "商哿 ‡é¢˜", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") + private String subject; + + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundExcelVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundExcelVO.java new file mode 100644 index 0000000..9bf9a71 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundExcelVO.java @@ -0,0 +1,61 @@ +package com.tashow.cloud.pay.controller.admin.refund.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert; +import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * é€€æ¬¾è®¢å• Excel VO + * + * @author aquan + */ +@Data +public class PayRefundExcelVO { + + @ExcelProperty("支付退款编å·") + private Long id; + + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @ExcelProperty(value = "支付金é¢", converter = MoneyConvert.class) + private Integer payPrice; + + @ExcelProperty(value = "退款金é¢", converter = MoneyConvert.class) + private Integer refundPrice; + + @ExcelProperty("商户退款å•å·") + private String merchantRefundId; + @ExcelProperty("退款å•å·") + private String no; + @ExcelProperty("渠é“退款å•å·") + private String channelRefundNo; + + @ExcelProperty("商户支付å•å·") + private String merchantOrderId; + @ExcelProperty("æ¸ é“æ”¯ä»˜å•å·") + private String channelOrderNo; + + @ExcelProperty(value = "退款状æ€", converter = DictConvert.class) + @DictFormat(DictTypeConstants.REFUND_STATUS) + private Integer status; + + @ExcelProperty(value = "退款渠é“", converter = DictConvert.class) + @DictFormat(DictTypeConstants.CHANNEL_CODE) + private String channelCode; + + @ExcelProperty("æˆåŠŸæ—¶é—´") + private LocalDateTime successTime; + + @ExcelProperty(value = "支付应用") + private String appName; + + @ExcelProperty("退款原因") + private String reason; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundExportReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundExportReqVO.java new file mode 100644 index 0000000..9aee454 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundExportReqVO.java @@ -0,0 +1,40 @@ +package com.tashow.cloud.pay.controller.admin.refund.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +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 = "管ç†åŽå° - é€€æ¬¾è®¢å• Excel 导出 Request VOï¼Œå‚æ•°å’Œ PayRefundPageReqVO 是一致的") +@Data +public class PayRefundExportReqVO { + + @Schema(description = "应用编å·", example = "1024") + private Long appId; + + @Schema(description = "渠é“ç¼–ç ", example = "wx_app") + private String channelCode; + + @Schema(description = "商户支付å•å·", example = "10") + private String merchantOrderId; + + @Schema(description = "商户退款å•å·", example = "20") + private String merchantRefundId; + + @Schema(description = "æ¸ é“æ”¯ä»˜å•å·", example = "30") + private String channelOrderNo; + + @Schema(description = "渠é“退款å•å·", example = "40") + private String channelRefundNo; + + @Schema(description = "退款状æ€", example = "0") + private Integer status; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundPageItemRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundPageItemRespVO.java new file mode 100644 index 0000000..3d652a4 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundPageItemRespVO.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.pay.controller.admin.refund.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 退款订å•分页查询 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayRefundPageItemRespVO extends PayRefundBaseVO { + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "应用åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是芋艿") + private String appName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundPageReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundPageReqVO.java new file mode 100644 index 0000000..4f573cc --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/refund/vo/PayRefundPageReqVO.java @@ -0,0 +1,45 @@ +package com.tashow.cloud.pay.controller.admin.refund.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 PayRefundPageReqVO extends PageParam { + + @Schema(description = "应用编å·", example = "1024") + private Long appId; + + @Schema(description = "渠é“ç¼–ç ", example = "wx_app") + private String channelCode; + + @Schema(description = "商户支付å•å·", example = "10") + private String merchantOrderId; + + @Schema(description = "商户退款å•å·", example = "20") + private String merchantRefundId; + + @Schema(description = "æ¸ é“æ”¯ä»˜å•å·", example = "30") + private String channelOrderNo; + + @Schema(description = "渠é“退款å•å·", example = "40") + private String channelRefundNo; + + @Schema(description = "退款状æ€", example = "0") + private Integer status; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/PayTransferController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/PayTransferController.java new file mode 100644 index 0000000..e523b1e --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/PayTransferController.java @@ -0,0 +1,53 @@ +package com.tashow.cloud.pay.controller.admin.transfer; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.*; +import com.tashow.cloud.pay.controller.admin.transfer.vo.*; +import com.tashow.cloud.payapi.controller.admin.transfer.vo.*; +import com.tashow.cloud.pay.convert.transfer.PayTransferConvert; +import com.tashow.cloud.pay.dal.dataobject.transfer.PayTransferDO; +import com.tashow.cloud.pay.service.transfer.PayTransferService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; + +@Tag(name = "管ç†åŽå° - 转账å•") +@RestController +@RequestMapping("/pay/transfer") +@Validated +public class PayTransferController { + + @Resource + private PayTransferService payTransferService; + + @PostMapping("/create") + @Operation(summary = "创建转账å•,å‘起转账") + @PreAuthorize("@ss.hasPermission('pay:transfer:create')") + public CommonResult createPayTransfer(@Valid @RequestBody PayTransferCreateReqVO reqVO) { + PayTransferDO payTransfer = payTransferService.createTransfer(reqVO, getClientIP()); + return success(new PayTransferCreateRespVO().setId(payTransfer.getId()).setStatus(payTransfer.getStatus())); + } + + @GetMapping("/get") + @Operation(summary = "获得转账订å•") + @PreAuthorize("@ss.hasPermission('pay:transfer:query')") + public CommonResult getTransfer(@RequestParam("id") Long id) { + return success(PayTransferConvert.INSTANCE.convert(payTransferService.getTransfer(id))); + } + + @GetMapping("/page") + @Operation(summary = "获得转账订å•分页") + @PreAuthorize("@ss.hasPermission('pay:transfer:query')") + public CommonResult> getTransferPage(@Valid PayTransferPageReqVO pageVO) { + PageResult pageResult = payTransferService.getTransferPage(pageVO); + return success(PayTransferConvert.INSTANCE.convertPage(pageResult)); + } +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferCreateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferCreateReqVO.java new file mode 100644 index 0000000..809bedc --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferCreateReqVO.java @@ -0,0 +1,95 @@ +package com.tashow.cloud.pay.controller.admin.transfer.vo; + +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Validator; +import jakarta.validation.constraints.*; +import lombok.Data; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum.*; + +@Schema(description = "管ç†åŽå° - å‘起转账 Request VO") +@Data +public class PayTransferCreateReqVO { + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "应用编å·ä¸èƒ½ä¸ºç©º") + private Long appId; + + @Schema(description = "商户转账å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "商户转账å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private String merchantTransferId; + + @Schema(description = "转账类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "转账类型ä¸èƒ½ä¸ºç©º") + @InEnum(PayTransferTypeEnum.class) + private Integer type; + + @Schema(description = "转账渠é“", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_pc") + @NotEmpty(message = "转账渠é“ä¸èƒ½ä¸ºç©º") + private String channelCode; + + @Min(value = 1, message = "转账金é¢å¿…须大于零") + @NotNull(message = "转账金é¢ä¸èƒ½ä¸ºç©º") + private Integer price; + + @Schema(description = "转账标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "示例转账") + @NotEmpty(message = "转账标题ä¸èƒ½ä¸ºç©º") + private String subject; + + @Schema(description = "收款人姓å", example = "test1") + @NotBlank(message = "收款人姓åä¸èƒ½ä¸ºç©º", groups = {Alipay.class}) + private String userName; + + @Schema(description = "支付å®ç™»å½•å·", example = "test1@sandbox.com") + @NotBlank(message = "支付å®ç™»å½•å·ä¸èƒ½ä¸ºç©º", groups = {Alipay.class}) + private String alipayLogonId; + + @Schema(description = "微信 openId", example = "oLefc4g5Gxx") + @NotBlank(message = "微信 openId ä¸èƒ½ä¸ºç©º", groups = {WxPay.class}) + private String openid; + + @Schema(description = "转账渠é“çš„é¢å¤–傿•°") + private Map channelExtras; + + public void validate(Validator validator) { + PayTransferTypeEnum transferType = typeOf(type); + switch (transferType) { + case ALIPAY_BALANCE: { + ValidationUtils.validate(validator, this, Alipay.class); + break; + } + case WX_BALANCE: { + ValidationUtils.validate(validator, this, WxPay.class); + break; + } + default: { + throw new UnsupportedOperationException("待实现"); + } + } + } + + @AssertTrue(message = "转账类型和转账渠é“ä¸åŒ¹é…") + public boolean isValidChannelCode() { + PayTransferTypeEnum transferType = typeOf(type); + switch (transferType) { + case ALIPAY_BALANCE: { + return PayChannelEnum.isAlipay(channelCode); + } + case WX_BALANCE: + case BANK_CARD: + case WALLET_BALANCE: { + throw exception(NOT_IMPLEMENTED); + } + } + return Boolean.FALSE; + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferCreateRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferCreateRespVO.java new file mode 100644 index 0000000..b2ef0e9 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferCreateRespVO.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.pay.controller.admin.transfer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - å‘起转账 Response VO") +@Data +public class PayTransferCreateRespVO { + + @Schema(description = "转账å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "转账状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // å‚è§ PayTransferStatusEnum 枚举 + private Integer status; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferPageItemRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferPageItemRespVO.java new file mode 100644 index 0000000..4e67881 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferPageItemRespVO.java @@ -0,0 +1,62 @@ +package com.tashow.cloud.pay.controller.admin.transfer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @author jason + */ +@Schema(description = "管ç†åŽå° - 转账å•分页项 Response VO") +@Data +public class PayTransferPageItemRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2931") + private Long id; + + @Schema(description = "转账å•å·", requiredMode = Schema.RequiredMode.REQUIRED) + private String no; + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "12831") + private Long appId; + + @Schema(description = "转账渠é“ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "24833") + private Long channelId; + + @Schema(description = "转账渠é“ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED) + private String channelCode; + + @Schema(description = "商户转账å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "17481") + private String merchantTransferId; + + @Schema(description = "类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer type; + + @Schema(description = "转账状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer status; + + @Schema(description = "转账æˆåŠŸæ—¶é—´") + private LocalDateTime successTime; + + @Schema(description = "转账金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "964") + private Integer price; + + @Schema(description = "转账标题", requiredMode = Schema.RequiredMode.REQUIRED) + private String subject; + + @Schema(description = "收款人姓å", example = "王五") + private String userName; + + @Schema(description = "支付å®ç™»å½•å·", example = "29245") + private String alipayLogonId; + + @Schema(description = "微信 openId", example = "26589") + private String openid; + + @Schema(description = "渠é“转账å•å·") + private String channelTransferNo; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferPageReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferPageReqVO.java new file mode 100644 index 0000000..c5f0494 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferPageReqVO.java @@ -0,0 +1,48 @@ +package com.tashow.cloud.pay.controller.admin.transfer.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 PayTransferPageReqVO extends PageParam { + + @Schema(description = "转账å•å·") + private String no; + + @Schema(description = "应用编å·", example = "12831") + private Long appId; + + @Schema(description = "渠é“ç¼–ç ", example = "wx_app") + private String channelCode; + + @Schema(description = "商户转账å•ç¼–å·", example = "17481") + private String merchantTransferId; + + @Schema(description = "类型", example = "2") + private Integer type; + + @Schema(description = "转账状æ€", example = "2") + private Integer status; + + @Schema(description = "收款人姓å", example = "王五") + private String userName; + + @Schema(description = "渠é“转账å•å·") + private String channelTransferNo; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferRespVO.java new file mode 100644 index 0000000..fc7fb8d --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/transfer/vo/PayTransferRespVO.java @@ -0,0 +1,79 @@ +package com.tashow.cloud.pay.controller.admin.transfer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Map; + +@Schema(description = "管ç†åŽå° - è½¬è´¦å• Response VO") +@Data +public class PayTransferRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2931") + private Long id; + + @Schema(description = "转账å•å·", requiredMode = Schema.RequiredMode.REQUIRED) + private String no; + + @Schema(description = "应用编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "12831") + private Long appId; + + @Schema(description = "转账渠é“ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "24833") + private Long channelId; + + @Schema(description = "转账渠é“ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED) + private String channelCode; + + @Schema(description = "商户转账å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "17481") + private String merchantTransferId; + + @Schema(description = "类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer type; + + @Schema(description = "转账状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer status; + + @Schema(description = "转账æˆåŠŸæ—¶é—´") + private LocalDateTime successTime; + + @Schema(description = "转账金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "964") + private Integer price; + + @Schema(description = "转账标题", requiredMode = Schema.RequiredMode.REQUIRED) + private String subject; + + @Schema(description = "收款人姓å", example = "王五") + private String userName; + + @Schema(description = "支付å®ç™»å½•å·", example = "29245") + private String alipayLogonId; + + @Schema(description = "微信 openId", example = "26589") + private String openid; + + @Schema(description = "异步通知商户地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") + private String notifyUrl; + + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED) + private String userIp; + + @Schema(description = "渠é“çš„é¢å¤–傿•°") + private Map channelExtras; + + @Schema(description = "渠é“转账å•å·") + private String channelTransferNo; + + @Schema(description = "调用渠é“的错误ç ") + private String channelErrorCode; + + @Schema(description = "调用渠é“的错误æç¤º") + private String channelErrorMsg; + + @Schema(description = "渠é“çš„åŒæ­¥/异步通知的内容") + private String channelNotifyData; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletController.java new file mode 100644 index 0000000..c11581b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletController.java @@ -0,0 +1,70 @@ +package com.tashow.cloud.pay.controller.admin.wallet; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.wallet.PayWalletPageReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.wallet.PayWalletRespVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.wallet.PayWalletUpdateBalanceReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.wallet.PayWalletUserReqVO; +import com.tashow.cloud.pay.convert.wallet.PayWalletConvert; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletDO; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import com.tashow.cloud.pay.service.wallet.PayWalletService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.enums.UserTypeEnum.MEMBER; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.WALLET_NOT_FOUND; + +@Tag(name = "管ç†åŽå° - 用户钱包") +@RestController +@RequestMapping("/pay/wallet") +@Validated +@Slf4j +public class PayWalletController { + + @Resource + private PayWalletService payWalletService; + + @GetMapping("/get") + @PreAuthorize("@ss.hasPermission('pay:wallet:query')") + @Operation(summary = "获得用户钱包明细") + public CommonResult getWallet(PayWalletUserReqVO reqVO) { + PayWalletDO wallet = payWalletService.getOrCreateWallet(reqVO.getUserId(), MEMBER.getValue()); + return success(PayWalletConvert.INSTANCE.convert02(wallet)); + } + + @GetMapping("/page") + @Operation(summary = "获得会员钱包分页") + @PreAuthorize("@ss.hasPermission('pay:wallet:query')") + public CommonResult> getWalletPage(@Valid PayWalletPageReqVO pageVO) { + PageResult pageResult = payWalletService.getWalletPage(pageVO); + return success(PayWalletConvert.INSTANCE.convertPage(pageResult)); + } + + @PutMapping("/update-balance") + @Operation(summary = "更新会员用户余é¢") + @PreAuthorize("@ss.hasPermission('pay:wallet:update-balance')") + public CommonResult updateWalletBalance(@Valid @RequestBody PayWalletUpdateBalanceReqVO updateReqVO) { + // 获得用户钱包 + PayWalletDO wallet = payWalletService.getOrCreateWallet(updateReqVO.getUserId(), MEMBER.getValue()); + if (wallet == null) { + log.error("[updateWalletBalance],updateReqVO({}) 用户钱包ä¸å­˜åœ¨.", updateReqVO); + throw exception(WALLET_NOT_FOUND); + } + + // æ›´æ–°é’±åŒ…ä½™é¢ + payWalletService.addWalletBalance(wallet.getId(), String.valueOf(updateReqVO.getUserId()), + PayWalletBizTypeEnum.UPDATE_BALANCE, updateReqVO.getBalance()); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletRechargeController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletRechargeController.java new file mode 100644 index 0000000..cb10401 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletRechargeController.java @@ -0,0 +1,57 @@ +package com.tashow.cloud.pay.controller.admin.wallet; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; +import com.tashow.cloud.pay.service.wallet.PayWalletRechargeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; + +@Tag(name = "管ç†åŽå° - 钱包充值") +@RestController +@RequestMapping("/pay/wallet-recharge") +@Validated +@Slf4j +public class PayWalletRechargeController { + + @Resource + private PayWalletRechargeService walletRechargeService; + + @PostMapping("/update-paid") + @Operation(summary = "更新钱包充值为已充值") // ç”± pay-module 支付æœåŠ¡ï¼Œè¿›è¡Œå›žè°ƒï¼Œå¯è§ PayNotifyJob + @PermitAll // 无需登录, 内部校验实现 + public CommonResult updateWalletRechargerPaid(@Valid @RequestBody PayOrderNotifyReqDTO notifyReqDTO) { + walletRechargeService.updateWalletRechargerPaid(Long.valueOf(notifyReqDTO.getMerchantOrderId()), + notifyReqDTO.getPayOrderId()); + return success(true); + } + + // TODO @jason:å‘èµ·é€€æ¬¾ï¼Œè¦ post æ“作哈; + @GetMapping("/refund") + @Operation(summary = "å‘起钱包充值退款") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + public CommonResult refundWalletRecharge(@RequestParam("id") Long id) { + walletRechargeService.refundWalletRecharge(id, getClientIP()); + return success(true); + } + + @PostMapping("/update-refunded") + @Operation(summary = "更新钱包充值为已退款") // ç”± pay-module 支付æœåŠ¡ï¼Œè¿›è¡Œå›žè°ƒï¼Œå¯è§ PayNotifyJob + @PermitAll // 无需登录, 内部校验实现 + public CommonResult updateWalletRechargeRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { + walletRechargeService.updateWalletRechargeRefunded( + Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId()); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletRechargePackageController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletRechargePackageController.java new file mode 100644 index 0000000..0b081d1 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletRechargePackageController.java @@ -0,0 +1,74 @@ +package com.tashow.cloud.pay.controller.admin.wallet; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageCreateReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackagePageReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageRespVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageUpdateReqVO; +import com.tashow.cloud.pay.convert.wallet.PayWalletRechargePackageConvert; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; +import com.tashow.cloud.pay.service.wallet.PayWalletRechargePackageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + + +@Tag(name = "管ç†åŽå° - 钱包充值套é¤") +@RestController +@RequestMapping("/pay/wallet-recharge-package") +@Validated +public class PayWalletRechargePackageController { + + @Resource + private PayWalletRechargePackageService walletRechargePackageService; + + @PostMapping("/create") + @Operation(summary = "创建钱包充值套é¤") + @PreAuthorize("@ss.hasPermission('pay:wallet-recharge-package:create')") + public CommonResult createWalletRechargePackage(@Valid @RequestBody WalletRechargePackageCreateReqVO createReqVO) { + return success(walletRechargePackageService.createWalletRechargePackage(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新钱包充值套é¤") + @PreAuthorize("@ss.hasPermission('pay:wallet-recharge-package:update')") + public CommonResult updateWalletRechargePackage(@Valid @RequestBody WalletRechargePackageUpdateReqVO updateReqVO) { + walletRechargePackageService.updateWalletRechargePackage(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除钱包充值套é¤") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('pay:wallet-recharge-package:delete')") + public CommonResult deleteWalletRechargePackage(@RequestParam("id") Long id) { + walletRechargePackageService.deleteWalletRechargePackage(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得钱包充值套é¤") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('pay:wallet-recharge-package:query')") + public CommonResult getWalletRechargePackage(@RequestParam("id") Long id) { + PayWalletRechargePackageDO walletRechargePackage = walletRechargePackageService.getWalletRechargePackage(id); + return success(PayWalletRechargePackageConvert.INSTANCE.convert(walletRechargePackage)); + } + + @GetMapping("/page") + @Operation(summary = "获得钱包充值套é¤åˆ†é¡µ") + @PreAuthorize("@ss.hasPermission('pay:wallet-recharge-package:query')") + public CommonResult> getWalletRechargePackagePage(@Valid WalletRechargePackagePageReqVO pageVO) { + PageResult pageResult = walletRechargePackageService.getWalletRechargePackagePage(pageVO); + return success(PayWalletRechargePackageConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletTransactionController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletTransactionController.java new file mode 100644 index 0000000..dc8dd6d --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/PayWalletTransactionController.java @@ -0,0 +1,42 @@ +package com.tashow.cloud.pay.controller.admin.wallet; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.transaction.PayWalletTransactionPageReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.transaction.PayWalletTransactionRespVO; +import com.tashow.cloud.pay.convert.wallet.PayWalletTransactionConvert; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletTransactionDO; +import com.tashow.cloud.pay.service.wallet.PayWalletTransactionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +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.RestController; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管ç†åŽå° - é’±åŒ…ä½™é¢æ˜Žç»†") +@RestController +@RequestMapping("/pay/wallet-transaction") +@Validated +@Slf4j +public class PayWalletTransactionController { + + @Resource + private PayWalletTransactionService payWalletTransactionService; + + @GetMapping("/page") + @Operation(summary = "èŽ·å¾—é’±åŒ…æµæ°´åˆ†é¡µ") + @PreAuthorize("@ss.hasPermission('pay:wallet:query')") + public CommonResult> getWalletTransactionPage( + @Valid PayWalletTransactionPageReqVO pageReqVO) { + PageResult result = payWalletTransactionService.getWalletTransactionPage(pageReqVO); + return success(PayWalletTransactionConvert.INSTANCE.convertPage2(result)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageBaseVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageBaseVO.java new file mode 100644 index 0000000..08da20b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageBaseVO.java @@ -0,0 +1,30 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * å……å€¼å¥—é¤ Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class WalletRechargePackageBaseVO { + + @Schema(description = "套é¤å", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + @NotNull(message = "套é¤åä¸èƒ½ä¸ºç©º") + private String name; + + @Schema(description = "支付金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "16454") + @NotNull(message = "支付金é¢ä¸èƒ½ä¸ºç©º") + private Integer payPrice; + + @Schema(description = "èµ é€é‡‘é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "20887") + @NotNull(message = "èµ é€é‡‘é¢ä¸èƒ½ä¸ºç©º") + private Integer bonusPrice; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "状æ€ä¸èƒ½ä¸ºç©º") + private Byte status; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageCreateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageCreateReqVO.java new file mode 100644 index 0000000..affba6f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageCreateReqVO.java @@ -0,0 +1,14 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage; + +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 WalletRechargePackageCreateReqVO extends WalletRechargePackageBaseVO { + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackagePageReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackagePageReqVO.java new file mode 100644 index 0000000..79667d0 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackagePageReqVO.java @@ -0,0 +1,30 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 WalletRechargePackagePageReqVO extends PageParam { + + @Schema(description = "套é¤å", example = "æŽå››") + private String name; + + @Schema(description = "状æ€", example = "2") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageRespVO.java new file mode 100644 index 0000000..0ec7484 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - å……å€¼å¥—é¤ Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WalletRechargePackageRespVO extends WalletRechargePackageBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "9032") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageUpdateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageUpdateReqVO.java new file mode 100644 index 0000000..3f848fc --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/rechargepackage/WalletRechargePackageUpdateReqVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - 充值套餿›´æ–° Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class WalletRechargePackageUpdateReqVO extends WalletRechargePackageBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "9032") + @NotNull(message = "ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/transaction/PayWalletTransactionPageReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/transaction/PayWalletTransactionPageReqVO.java new file mode 100644 index 0000000..eea1b62 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/transaction/PayWalletTransactionPageReqVO.java @@ -0,0 +1,23 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.transaction; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - é’±åŒ…æµæ°´åˆ†é¡µ Request VO") +@Data +public class PayWalletTransactionPageReqVO extends PageParam { + + @Schema(description = "钱包编å·", example = "888") + private Long walletId; + + @Schema(description = "用户编å·", example = "1024") + private Long userId; + + @Schema(description = "用户类型", example = "1") + @InEnum(UserTypeEnum.class) + private Integer userType; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/transaction/PayWalletTransactionRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/transaction/PayWalletTransactionRespVO.java new file mode 100644 index 0000000..7db9a5c --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/transaction/PayWalletTransactionRespVO.java @@ -0,0 +1,35 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.transaction; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 APP - é’±åŒ…æµæ°´åˆ†é¡µ Response VO") +@Data +public class PayWalletTransactionRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "钱包编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + private Long walletId; + + @Schema(description = "业务分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer bizType; + + @Schema(description = "交易金é¢ï¼Œå•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Long price; + + @Schema(description = "æµæ°´æ ‡é¢˜", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆土豆") + private String title; + + @Schema(description = "交易åŽçš„ä½™é¢ï¼Œå•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Long balance; + + @Schema(description = "交易时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + // TODO @jason:merchantOrderId 字段,需è¦åœ¨ PayWalletTransaction 存储下;然åŽï¼Œå‰ç«¯ä¹Ÿè¿”回下这个字段,界é¢ä¹Ÿå±•示下商户å + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletBaseVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletBaseVO.java new file mode 100644 index 0000000..dc6cd80 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletBaseVO.java @@ -0,0 +1,38 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.wallet; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 用户钱包 Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class PayWalletBaseVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20020") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + + @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "用户类型ä¸èƒ½ä¸ºç©º") + private Integer userType; + + @Schema(description = "ä½™é¢ï¼Œå•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "ä½™é¢ï¼Œå•ä½åˆ†ä¸èƒ½ä¸ºç©º") + private Integer balance; + + @Schema(description = "累计支出,å•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "累计支出,å•ä½åˆ†ä¸èƒ½ä¸ºç©º") + private Integer totalExpense; + + @Schema(description = "累计充值,å•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "累计充值,å•ä½åˆ†ä¸èƒ½ä¸ºç©º") + private Integer totalRecharge; + + @Schema(description = "冻结金é¢ï¼Œå•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "20737") + @NotNull(message = "冻结金é¢ï¼Œå•ä½åˆ†ä¸èƒ½ä¸ºç©º") + private Integer freezePrice; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletPageReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletPageReqVO.java new file mode 100644 index 0000000..f892763 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletPageReqVO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.wallet; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 PayWalletPageReqVO extends PageParam { + + @Schema(description = "用户编å·", example = "1024") + private Long userId; + + @Schema(description = "用户类型", example = "1") + @InEnum(value = UserTypeEnum.class) + private Integer userType; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletRespVO.java new file mode 100644 index 0000000..7cc2d7f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.wallet; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 用户钱包 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayWalletRespVO extends PayWalletBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "29528") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletUpdateBalanceReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletUpdateBalanceReqVO.java new file mode 100644 index 0000000..2de68f5 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletUpdateBalanceReqVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.wallet; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - ä¿®æ”¹é’±åŒ…ä½™é¢ Request VO") +@Data +public class PayWalletUpdateBalanceReqVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "23788") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + + @Schema(description = "å˜åЍ余é¢ï¼Œæ­£æ•°ä¸ºå¢žåŠ ï¼Œè´Ÿæ•°ä¸ºå‡å°‘", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "å˜åЍ余é¢ä¸èƒ½ä¸ºç©º") + private Integer balance; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletUserReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletUserReqVO.java new file mode 100644 index 0000000..c4b548d --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/admin/wallet/vo/wallet/PayWalletUserReqVO.java @@ -0,0 +1,15 @@ +package com.tashow.cloud.pay.controller.admin.wallet.vo.wallet; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - 用户钱包明细 Request VO") +@Data +public class PayWalletUserReqVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/channel/AppPayChannelController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/channel/AppPayChannelController.java new file mode 100644 index 0000000..afea122 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/channel/AppPayChannelController.java @@ -0,0 +1,42 @@ +package com.tashow.cloud.pay.controller.app.channel; + +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.service.channel.PayChannelService; +import jakarta.annotation.Resource; +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 java.util.List; +import java.util.Set; + +import static com.tashow.cloud.common.pojo.CommonResult.success; +import static com.tashow.cloud.common.util.collection.CollectionUtils.convertSet; + + +/** + * 用户 App - æ”¯ä»˜æ¸ é“ + */ +@RestController +@RequestMapping("/pay/channel") +@Validated +public class AppPayChannelController { + + @Resource + private PayChannelService channelService; + + /** + * 获得指定应用的开å¯çš„æ”¯ä»˜æ¸ é“ç¼–ç åˆ—表 + * @param appId åº”ç”¨ç¼–å· + * @return + */ + @GetMapping("/get-enable-code-list") + public CommonResult> getEnableChannelCodeList(@RequestParam("appId") Long appId) { + List channels = channelService.getEnableChannelList(appId); + return success(convertSet(channels, PayChannelDO::getCode)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/AppPayOrderController.http b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/AppPayOrderController.http new file mode 100644 index 0000000..4fe1898 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/AppPayOrderController.http @@ -0,0 +1,63 @@ +### /pay/create æäº¤æ”¯ä»˜è®¢å•ã€alipay_pc】 +POST {{appApi}}/pay/order/submit +Content-Type: application/json +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +{ + "id": 174, + "channelCode": "alipay_pc" +} + +### /pay/create æäº¤æ”¯ä»˜è®¢å•ã€wx_bar】 +POST {{appApi}}/pay/order/submit +Content-Type: application/json +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +{ + "id": 202, + "channelCode": "wx_bar", + "channelExtras": { + "authCode": "134042110834344848" + } +} + +### /pay/create æäº¤æ”¯ä»˜è®¢å•ã€wx_pub】 +POST {{appApi}}/pay/order/submit +Content-Type: application/json +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +{ + "id": 202, + "channelCode": "wx_pub", + "channelExtras": { + "openid": "ockUAwIZ-0OeMZl9ogcZ4ILrGba0" + } +} + +### /pay/create æäº¤æ”¯ä»˜è®¢å•ã€wx_lite】 +POST {{appApi}}/pay/order/submit +Content-Type: application/json +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +{ + "id": 202, + "channelCode": "wx_lite", + "channelExtras": { + "openid": "oLefc4g5GjKWHJjLjMSXB3wX0fD0" + } +} + +### /pay/create æäº¤æ”¯ä»˜è®¢å•ã€wx_native】 +POST {{appApi}}/pay/order/submit +Content-Type: application/json +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +{ + "id": 202, + "channelCode": "wx_native" +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/AppPayOrderController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/AppPayOrderController.java new file mode 100644 index 0000000..553ac6e --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/AppPayOrderController.java @@ -0,0 +1,74 @@ +package com.tashow.cloud.pay.controller.app.order; + +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.common.util.object.BeanUtils; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderRespVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderSubmitRespVO; +import com.tashow.cloud.pay.controller.app.order.vo.AppPayOrderSubmitReqVO; +import com.tashow.cloud.pay.controller.app.order.vo.AppPayOrderSubmitRespVO; +import com.tashow.cloud.pay.convert.order.PayOrderConvert; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.payapi.enums.order.PayOrderStatusEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.tashow.cloud.common.pojo.CommonResult.success; +import static com.tashow.cloud.common.util.servlet.ServletUtils.getClientIP; + + +/** + * 用户 APP - æ”¯ä»˜è®¢å• + */ +@RestController +@RequestMapping("/pay/order") +@Validated +@Slf4j +public class AppPayOrderController { + + @Resource + private PayOrderService payOrderService; + + /** + * èŽ·å¾—æ”¯ä»˜è®¢å• + * @param id ç¼–å· + * @param sync 是å¦åŒæ­¥ + * @return + */ + @GetMapping("/get") + public CommonResult getOrder(@RequestParam("id") Long id, + @RequestParam(value = "sync", required = false) Boolean sync) { + PayOrderDO order = payOrderService.getOrder(id); + // sync 仅在等待支付 + if (Boolean.TRUE.equals(sync) && PayOrderStatusEnum.isWaiting(order.getStatus())) { + payOrderService.syncOrderQuietly(order.getId()); + // 釿–°æŸ¥è¯¢ï¼Œå› ä¸ºåŒæ­¥åŽï¼Œå¯èƒ½ä¼šæœ‰å˜åŒ– + order = payOrderService.getOrder(id); + } + return success(BeanUtils.toBean(order, PayOrderRespVO.class)); + } + + /** + * æäº¤æ”¯ä»˜è®¢å• + * @param reqVO + * @return + */ + @PostMapping("/submit") + public CommonResult submitPayOrder(@RequestBody AppPayOrderSubmitReqVO reqVO) { + // 1. 钱包支付事,需è¦é¢å¤–ä¼  user_id å’Œ user_type +// if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) { +// Map channelExtras = reqVO.getChannelExtras() == null ? +// Maps.newHashMapWithExpectedSize(2) : reqVO.getChannelExtras(); +// channelExtras.put(WalletPayClient.USER_ID_KEY, String.valueOf(getLoginUserId())); +// channelExtras.put(WalletPayClient.USER_TYPE_KEY, String.valueOf(getLoginUserType())); +// reqVO.setChannelExtras(channelExtras); +// } + + // 2. æäº¤æ”¯ä»˜ + PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP()); + return success(PayOrderConvert.INSTANCE.convert3(respVO)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/vo/AppPayOrderSubmitReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/vo/AppPayOrderSubmitReqVO.java new file mode 100644 index 0000000..a03a051 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/vo/AppPayOrderSubmitReqVO.java @@ -0,0 +1,10 @@ +package com.tashow.cloud.pay.controller.app.order.vo; + +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderSubmitReqVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 APP - æ”¯ä»˜è®¢å•æäº¤ Request VO") +@Data +public class AppPayOrderSubmitReqVO extends PayOrderSubmitReqVO { +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/vo/AppPayOrderSubmitRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/vo/AppPayOrderSubmitRespVO.java new file mode 100644 index 0000000..e7185f7 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/order/vo/AppPayOrderSubmitRespVO.java @@ -0,0 +1,11 @@ +package com.tashow.cloud.pay.controller.app.order.vo; + +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderSubmitRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 APP - æ”¯ä»˜è®¢å•æäº¤ Response VO") +@Data +public class AppPayOrderSubmitRespVO extends PayOrderSubmitRespVO { + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletController.java new file mode 100644 index 0000000..71fbcc2 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletController.java @@ -0,0 +1,41 @@ +package com.tashow.cloud.pay.controller.app.wallet; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import com.tashow.cloud.pay.controller.app.wallet.vo.wallet.AppPayWalletRespVO; +import com.tashow.cloud.pay.convert.wallet.PayWalletConvert; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletDO; +import com.tashow.cloud.pay.service.wallet.PayWalletService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +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.RestController; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +/** + * @author jason + */ +@Tag(name = "用户 APP - 钱包") +@RestController +@RequestMapping("/pay/wallet") +@Validated +@Slf4j +public class AppPayWalletController { + + @Resource + private PayWalletService payWalletService; + + @GetMapping("/get") + @Operation(summary = "获å–钱包") + public CommonResult getPayWallet() { + PayWalletDO wallet = payWalletService.getOrCreateWallet(getLoginUserId(), UserTypeEnum.MEMBER.getValue()); + return success(PayWalletConvert.INSTANCE.convert(wallet)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletRechargeController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletRechargeController.java new file mode 100644 index 0000000..61cb25f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletRechargeController.java @@ -0,0 +1,67 @@ +package com.tashow.cloud.pay.controller.app.wallet; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +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 com.tashow.cloud.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateRespVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeRespVO; +import com.tashow.cloud.pay.convert.wallet.PayWalletRechargeConvert; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargeDO; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.pay.service.wallet.PayWalletRechargeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType; + +@Tag(name = "用户 APP - 钱包充值") +@RestController +@RequestMapping("/pay/wallet-recharge") +@Validated +@Slf4j +public class AppPayWalletRechargeController { + + @Resource + private PayWalletRechargeService walletRechargeService; + @Resource + private PayOrderService payOrderService; + + @PostMapping("/create") + @Operation(summary = "创建钱包充值记录(å‘起充值)") + public CommonResult createWalletRecharge( + @Valid @RequestBody AppPayWalletRechargeCreateReqVO reqVO) { + PayWalletRechargeDO walletRecharge = walletRechargeService.createWalletRecharge( + getLoginUserId(), getLoginUserType(), getClientIP(), reqVO); + return success(PayWalletRechargeConvert.INSTANCE.convert(walletRecharge)); + } + + @GetMapping("/page") + @Operation(summary = "获得钱包充值记录分页") + public CommonResult> getWalletRechargePage(@Valid PageParam pageReqVO) { + PageResult pageResult = walletRechargeService.getWalletRechargePackagePage( + getLoginUserId(), UserTypeEnum.MEMBER.getValue(), pageReqVO, true); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + // æ‹¼æŽ¥æ•°æ® + List payOrderList = payOrderService.getOrderList( + convertList(pageResult.getList(), PayWalletRechargeDO::getPayOrderId)); + return success(PayWalletRechargeConvert.INSTANCE.convertPage(pageResult, payOrderList)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletRechargePackageController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletRechargePackageController.java new file mode 100644 index 0000000..49f46f5 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletRechargePackageController.java @@ -0,0 +1,42 @@ +package com.tashow.cloud.pay.controller.app.wallet; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.tashow.cloud.pay.controller.app.wallet.vo.recharge.AppPayWalletPackageRespVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; +import com.tashow.cloud.pay.service.wallet.PayWalletRechargePackageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +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.RestController; + +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "用户 APP - 钱包充值套é¤") +@RestController +@RequestMapping("/pay/wallet-recharge-package") +@Validated +@Slf4j +public class AppPayWalletRechargePackageController { + + @Resource + private PayWalletRechargePackageService walletRechargePackageService; + + @GetMapping("/list") + @Operation(summary = "获得钱包充值套é¤åˆ—表") + public CommonResult> getWalletRechargePackageList() { + List list = walletRechargePackageService.getWalletRechargePackageList( + CommonStatusEnum.ENABLE.getStatus()); + list.sort(Comparator.comparingInt(PayWalletRechargePackageDO::getPayPrice)); + return success(BeanUtils.toBean(list, AppPayWalletPackageRespVO.class)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletTransactionController.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletTransactionController.java new file mode 100644 index 0000000..6c3dc8c --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/AppPayWalletTransactionController.java @@ -0,0 +1,60 @@ +package com.tashow.cloud.pay.controller.app.wallet; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionRespVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionSummaryRespVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletTransactionDO; +import com.tashow.cloud.pay.service.wallet.PayWalletTransactionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.format.annotation.DateTimeFormat; +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 java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 APP - é’±åŒ…ä½™é¢æ˜Žç»†") +@RestController +@RequestMapping("/pay/wallet-transaction") +@Validated +@Slf4j +public class AppPayWalletTransactionController { + + @Resource + private PayWalletTransactionService payWalletTransactionService; + + @GetMapping("/page") + @Operation(summary = "èŽ·å¾—é’±åŒ…æµæ°´åˆ†é¡µ") + public CommonResult> getWalletTransactionPage( + @Valid AppPayWalletTransactionPageReqVO pageReqVO) { + PageResult pageResult = payWalletTransactionService.getWalletTransactionPage( + getLoginUserId(), UserTypeEnum.MEMBER.getValue(), pageReqVO); + return success(BeanUtils.toBean(pageResult, AppPayWalletTransactionRespVO.class)); + } + + @GetMapping("/get-summary") + @Operation(summary = "èŽ·å¾—é’±åŒ…æµæ°´ç»Ÿè®¡") + @Parameter(name = "times", description = "时间段", required = true) + public CommonResult getWalletTransactionSummary( + @RequestParam("createTime") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] createTime) { + AppPayWalletTransactionSummaryRespVO summary = payWalletTransactionService.getWalletTransactionSummary( + getLoginUserId(), UserTypeEnum.MEMBER.getValue(), createTime); + return success(summary); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletPackageRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletPackageRespVO.java new file mode 100644 index 0000000..46f95a0 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletPackageRespVO.java @@ -0,0 +1,20 @@ +package com.tashow.cloud.pay.controller.app.wallet.vo.recharge; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 APP - ç”¨æˆ·å……å€¼å¥—é¤ Response VO") +@Data +public class AppPayWalletPackageRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + @Schema(description = "套é¤å", requiredMode = Schema.RequiredMode.REQUIRED, example = "å°å¥—é¤") + private String name; + + @Schema(description = "支付金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer payPrice; + @Schema(description = "èµ é€é‡‘é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + private Integer bonusPrice; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeCreateReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeCreateReqVO.java new file mode 100644 index 0000000..85cc681 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeCreateReqVO.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.pay.controller.app.wallet.vo.recharge; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.Min; +import lombok.Data; + +import java.util.Objects; + +@Schema(description = "用户 APP - 创建钱包充值 Request VO") +@Data +public class AppPayWalletRechargeCreateReqVO { + + @Schema(description = "支付金é¢", example = "1000") + @Min(value = 1, message = "支付金é¢å¿…须大于零") + private Integer payPrice; + + @Schema(description = "充值套é¤ç¼–å·", example = "1024") + private Long packageId; + + @AssertTrue(message = "充值金é¢å’Œå……钱套é¤ä¸èƒ½åŒæ—¶ä¸ºç©º") + public boolean isValidPayPriceAndPackageId() { + return Objects.nonNull(payPrice) || Objects.nonNull(packageId); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeCreateRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeCreateRespVO.java new file mode 100644 index 0000000..15d7e5b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeCreateRespVO.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.pay.controller.app.wallet.vo.recharge; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 APP - 创建钱包充值 Resp VO") +@Data +public class AppPayWalletRechargeCreateRespVO { + + @Schema(description = "钱包充值编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Long payOrderId; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeRespVO.java new file mode 100644 index 0000000..da26eb4 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/recharge/AppPayWalletRechargeRespVO.java @@ -0,0 +1,42 @@ +package com.tashow.cloud.pay.controller.app.wallet.vo.recharge; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 APP - 钱包充值记录 Resp VO") +@Data +public class AppPayWalletRechargeRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "用户实际到账余é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer totalPrice; + + @Schema(description = "实际支付金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + private Integer payPrice; + + @Schema(description = "钱包赠é€é‡‘é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") + private Integer bonusPrice; + + @Schema(description = "支付æˆåŠŸçš„æ”¯ä»˜æ¸ é“", requiredMode = Schema.RequiredMode.REQUIRED) + private String payChannelCode; + + @Schema(description = "支付渠é“å", example = "微信å°ç¨‹åºæ”¯ä»˜") + private String payChannelName; + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED) + private Long payOrderId; + + @Schema(description = "支付æˆåŠŸçš„å¤–éƒ¨è®¢å•å·", requiredMode = Schema.RequiredMode.REQUIRED) + private String payOrderChannelOrderNo; // 从 PayOrderDO çš„ channelOrderNo 字段 + + @Schema(description = "è®¢å•æ”¯ä»˜æ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime payTime; + + @Schema(description = "退款状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer refundStatus; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionPageReqVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionPageReqVO.java new file mode 100644 index 0000000..daba89b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionPageReqVO.java @@ -0,0 +1,31 @@ +package com.tashow.cloud.pay.controller.app.wallet.vo.transaction; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +@Schema(description = "用户 APP - é’±åŒ…æµæ°´åˆ†é¡µ Request VO") +@Data +public class AppPayWalletTransactionPageReqVO extends PageParam { + + /** + * 类型 - æ”¶å…¥ + */ + public static final Integer TYPE_INCOME = 1; + /** + * 类型 - 支出 + */ + public static final Integer TYPE_EXPENSE = 2; + + @Schema(description = "类型", example = "1") + private Integer type; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionRespVO.java new file mode 100644 index 0000000..bd26b34 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionRespVO.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.pay.controller.app.wallet.vo.transaction; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 APP - é’±åŒ…æµæ°´åˆ†é¡µ Response VO") +@Data +public class AppPayWalletTransactionRespVO { + + @Schema(description = "业务分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer bizType; + + @Schema(description = "交易金é¢ï¼Œå•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Long price; + + @Schema(description = "æµæ°´æ ‡é¢˜", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆土豆") + private String title; + + @Schema(description = "交易时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionSummaryRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionSummaryRespVO.java new file mode 100644 index 0000000..5c3c685 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/transaction/AppPayWalletTransactionSummaryRespVO.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.pay.controller.app.wallet.vo.transaction; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 APP - é’±åŒ…æµæ°´ç»Ÿè®¡ Request VO") +@Data +public class AppPayWalletTransactionSummaryRespVO { + + @Schema(description = "累计支出,å•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Integer totalExpense; + + @Schema(description = "累计收入,å•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000") + private Integer totalIncome; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/wallet/AppPayWalletRespVO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/wallet/AppPayWalletRespVO.java new file mode 100644 index 0000000..e5f6f75 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/app/wallet/vo/wallet/AppPayWalletRespVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.pay.controller.app.wallet.vo.wallet; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 APP - 用户钱包 Response VO") +@Data +public class AppPayWalletRespVO { + + @Schema(description = "钱包余é¢ï¼Œå•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer balance; + + @Schema(description = "累计支出,å•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Integer totalExpense; + + @Schema(description = "累计充值,å•ä½åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000") + private Integer totalRecharge; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/package-info.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/package-info.java new file mode 100644 index 0000000..3b9527b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * æä¾› RESTful API ç»™å‰ç«¯ï¼š + * 1. admin 包:æä¾›ç»™ç®¡ç†åŽå° yudao-ui-admin å‰ç«¯é¡¹ç›® + * 2. app 包:æä¾›ç»™ç”¨æˆ· APP yudao-ui-app å‰ç«¯é¡¹ç›®ï¼Œå®ƒçš„ Controller å’Œ VO éƒ½è¦æ·»åŠ  App å‰ç¼€ï¼Œç”¨äºŽå’Œç®¡ç†åŽå°è¿›è¡ŒåŒºåˆ† + */ +package com.tashow.cloud.pay.controller; diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/app/PayAppConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/app/PayAppConvert.java new file mode 100644 index 0000000..930c3a4 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/app/PayAppConvert.java @@ -0,0 +1,48 @@ +package com.tashow.cloud.pay.convert.app; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppCreateReqVO; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppPageItemRespVO; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppRespVO; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppUpdateReqVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * æ”¯ä»˜åº”ç”¨ä¿¡æ¯ Convert + * + * @author 芋艿 + */ +@Mapper +public interface PayAppConvert { + + PayAppConvert INSTANCE = Mappers.getMapper(PayAppConvert.class); + + PayAppPageItemRespVO pageConvert (PayAppDO bean); + + PayAppDO convert(PayAppCreateReqVO bean); + + PayAppDO convert(PayAppUpdateReqVO bean); + + PayAppRespVO convert(PayAppDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + default PageResult convertPage(PageResult pageResult, List channels) { + PageResult voPageResult = convertPage(pageResult); + // å¤„ç† channel 关系 + Map> appIdChannelMap = CollectionUtils.convertMultiMap2(channels, PayChannelDO::getAppId, PayChannelDO::getCode); + voPageResult.getList().forEach(app -> app.setChannelCodes(appIdChannelMap.get(app.getId()))); + return voPageResult; + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/channel/PayChannelConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/channel/PayChannelConvert.java new file mode 100644 index 0000000..2458dab --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/channel/PayChannelConvert.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.pay.convert.channel; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelCreateReqVO; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelRespVO; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelUpdateReqVO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface PayChannelConvert { + + PayChannelConvert INSTANCE = Mappers.getMapper(PayChannelConvert.class); + + @Mapping(target = "config",ignore = true) + PayChannelDO convert(PayChannelCreateReqVO bean); + + @Mapping(target = "config",ignore = true) + PayChannelDO convert(PayChannelUpdateReqVO bean); + + @Mapping(target = "config",expression = "java(cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString(bean.getConfig()))") + PayChannelRespVO convert(PayChannelDO bean); + + PageResult convertPage(PageResult page); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/demo/PayDemoOrderConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/demo/PayDemoOrderConvert.java new file mode 100644 index 0000000..e48872f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/demo/PayDemoOrderConvert.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.pay.convert.demo; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO; +import com.tashow.cloud.pay.controller.admin.demo.vo.order.PayDemoOrderRespVO; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoOrderDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * ç¤ºä¾‹è®¢å• Convert + * + * @author èŠ‹é“æºç  + */ +@Mapper +public interface PayDemoOrderConvert { + + PayDemoOrderConvert INSTANCE = Mappers.getMapper(PayDemoOrderConvert.class); + + PayDemoOrderDO convert(PayDemoOrderCreateReqVO bean); + + PayDemoOrderRespVO convert(PayDemoOrderDO bean); + + PageResult convertPage(PageResult page); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/demo/PayDemoTransferConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/demo/PayDemoTransferConvert.java new file mode 100644 index 0000000..a0e9f3c --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/demo/PayDemoTransferConvert.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.pay.convert.demo; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; +import com.tashow.cloud.pay.controller.admin.demo.vo.transfer.PayDemoTransferRespVO; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoTransferDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author jason + */ +@Mapper +public interface PayDemoTransferConvert { + + PayDemoTransferConvert INSTANCE = Mappers.getMapper(PayDemoTransferConvert.class); + + PayDemoTransferDO convert(PayDemoTransferCreateReqVO bean); + + PageResult convertPage(PageResult pageResult); +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/notify/PayNotifyTaskConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/notify/PayNotifyTaskConvert.java new file mode 100644 index 0000000..c1842f0 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/notify/PayNotifyTaskConvert.java @@ -0,0 +1,43 @@ +package com.tashow.cloud.pay.convert.notify; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import com.tashow.cloud.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO; +import com.tashow.cloud.pay.controller.admin.notify.vo.PayNotifyTaskRespVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyLogDO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyTaskDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * 支付通知 Convert + * + * @author èŠ‹é“æºç  + */ +@Mapper +public interface PayNotifyTaskConvert { + + PayNotifyTaskConvert INSTANCE = Mappers.getMapper(PayNotifyTaskConvert.class); + + PayNotifyTaskRespVO convert(PayNotifyTaskDO bean); + + default PageResult convertPage(PageResult page, Map appMap){ + PageResult result = convertPage(page); + result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName()))); + return result; + } + PageResult convertPage(PageResult page); + + default PayNotifyTaskDetailRespVO convert(PayNotifyTaskDO task, PayAppDO app, List logs) { + PayNotifyTaskDetailRespVO respVO = convert(task, logs); + if (app != null) { + respVO.setAppName(app.getName()); + } + return respVO; + } + PayNotifyTaskDetailRespVO convert(PayNotifyTaskDO task, List logs); +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/order/PayOrderConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/order/PayOrderConvert.java new file mode 100644 index 0000000..21be540 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/order/PayOrderConvert.java @@ -0,0 +1,76 @@ +package com.tashow.cloud.pay.convert.order; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*; +import com.tashow.cloud.pay.controller.admin.order.vo.*; +import com.tashow.cloud.payapi.controller.admin.order.vo.*; +import com.tashow.cloud.pay.controller.app.order.vo.AppPayOrderSubmitRespVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderExtensionDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * æ”¯ä»˜è®¢å• Convert + * + * @author aquan + */ +@Mapper +public interface PayOrderConvert { + + PayOrderConvert INSTANCE = Mappers.getMapper(PayOrderConvert.class); + + PayOrderRespVO convert(PayOrderDO bean); + + PayOrderRespDTO convert2(PayOrderDO order); + + default PayOrderDetailsRespVO convert(PayOrderDO order, PayOrderExtensionDO orderExtension, PayAppDO app) { + PayOrderDetailsRespVO respVO = convertDetail(order); + respVO.setExtension(convert(orderExtension)); + if (app != null) { + respVO.setAppName(app.getName()); + } + return respVO; + } + PayOrderDetailsRespVO convertDetail(PayOrderDO bean); + PayOrderDetailsRespVO.PayOrderExtension convert(PayOrderExtensionDO bean); + + default PageResult convertPage(PageResult page, Map appMap) { + PageResult result = convertPage(page); + result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName()))); + return result; + } + PageResult convertPage(PageResult page); + + default List convertList(List list, Map appMap) { + return CollectionUtils.convertList(list, order -> { + PayOrderExcelVO excelVO = convertExcel(order); + MapUtils.findAndThen(appMap, order.getAppId(), app -> excelVO.setAppName(app.getName())); + return excelVO; + }); + } + PayOrderExcelVO convertExcel(PayOrderDO bean); + + PayOrderDO convert(PayOrderCreateReqDTO bean); + + @Mapping(target = "id", ignore = true) + PayOrderExtensionDO convert(PayOrderSubmitReqVO bean, String userIp); + + PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO, String userIp); + + @Mapping(source = "order.status", target = "status") + PayOrderSubmitRespVO convert(PayOrderDO order, cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO respDTO); + + AppPayOrderSubmitRespVO convert3(PayOrderSubmitRespVO bean); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/package-info.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/package-info.java new file mode 100644 index 0000000..303126f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/package-info.java @@ -0,0 +1,6 @@ +/** + * æä¾› POJO ç±»çš„å®žä½“è½¬æ¢ + * + * ç›®å‰ä½¿ç”¨ MapStruct 框架 + */ +package com.tashow.cloud.pay.convert; diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/refund/PayRefundConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/refund/PayRefundConvert.java new file mode 100644 index 0000000..dee47b6 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/refund/PayRefundConvert.java @@ -0,0 +1,56 @@ +package com.tashow.cloud.pay.convert.refund; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundDetailsRespVO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundExcelVO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundPageItemRespVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +@Mapper +public interface PayRefundConvert { + + PayRefundConvert INSTANCE = Mappers.getMapper(PayRefundConvert.class); + + + default PayRefundDetailsRespVO convert(PayRefundDO refund, PayAppDO app) { + PayRefundDetailsRespVO respVO = convert(refund); + if (app != null) { + respVO.setAppName(app.getName()); + } + return respVO; + } + PayRefundDetailsRespVO convert(PayRefundDO bean); + PayRefundDetailsRespVO.Order convert(PayOrderDO bean); + + default PageResult convertPage(PageResult page, Map appMap) { + PageResult result = convertPage(page); + result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName()))); + return result; + } + PageResult convertPage(PageResult page); + + PayRefundDO convert(PayRefundCreateReqDTO bean); + + PayRefundRespDTO convert02(PayRefundDO bean); + + default List convertList(List list, Map appMap) { + return CollectionUtils.convertList(list, order -> { + PayRefundExcelVO excelVO = convertExcel(order); + MapUtils.findAndThen(appMap, order.getAppId(), app -> excelVO.setAppName(app.getName())); + return excelVO; + }); + } + PayRefundExcelVO convertExcel(PayRefundDO bean); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/transfer/PayTransferConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/transfer/PayTransferConvert.java new file mode 100644 index 0000000..85c2f84 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/transfer/PayTransferConvert.java @@ -0,0 +1,31 @@ +package com.tashow.cloud.pay.convert.transfer; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; +import com.tashow.cloud.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; +import com.tashow.cloud.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; +import com.tashow.cloud.pay.controller.admin.transfer.vo.PayTransferPageItemRespVO; +import com.tashow.cloud.pay.controller.admin.transfer.vo.PayTransferRespVO; +import com.tashow.cloud.pay.dal.dataobject.transfer.PayTransferDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface PayTransferConvert { + + PayTransferConvert INSTANCE = Mappers.getMapper(PayTransferConvert.class); + + PayTransferDO convert(PayTransferCreateReqDTO dto); + + PayTransferUnifiedReqDTO convert2(PayTransferDO dto); + + PayTransferCreateReqDTO convert(PayTransferCreateReqVO vo); + + PayTransferCreateReqDTO convert(PayDemoTransferCreateReqVO vo); + + PayTransferRespVO convert(PayTransferDO bean); + + PageResult convertPage(PageResult pageResult); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletConvert.java new file mode 100644 index 0000000..5dd663b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletConvert.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.pay.convert.wallet; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.wallet.PayWalletRespVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.wallet.AppPayWalletRespVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface PayWalletConvert { + + PayWalletConvert INSTANCE = Mappers.getMapper(PayWalletConvert.class); + + AppPayWalletRespVO convert(PayWalletDO bean); + + PayWalletRespVO convert02(PayWalletDO bean); + + PageResult convertPage(PageResult page); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletRechargeConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletRechargeConvert.java new file mode 100644 index 0000000..1556660 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletRechargeConvert.java @@ -0,0 +1,43 @@ +package com.tashow.cloud.pay.convert.wallet; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; +import com.tashow.cloud.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateRespVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeRespVO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargeDO; +import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +@Mapper +public interface PayWalletRechargeConvert { + + PayWalletRechargeConvert INSTANCE = Mappers.getMapper(PayWalletRechargeConvert.class); + + @Mapping(target = "totalPrice", expression = "java( payPrice + bonusPrice)") + PayWalletRechargeDO convert(Long walletId, Integer payPrice, Integer bonusPrice, Long packageId); + + AppPayWalletRechargeCreateRespVO convert(PayWalletRechargeDO bean); + + default PageResult convertPage(PageResult pageResult, + List payOrderList) { + PageResult voPageResult = BeanUtils.toBean(pageResult, AppPayWalletRechargeRespVO.class); + Map payOrderMap = CollectionUtils.convertMap(payOrderList, PayOrderDO::getId); + voPageResult.getList().forEach(recharge -> { + recharge.setPayChannelName(DictFrameworkUtils.getDictDataLabel( + DictTypeConstants.CHANNEL_CODE, recharge.getPayChannelCode())); + MapUtils.findAndThen(payOrderMap, recharge.getPayOrderId(), + order -> recharge.setPayOrderChannelOrderNo(order.getChannelOrderNo())); + }); + return voPageResult; + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletRechargePackageConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletRechargePackageConvert.java new file mode 100644 index 0000000..2a2bcb7 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletRechargePackageConvert.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.pay.convert.wallet; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageCreateReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageRespVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageUpdateReqVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface PayWalletRechargePackageConvert { + + PayWalletRechargePackageConvert INSTANCE = Mappers.getMapper(PayWalletRechargePackageConvert.class); + + PayWalletRechargePackageDO convert(WalletRechargePackageCreateReqVO bean); + + PayWalletRechargePackageDO convert(WalletRechargePackageUpdateReqVO bean); + + WalletRechargePackageRespVO convert(PayWalletRechargePackageDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletTransactionConvert.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletTransactionConvert.java new file mode 100644 index 0000000..7c181d4 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/wallet/PayWalletTransactionConvert.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.pay.convert.wallet; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.transaction.PayWalletTransactionRespVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletTransactionDO; +import com.tashow.cloud.pay.service.wallet.bo.WalletTransactionCreateReqBO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface PayWalletTransactionConvert { + + PayWalletTransactionConvert INSTANCE = Mappers.getMapper(PayWalletTransactionConvert.class); + + PageResult convertPage2(PageResult page); + + PayWalletTransactionDO convert(WalletTransactionCreateReqBO bean); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/ã€ŠèŠ‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/ã€ŠèŠ‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md new file mode 100644 index 0000000..8153487 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/convert/ã€ŠèŠ‹é“ Spring Boot å¯¹è±¡è½¬æ¢ MapStruct 入门》.md @@ -0,0 +1 @@ + diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/app/PayAppDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/app/PayAppDO.java new file mode 100644 index 0000000..a4d439e --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/app/PayAppDO.java @@ -0,0 +1,66 @@ +package com.tashow.cloud.pay.dal.dataobject.app; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.*; + +/** + * 支付应用 DO + * 一个商户下,å¯èƒ½ä¼šæœ‰å¤šä¸ªæ”¯ä»˜åº”用。例如说,京东有京东商城ã€äº¬ä¸œåˆ°å®¶ç­‰ç­‰ + * ä¸è¿‡ä¸€èˆ¬æ¥è¯´ï¼Œä¸€ä¸ªå•†æˆ·ï¼Œåªæœ‰ä¸€ä¸ªåº”用哈~ + * + * å³ PayMerchantDO : PayAppDO = 1 : n + * + * @author èŠ‹é“æºç  + */ +@TableName("pay_app") +@KeySequence("pay_app_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayAppDO extends BaseDO { + + /** + * 应用编å·ï¼Œæ•°æ®åº“自增 + */ + @TableId + private Long id; + /** + * 应用标识 + */ + private String appKey; + /** + * 应用å + */ + private String name; + /** + * çŠ¶æ€ + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * æ”¯ä»˜ç»“æžœçš„å›žè°ƒåœ°å€ + */ + private String orderNotifyUrl; + /** + * é€€æ¬¾ç»“æžœçš„å›žè°ƒåœ°å€ + */ + private String refundNotifyUrl; + + /** + * è½¬è´¦ç»“æžœçš„å›žè°ƒåœ°å€ + */ + private String transferNotifyUrl; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/channel/PayChannelDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/channel/PayChannelDO.java new file mode 100644 index 0000000..3828922 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/channel/PayChannelDO.java @@ -0,0 +1,69 @@ +package com.tashow.cloud.pay.dal.dataobject.channel; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +/** + * æ”¯ä»˜æ¸ é“ DO + * ä¸€ä¸ªåº”ç”¨ä¸‹ï¼Œä¼šæœ‰å¤šç§æ”¯ä»˜æ¸ é“ï¼Œä¾‹å¦‚è¯´å¾®ä¿¡æ”¯ä»˜ã€æ”¯ä»˜å®æ”¯ä»˜ç­‰ç­‰ + * + * å³ PayAppDO : PayChannelDO = 1 : n + * + * @author èŠ‹é“æºç  + */ +@TableName(value = "pay_channel", autoResultMap = true) +@KeySequence("pay_channel_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayChannelDO extends TenantBaseDO { + + /** + * 渠é“ç¼–å·ï¼Œæ•°æ®åº“自增 + */ + private Long id; + /** + * 渠é“ç¼–ç  + * + * 枚举 {@link PayChannelEnum} + */ + private String code; + /** + * çŠ¶æ€ + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 渠é“费率,å•ä½ï¼šç™¾åˆ†æ¯” + */ + private Double feeRate; + /** + * 备注 + */ + private String remark; + + /** + * åº”ç”¨ç¼–å· + * + * å…³è” {@link PayAppDO#getId()} + */ + private Long appId; + /** + * 支付渠é“é…ç½® + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private PayClientConfig config; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/demo/PayDemoOrderDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/demo/PayDemoOrderDO.java new file mode 100644 index 0000000..e29388c --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/demo/PayDemoOrderDO.java @@ -0,0 +1,87 @@ +package com.tashow.cloud.pay.dal.dataobject.demo; + +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.time.LocalDateTime; + +/** + * ç¤ºä¾‹è®¢å• + * + * 演示业务系统的订å•,如何接入 pay 系统的支付与退款 + * + * @author èŠ‹é“æºç  + */ +@TableName("pay_demo_order") +@KeySequence("pay_demo_order_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayDemoOrderDO extends BaseDO { + + /** + * 订å•ç¼–å·ï¼Œè‡ªå¢ž + */ + @TableId + private Long id; + /** + * ç”¨æˆ·ç¼–å· + */ + private Long userId; + /** + * 商å“ç¼–å· + */ + private Long spuId; + /** + * 商å“åç§° + */ + private String spuName; + /** + * 价格,å•ä½ï¼šåˆ† + */ + private Integer price; + + // ========== 支付相关字段 ========== + + /** + * æ˜¯å¦æ”¯ä»˜ + */ + private Boolean payStatus; + /** + * 支付订å•ç¼–å· + * + * 对接 pay-module-biz 支付æœåŠ¡çš„æ”¯ä»˜è®¢å•ç¼–å·ï¼Œå³ PayOrderDO çš„ id ç¼–å· + */ + private Long payOrderId; + /** + * 付款时间 + */ + private LocalDateTime payTime; + /** + * æ”¯ä»˜æ¸ é“ + * + * 对应 PayChannelEnum 枚举 + */ + private String payChannelCode; + + // ========== 退款相关字段 ========== + /** + * 支付退款å•å· + */ + private Long payRefundId; + /** + * 退款金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer refundPrice; + /** + * é€€æ¬¾å®Œæˆæ—¶é—´ + */ + private LocalDateTime refundTime; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/demo/PayDemoTransferDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/demo/PayDemoTransferDO.java new file mode 100644 index 0000000..e25adca --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/demo/PayDemoTransferDO.java @@ -0,0 +1,84 @@ +package com.tashow.cloud.pay.dal.dataobject.demo; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +// TODO 芋艿:需è¦è¯¦ç»† review +/** + * ç¤ºä¾‹è½¬è´¦è®¢å• + * + * 演示业务系统的转账业务 + */ +@TableName(value ="pay_demo_transfer", autoResultMap = true) +@KeySequence("pay_demo_transfer_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class PayDemoTransferDO extends BaseDO { + + /** + * 订å•ç¼–å· + */ + @TableId + private Long id; + + /** + * åº”ç”¨ç¼–å· + * + * å…³è” {@link PayAppDO#getId()} + */ + private Long appId; + + /** + * 转账类型 + *

+ * 枚举 {@link PayTransferTypeEnum} + */ + private Integer type; + + /** + * 转账金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer price; + + /** + * 收款人姓å + */ + private String userName; + + /** + * 支付å®ç™»å½•å· + */ + private String alipayLogonId; + + /** + * 微信 openId + */ + private String openid; + + /** + * è½¬è´¦çŠ¶æ€ + */ + private Integer transferStatus; + + /** + * 转账å•ç¼–å· + */ + private Long payTransferId; + + /** + * 转账支付æˆåŠŸæ¸ é“ + */ + private String payChannelCode; + + /** + * 转账支付时间 + */ + private LocalDateTime transferTime; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/notify/PayNotifyLogDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/notify/PayNotifyLogDO.java new file mode 100644 index 0000000..84188c0 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/notify/PayNotifyLogDO.java @@ -0,0 +1,51 @@ +package com.tashow.cloud.pay.dal.dataobject.notify; + +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 商户支付ã€é€€æ¬¾ç­‰çš„通知 Log + * æ¯æ¬¡é€šçŸ¥æ—¶ï¼Œéƒ½ä¼šåœ¨è¯¥è¡¨ä¸­ï¼Œè®°å½•一次 Log,方便排查问题 + * + * @author èŠ‹é“æºç  + */ +@TableName("pay_notify_log") +@KeySequence("pay_notify_log_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayNotifyLogDO extends BaseDO { + + /** + * 日志编å·ï¼Œè‡ªå¢ž + */ + private Long id; + /** + * é€šçŸ¥ä»»åŠ¡ç¼–å· + * + * å…³è” {@link PayNotifyTaskDO#getId()} + */ + private Long taskId; + /** + * 第几次被通知 + * + * 对应到 {@link PayNotifyTaskDO#getNotifyTimes()} + */ + private Integer notifyTimes; + /** + * HTTP å“应结果 + */ + private String response; + /** + * æ”¯ä»˜é€šçŸ¥çŠ¶æ€ + * + * 枚举 {@link PayNotifyStatusEnum} + */ + private Integer status; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/notify/PayNotifyTaskDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/notify/PayNotifyTaskDO.java new file mode 100644 index 0000000..fa82763 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/notify/PayNotifyTaskDO.java @@ -0,0 +1,100 @@ +package com.tashow.cloud.pay.dal.dataobject.notify; + +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + * 支付通知 + * 在支付系统收到支付渠é“的支付ã€é€€æ¬¾çš„结果åŽï¼Œéœ€è¦ä¸æ–­çš„通知到业务系统,直到æˆåŠŸã€‚ + * + * @author èŠ‹é“æºç  + */ +@TableName("pay_notify_task") +@KeySequence("pay_notify_task_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class PayNotifyTaskDO extends TenantBaseDO { + + /** + * 通知频率,å•ä½ä¸ºç§’。 + * + * 算上首次的通知,实际是一共 1 + 8 = 9 次。 + */ + public static final Integer[] NOTIFY_FREQUENCY = new Integer[]{ + 15, 15, 30, 180, + 1800, 1800, 1800, 3600 + }; + + /** + * ç¼–å·ï¼Œè‡ªå¢ž + */ + @TableId + private Long id; + /** + * åº”ç”¨ç¼–å· + * + * å…³è” {@link PayAppDO#getId()} + */ + private Long appId; + /** + * 通知类型 + * + * 枚举 {@link PayNotifyTypeEnum} + */ + private Integer type; + /** + * æ•°æ®ç¼–å·ï¼Œæ ¹æ®ä¸åŒ type 进行关è”: + * + * 1. {@link PayNotifyTypeEnum#ORDER} æ—¶ï¼Œå…³è” {@link PayOrderDO#getId()} + * 2. {@link PayNotifyTypeEnum#REFUND} æ—¶ï¼Œå…³è” {@link PayRefundDO#getId()} + */ + private Long dataId; + /** + * 商户订å•ç¼–å· + */ + private String merchantOrderId; + /** + * 商户转账å•ç¼–å· + */ + private String merchantTransferId; + /** + * é€šçŸ¥çŠ¶æ€ + * + * 枚举 {@link PayNotifyStatusEnum} + */ + private Integer status; + /** + * 下一次通知时间 + */ + private LocalDateTime nextNotifyTime; + /** + * 最åŽä¸€æ¬¡æ‰§è¡Œæ—¶é—´ + */ + private LocalDateTime lastExecuteTime; + /** + * 当å‰é€šçŸ¥æ¬¡æ•° + */ + private Integer notifyTimes; + /** + * 最大å¯é€šçŸ¥æ¬¡æ•° + */ + private Integer maxNotifyTimes; + /** + * é€šçŸ¥åœ°å€ + */ + private String notifyUrl; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/order/PayOrderDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/order/PayOrderDO.java new file mode 100644 index 0000000..61f28bc --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/order/PayOrderDO.java @@ -0,0 +1,138 @@ +package com.tashow.cloud.pay.dal.dataobject.order; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * æ”¯ä»˜è®¢å• DO + * + * @author èŠ‹é“æºç  + */ +@TableName("pay_order") +@KeySequence("pay_order_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayOrderDO extends BaseDO { + + /** + * 订å•ç¼–å·ï¼Œæ•°æ®åº“自增 + */ + private Long id; + /** + * åº”ç”¨ç¼–å· + * + * å…³è” {@link PayAppDO#getId()} + */ + private Long appId; + /** + * 渠é“ç¼–å· + * + * å…³è” {@link PayChannelDO#getId()} + */ + private Long channelId; + /** + * 渠é“ç¼–ç  + * + * 枚举 {@link PayChannelEnum} + */ + private String channelCode; + + // ========== 商户相关字段 ========== + + /** + * 商户订å•ç¼–å· + * + * 例如说,内部系统 A 的订å•å·ï¼Œéœ€è¦ä¿è¯æ¯ä¸ª PayAppDO 唯一 + */ + private String merchantOrderId; + /** + * 商哿 ‡é¢˜ + */ + private String subject; + /** + * å•†å“æè¿°ä¿¡æ¯ + */ + private String body; + /** + * å¼‚æ­¥é€šçŸ¥åœ°å€ + */ + private String notifyUrl; + + // ========== 订å•相关字段 ========== + + /** + * 支付金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer price; + /** + * æ¸ é“æ‰‹ç»­è´¹ï¼Œå•ä½ï¼šç™¾åˆ†æ¯” + * + * 冗余 {@link PayChannelDO#getFeeRate()} + */ + private Double channelFeeRate; + /** + * æ¸ é“æ‰‹ç»­é‡‘é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer channelFeePrice; + /** + * æ”¯ä»˜çŠ¶æ€ + * + * 枚举 {@link PayOrderStatusEnum} + */ + private Integer status; + /** + * 用户 IP + */ + private String userIp; + /** + * 订å•失效时间 + */ + private LocalDateTime expireTime; + /** + * è®¢å•æ”¯ä»˜æˆåŠŸæ—¶é—´ + */ + private LocalDateTime successTime; + /** + * 支付æˆåŠŸçš„è®¢å•æ‹“展å•ç¼–å· + * + * å…³è” {@link PayOrderExtensionDO#getId()} + */ + private Long extensionId; + /** + * 支付æˆåŠŸçš„å¤–éƒ¨è®¢å•å· + * + * å…³è” {@link PayOrderExtensionDO#getNo()} + */ + private String no; + + // ========== 退款相关字段 ========== + /** + * 退款总金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer refundPrice; + + // ========== 渠é“相关字段 ========== + /** + * 渠é“ç”¨æˆ·ç¼–å· + * + * 例如说,微信 openidã€æ”¯ä»˜å®è´¦å· + */ + private String channelUserId; + /** + * 渠é“订å•å· + */ + private String channelOrderNo; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/order/PayOrderExtensionDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/order/PayOrderExtensionDO.java new file mode 100644 index 0000000..9191db7 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/order/PayOrderExtensionDO.java @@ -0,0 +1,96 @@ +package com.tashow.cloud.pay.dal.dataobject.order; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.util.Map; + +/** + * æ”¯ä»˜è®¢å•æ‹“展 DO + * + * æ¯æ¬¡è°ƒç”¨æ”¯ä»˜æ¸ é“,都会生æˆä¸€æ¡å¯¹åº”记录 + * + * @author èŠ‹é“æºç  + */ +@TableName(value = "pay_order_extension",autoResultMap = true) +@KeySequence("pay_order_extension_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayOrderExtensionDO extends BaseDO { + + /** + * è®¢å•æ‹“展编å·ï¼Œæ•°æ®åº“自增 + */ + private Long id; + /** + * 外部订å•å·ï¼Œæ ¹æ®è§„åˆ™ç”Ÿæˆ + * + * è°ƒç”¨æ”¯ä»˜æ¸ é“æ—¶ï¼Œä½¿ç”¨è¯¥å­—段作为对接的订å•å·ï¼š + * 1. 微信支付:对应 JSAPI 支付 çš„ out_trade_no 字段 + * 2. æ”¯ä»˜å®æ”¯ä»˜ï¼šå¯¹åº” 电脑网站支付 çš„ out_trade_no 字段 + * + * 例如说,P202110132239124200055 + */ + private String no; + /** + * 订å•å· + * + * å…³è” {@link PayOrderDO#getId()} + */ + private Long orderId; + /** + * 渠é“ç¼–å· + * + * å…³è” {@link PayChannelDO#getId()} + */ + private Long channelId; + /** + * 渠é“ç¼–ç  + */ + private String channelCode; + /** + * 用户 IP + */ + private String userIp; + /** + * æ”¯ä»˜çŠ¶æ€ + * + * 枚举 {@link PayOrderStatusEnum} + */ + private Integer status; + /** + * 支付渠é“çš„é¢å¤–傿•° + * + * å‚è§ å‚æ•°è¯´æ˜Ž + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map channelExtras; + + /** + * 调用渠é“çš„é”™è¯¯ç  + */ + private String channelErrorCode; + /** + * è°ƒç”¨æ¸ é“æŠ¥é”™æ—¶ï¼Œé”™è¯¯ä¿¡æ¯ + */ + private String channelErrorMsg; + + /** + * 支付渠é“çš„åŒæ­¥/异步通知的内容 + * + * 对应 {@link PayOrderRespDTO#getRawData()} + */ + private String channelNotifyData; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/refund/PayRefundDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/refund/PayRefundDO.java new file mode 100644 index 0000000..546cd32 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/refund/PayRefundDO.java @@ -0,0 +1,160 @@ +package com.tashow.cloud.pay.dal.dataobject.refund; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * æ”¯ä»˜é€€æ¬¾å• DO + * 一个支付订å•,å¯ä»¥æ‹¥æœ‰å¤šä¸ªæ”¯ä»˜é€€æ¬¾å• + * + * å³ PayOrderDO : PayRefundDO = 1 : n + * + * @author èŠ‹é“æºç  + */ +@TableName("pay_refund") +@KeySequence("pay_refund_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayRefundDO extends BaseDO { + + /** + * 退款å•ç¼–å·ï¼Œæ•°æ®åº“自增 + */ + @TableId + private Long id; + /** + * 外部退款å·ï¼Œæ ¹æ®è§„åˆ™ç”Ÿæˆ + * + * è°ƒç”¨æ”¯ä»˜æ¸ é“æ—¶ï¼Œä½¿ç”¨è¯¥å­—段作为对接的退款å·ï¼š + * 1. 微信退款:对应 申请退款 çš„ out_refund_no 字段 + * 2. 支付å®é€€æ¬¾ï¼šå¯¹åº” çš„ out_request_no 字段 + */ + private String no; + + /** + * åº”ç”¨ç¼–å· + * + * å…³è” {@link PayAppDO#getId()} + */ + private Long appId; + /** + * 渠é“ç¼–å· + * + * å…³è” {@link PayChannelDO#getId()} + */ + private Long channelId; + /** + * 渠é“ç¼–ç  + * + * 枚举 {@link PayChannelEnum} + */ + private String channelCode; + /** + * 订å•ç¼–å· + * + * å…³è” {@link PayOrderDO#getId()} + */ + private Long orderId; + /** + * 支付订å•ç¼–å· + * + * 冗余 {@link PayOrderDO#getNo()} + */ + private String orderNo; + + // ========== 商户相关字段 ========== + /** + * 商户订å•ç¼–å· + * + * 例如说,内部系统 A 的订å•å·ï¼Œéœ€è¦ä¿è¯æ¯ä¸ª PayAppDO 唯一 + */ + private String merchantOrderId; + /** + * 商户退款订å•å· + * + * 例如说,内部系统 A 的订å•å·ï¼Œéœ€è¦ä¿è¯æ¯ä¸ª PayAppDO 唯一 + */ + private String merchantRefundId; + /** + * å¼‚æ­¥é€šçŸ¥åœ°å€ + */ + private String notifyUrl; + + // ========== 退款相关字段 ========== + /** + * é€€æ¬¾çŠ¶æ€ + * + * 枚举 {@link PayRefundStatusEnum} + */ + private Integer status; + + /** + * 支付金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer payPrice; + /** + * 退款金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer refundPrice; + + /** + * 退款原因 + */ + private String reason; + + /** + * 用户 IP + */ + private String userIp; + + // ========== 渠é“相关字段 ========== + /** + * 渠é“订å•å· + * + * 冗余 {@link PayOrderDO#getChannelOrderNo()} + */ + private String channelOrderNo; + /** + * 渠é“退款å•å· + * + * 1. 微信退款:对应 申请退款 çš„ refund_id 字段 + * 2. 支付å®é€€æ¬¾ï¼šæ²¡æœ‰å­—段 + */ + private String channelRefundNo; + /** + * 退款æˆåŠŸæ—¶é—´ + */ + private LocalDateTime successTime; + + /** + * 调用渠é“çš„é”™è¯¯ç  + */ + private String channelErrorCode; + /** + * 调用渠é“的错误æç¤º + */ + private String channelErrorMsg; + + /** + * 支付渠é“çš„åŒæ­¥/异步通知的内容 + * + * 对应 {@link PayRefundRespDTO#getRawData()} + */ + private String channelNotifyData; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/transfer/PayTransferDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/transfer/PayTransferDO.java new file mode 100644 index 0000000..dda157e --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/transfer/PayTransferDO.java @@ -0,0 +1,158 @@ +package com.tashow.cloud.pay.dal.dataobject.transfer; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum; +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Map; + +// TODO 芋艿:需è¦è¯¦ç»† review +/** + * è½¬è´¦å• DO + * + * @author jason + */ +@TableName(value ="pay_transfer", autoResultMap = true) +@KeySequence("pay_transfer_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class PayTransferDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + + /** + * 转账å•å· + * + */ + private String no; + + /** + * åº”ç”¨ç¼–å· + * + * å…³è” {@link PayAppDO#getId()} + */ + private Long appId; + + /** + * 转账渠é“ç¼–å· + * + * å…³è” {@link PayChannelDO#getId()} + */ + private Long channelId; + + /** + * 转账渠é“ç¼–ç  + * + * 枚举 {@link PayChannelEnum} + */ + private String channelCode; + + // ========== 商户相关字段 ========== + /** + * 商户转账å•ç¼–å· + * + * 例如说,内部系统 A 的订å•å·ï¼Œéœ€è¦ä¿è¯æ¯ä¸ª PayAppDO 唯一 + */ + private String merchantTransferId; + + // ========== 转账相关字段 ========== + + /** + * 类型 + * + * 枚举 {@link PayTransferTypeEnum} + */ + private Integer type; + + /** + * 转账标题 + */ + private String subject; + + /** + * 转账金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer price; + + /** + * 收款人姓å + */ + private String userName; + + /** + * è½¬è´¦çŠ¶æ€ + * + * 枚举 {@link PayTransferStatusRespEnum} + */ + private Integer status; + + /** + * 订å•转账æˆåŠŸæ—¶é—´ + */ + private LocalDateTime successTime; + + // ========== 支付å®è½¬è´¦ç›¸å…³å­—段 ========== + /** + * 支付å®ç™»å½•å· + */ + private String alipayLogonId; + + + // ========== 微信转账相关字段 ========== + /** + * 微信 openId + */ + private String openid; + + // ========== 其它字段 ========== + + /** + * å¼‚æ­¥é€šçŸ¥åœ°å€ + */ + private String notifyUrl; + + /** + * 用户 IP + */ + private String userIp; + + /** + * 渠é“çš„é¢å¤–傿•° + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map channelExtras; + + /** + * 渠é“转账å•å· + */ + private String channelTransferNo; + + /** + * 调用渠é“çš„é”™è¯¯ç  + */ + private String channelErrorCode; + /** + * 调用渠é“的错误æç¤º + */ + private String channelErrorMsg; + + /** + * 渠é“çš„åŒæ­¥/异步通知的内容 + * + */ + private String channelNotifyData; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletDO.java new file mode 100644 index 0000000..df0f5a2 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletDO.java @@ -0,0 +1,59 @@ +package com.tashow.cloud.pay.dal.dataobject.wallet; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +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.Data; + +/** + * 会员钱包 DO + * + * @author jason + */ +@TableName(value ="pay_wallet") +@KeySequence("pay_wallet_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class PayWalletDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + + /** + * 用户 id + * + * å…³è” MemberUserDO çš„ id ç¼–å· + * å…³è” AdminUserDO çš„ id ç¼–å· + */ + private Long userId; + /** + * 用户类型, 预留 多商户转å¸å¯èƒ½éœ€è¦ç”¨åˆ° + * + * å…³è” {@link UserTypeEnum} + */ + private Integer userType; + + /** + * ä½™é¢ï¼Œå•ä½åˆ† + */ + private Integer balance; + + /** + * 冻结金é¢ï¼Œå•ä½åˆ† + */ + private Integer freezePrice; + + /** + * 累计支出,å•ä½åˆ† + */ + private Integer totalExpense; + /** + * 累计充值,å•ä½åˆ† + */ + private Integer totalRecharge; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletRechargeDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletRechargeDO.java new file mode 100644 index 0000000..df089f5 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletRechargeDO.java @@ -0,0 +1,116 @@ +package com.tashow.cloud.pay.dal.dataobject.wallet; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 会员钱包充值 + */ +@TableName(value ="pay_wallet_recharge") +@KeySequence("pay_wallet_recharge_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class PayWalletRechargeDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + + /** + * é’±åŒ…ç¼–å· + * + * å…³è” {@link PayWalletDO#getId()} + */ + private Long walletId; + + /** + * ç”¨æˆ·å®žé™…åˆ°è´¦ä½™é¢ + * + * 例如充 100 é€ 20,则该值是 120 + */ + private Integer totalPrice; + /** + * å®žé™…æ”¯ä»˜é‡‘é¢ + */ + private Integer payPrice; + /** + * 钱包赠é€é‡‘é¢ + */ + private Integer bonusPrice; + + /** + * 充值套é¤ç¼–å· + * + * å…³è” {@link PayWalletRechargeDO#getPackageId()} 字段 + */ + private Long packageId; + + /** + * 是å¦å·²æ”¯ä»˜ + * + * true - 已支付 + * false - 未支付 + */ + private Boolean payStatus; + + /** + * 支付订å•ç¼–å· + * + * å…³è” {@link PayOrderDO#getId()} + */ + private Long payOrderId; + + /** + * 支付æˆåŠŸçš„æ”¯ä»˜æ¸ é“ + * + * 冗余 {@link PayOrderDO#getChannelCode()} + */ + private String payChannelCode; + /** + * è®¢å•æ”¯ä»˜æ—¶é—´ + */ + private LocalDateTime payTime; + + /** + * 支付退款å•ç¼–å· + * + * å…³è” {@link PayRefundDO#getId()} + */ + private Long payRefundId; + + /** + * 退款金é¢ï¼ŒåŒ…å«èµ é€é‡‘é¢ + */ + private Integer refundTotalPrice; + /** + * é€€æ¬¾æ”¯ä»˜é‡‘é¢ + */ + private Integer refundPayPrice; + + /** + * 退款钱包赠é€é‡‘é¢ + */ + private Integer refundBonusPrice; + + /** + * 退款时间 + */ + private LocalDateTime refundTime; + + /** + * é€€æ¬¾çŠ¶æ€ + * + * 枚举 {@link PayRefundStatusEnum} + */ + private Integer refundStatus; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletRechargePackageDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletRechargePackageDO.java new file mode 100644 index 0000000..44790e7 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletRechargePackageDO.java @@ -0,0 +1,47 @@ +package com.tashow.cloud.pay.dal.dataobject.wallet; + +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.Data; + +/** + * ä¼šå‘˜é’±åŒ…å……å€¼å¥—é¤ DO + * + * é€šè¿‡å……å€¼å¥—é¤æ—¶ï¼Œå¯ä»¥èµ é€ä¸€å®šé‡‘é¢ï¼› + * + * @author èŠ‹é“æºç  + */ +@TableName(value ="pay_wallet_recharge_package") +@KeySequence("pay_wallet_recharge_package_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class PayWalletRechargePackageDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + /** + * 套é¤å + */ + private String name; + + /** + * æ”¯ä»˜é‡‘é¢ + */ + private Integer payPrice; + /** + * èµ é€é‡‘é¢ + */ + private Integer bonusPrice; + + /** + * çŠ¶æ€ + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletTransactionDO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletTransactionDO.java new file mode 100644 index 0000000..d8064d9 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/dataobject/wallet/PayWalletTransactionDO.java @@ -0,0 +1,66 @@ +package com.tashow.cloud.pay.dal.dataobject.wallet; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * ä¼šå‘˜é’±åŒ…æµæ°´ DO + * + * @author jason + */ +@TableName(value ="pay_wallet_transaction") +@KeySequence("pay_wallet_transaction_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class PayWalletTransactionDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + + /** + * æµæ°´å· + */ + private String no; + + /** + * é’±åŒ…ç¼–å· + * + * å…³è” {@link PayWalletDO#getId()} + */ + private Long walletId; + + /** + * å…³è”业务分类 + * + * 枚举 {@link PayWalletBizTypeEnum#getType()} + */ + private Integer bizType; + + /** + * å…³è”ä¸šåŠ¡ç¼–å· + */ + private String bizId; + + /** + * æµæ°´è¯´æ˜Ž + */ + private String title; + + /** + * 交易金é¢ï¼Œå•ä½åˆ† + * + * 正值表示余é¢å¢žåŠ ï¼Œè´Ÿå€¼è¡¨ç¤ºä½™é¢å‡å°‘ + */ + private Integer price; + + /** + * 交易åŽä½™é¢ï¼Œå•ä½åˆ† + */ + private Integer balance; +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/app/PayAppMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/app/PayAppMapper.java new file mode 100644 index 0000000..0200ef6 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/app/PayAppMapper.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.pay.dal.mysql.app; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PayAppMapper extends BaseMapperX { + + default PageResult selectPage(PayAppPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(PayAppDO::getName, reqVO.getName()) + .likeIfPresent(PayAppDO::getAppKey, reqVO.getAppKey()) + .eqIfPresent(PayAppDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayAppDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayAppDO::getId)); + } + + default PayAppDO selectByAppKey(String appKey) { + return selectOne(PayAppDO::getAppKey, appKey); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/channel/PayChannelMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/channel/PayChannelMapper.java new file mode 100644 index 0000000..e1b99c0 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/channel/PayChannelMapper.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.pay.dal.mysql.channel; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface PayChannelMapper extends BaseMapperX { + + default PayChannelDO selectByAppIdAndCode(Long appId, String code) { + return selectOne(PayChannelDO::getAppId, appId, PayChannelDO::getCode, code); + } + + default List selectListByAppIds(Collection appIds){ + return selectList(PayChannelDO::getAppId, appIds); + } + + default List selectListByAppId(Long appId, Integer status) { + return selectList(new LambdaQueryWrapperX() + .eq(PayChannelDO::getAppId, appId) + .eq(PayChannelDO::getStatus, status)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/demo/PayDemoOrderMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/demo/PayDemoOrderMapper.java new file mode 100644 index 0000000..c49ac37 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/demo/PayDemoOrderMapper.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.pay.dal.mysql.demo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoOrderDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * ç¤ºä¾‹è®¢å• Mapper + * + * @author èŠ‹é“æºç  + */ +@Mapper +public interface PayDemoOrderMapper extends BaseMapperX { + + default PageResult selectPage(PageParam reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .orderByDesc(PayDemoOrderDO::getId)); + } + + default int updateByIdAndPayed(Long id, boolean wherePayed, PayDemoOrderDO updateObj) { + return update(updateObj, new LambdaQueryWrapperX() + .eq(PayDemoOrderDO::getId, id).eq(PayDemoOrderDO::getPayStatus, wherePayed)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/demo/PayDemoTransferMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/demo/PayDemoTransferMapper.java new file mode 100644 index 0000000..ea6102a --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/demo/PayDemoTransferMapper.java @@ -0,0 +1,17 @@ +package com.tashow.cloud.pay.dal.mysql.demo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoTransferDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PayDemoTransferMapper extends BaseMapperX { + + default PageResult selectPage(PageParam pageParam){ + return selectPage(pageParam, new LambdaQueryWrapperX() + .orderByDesc(PayDemoTransferDO::getId)); + } +} \ No newline at end of file diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/notify/PayNotifyLogMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/notify/PayNotifyLogMapper.java new file mode 100644 index 0000000..dabdefd --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/notify/PayNotifyLogMapper.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.pay.dal.mysql.notify; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyLogDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface PayNotifyLogMapper extends BaseMapperX { + + default List selectListByTaskId(Long taskId) { + return selectList(PayNotifyLogDO::getTaskId, taskId); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/notify/PayNotifyTaskMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/notify/PayNotifyTaskMapper.java new file mode 100644 index 0000000..fdb8cb9 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/notify/PayNotifyTaskMapper.java @@ -0,0 +1,44 @@ +package com.tashow.cloud.pay.dal.mysql.notify; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyTaskDO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.List; + +@Mapper +public interface PayNotifyTaskMapper extends BaseMapperX { + + /** + * 获得需è¦é€šçŸ¥çš„ PayNotifyTaskDO è®°å½•ã€‚éœ€è¦æ»¡è¶³å¦‚下æ¡ä»¶ï¼š + * + * 1. status éžæˆåŠŸ + * 2. nextNotifyTime å°äºŽå½“剿—¶é—´ + * + * @return PayTransactionNotifyTaskDO 数组 + */ + default List selectListByNotify() { + return selectList(new LambdaQueryWrapper() + .in(PayNotifyTaskDO::getStatus, PayNotifyStatusEnum.WAITING.getStatus(), + PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()) + .le(PayNotifyTaskDO::getNextNotifyTime, LocalDateTime.now())); + } + + default PageResult selectPage(PayNotifyTaskPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PayNotifyTaskDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayNotifyTaskDO::getType, reqVO.getType()) + .eqIfPresent(PayNotifyTaskDO::getDataId, reqVO.getDataId()) + .eqIfPresent(PayNotifyTaskDO::getStatus, reqVO.getStatus()) + .eqIfPresent(PayNotifyTaskDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .betweenIfPresent(PayNotifyTaskDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayNotifyTaskDO::getId)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/order/PayOrderExtensionMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/order/PayOrderExtensionMapper.java new file mode 100644 index 0000000..ff9b66d --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/order/PayOrderExtensionMapper.java @@ -0,0 +1,38 @@ +package com.tashow.cloud.pay.dal.mysql.order; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderExtensionDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.List; + +@Mapper +public interface PayOrderExtensionMapper extends BaseMapperX { + + default PayOrderExtensionDO selectByNo(String no) { + return selectOne(PayOrderExtensionDO::getNo, no); + } + + default int updateByIdAndStatus(Long id, Integer status, PayOrderExtensionDO update) { + return update(update, new LambdaQueryWrapper() + .eq(PayOrderExtensionDO::getId, id).eq(PayOrderExtensionDO::getStatus, status)); + } + + default List selectListByOrderId(Long orderId) { + return selectList(PayOrderExtensionDO::getOrderId, orderId); + } + + default List selectListByOrderIdAndStatus(Long orderId, Integer status) { + return selectList(PayOrderExtensionDO::getOrderId, orderId, + PayOrderExtensionDO::getStatus, status); + } + + default List selectListByStatusAndCreateTimeGe(Integer status, LocalDateTime minCreateTime) { + return selectList(new LambdaQueryWrapper() + .eq(PayOrderExtensionDO::getStatus, status) + .ge(PayOrderExtensionDO::getCreateTime, minCreateTime)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/order/PayOrderMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/order/PayOrderMapper.java new file mode 100644 index 0000000..e949908 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/order/PayOrderMapper.java @@ -0,0 +1,62 @@ +package com.tashow.cloud.pay.dal.mysql.order; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderExportReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.List; + +@Mapper +public interface PayOrderMapper extends BaseMapperX { + + default PageResult selectPage(PayOrderPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PayOrderDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayOrderDO::getChannelCode, reqVO.getChannelCode()) + .likeIfPresent(PayOrderDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .likeIfPresent(PayOrderDO::getChannelOrderNo, reqVO.getChannelOrderNo()) + .likeIfPresent(PayOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(PayOrderDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayOrderDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayOrderDO::getId)); + } + + default List selectList(PayOrderExportReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(PayOrderDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayOrderDO::getChannelCode, reqVO.getChannelCode()) + .likeIfPresent(PayOrderDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .likeIfPresent(PayOrderDO::getChannelOrderNo, reqVO.getChannelOrderNo()) + .likeIfPresent(PayOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(PayOrderDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayOrderDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayOrderDO::getId)); + } + + default Long selectCountByAppId(Long appId) { + return selectCount(PayOrderDO::getAppId, appId); + } + + default PayOrderDO selectByAppIdAndMerchantOrderId(Long appId, String merchantOrderId) { + return selectOne(PayOrderDO::getAppId, appId, + PayOrderDO::getMerchantOrderId, merchantOrderId); + } + + default int updateByIdAndStatus(Long id, Integer status, PayOrderDO update) { + return update(update, new LambdaQueryWrapper() + .eq(PayOrderDO::getId, id).eq(PayOrderDO::getStatus, status)); + } + + default List selectListByStatusAndExpireTimeLt(Integer status, LocalDateTime expireTime) { + return selectList(new LambdaQueryWrapper() + .eq(PayOrderDO::getStatus, status) + .lt(PayOrderDO::getExpireTime, expireTime)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/refund/PayRefundMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/refund/PayRefundMapper.java new file mode 100644 index 0000000..a8e383c --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/refund/PayRefundMapper.java @@ -0,0 +1,78 @@ +package com.tashow.cloud.pay.dal.mysql.refund; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundExportReqVO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface PayRefundMapper extends BaseMapperX { + + default Long selectCountByAppId(Long appId) { + return selectCount(PayRefundDO::getAppId, appId); + } + + default PayRefundDO selectByAppIdAndMerchantRefundId(Long appId, String merchantRefundId) { + return selectOne(new LambdaQueryWrapperX() + .eq(PayRefundDO::getAppId, appId) + .eq(PayRefundDO::getMerchantRefundId, merchantRefundId)); + } + + default Long selectCountByAppIdAndOrderId(Long appId, Long orderId, Integer status) { + return selectCount(new LambdaQueryWrapperX() + .eq(PayRefundDO::getAppId, appId) + .eq(PayRefundDO::getOrderId, orderId) + .eq(PayRefundDO::getStatus, status)); + } + + default PayRefundDO selectByAppIdAndNo(Long appId, String no) { + return selectOne(new LambdaQueryWrapperX() + .eq(PayRefundDO::getAppId, appId) + .eq(PayRefundDO::getNo, no)); + } + + default PayRefundDO selectByNo(String no) { + return selectOne(PayRefundDO::getNo, no); + } + + default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) { + return update(update, new LambdaQueryWrapper() + .eq(PayRefundDO::getId, id).eq(PayRefundDO::getStatus, status)); + } + + default PageResult selectPage(PayRefundPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PayRefundDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayRefundDO::getChannelCode, reqVO.getChannelCode()) + .likeIfPresent(PayRefundDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .likeIfPresent(PayRefundDO::getMerchantRefundId, reqVO.getMerchantRefundId()) + .likeIfPresent(PayRefundDO::getChannelOrderNo, reqVO.getChannelOrderNo()) + .likeIfPresent(PayRefundDO::getChannelRefundNo, reqVO.getChannelRefundNo()) + .eqIfPresent(PayRefundDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayRefundDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayRefundDO::getId)); + } + + default List selectList(PayRefundExportReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(PayRefundDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayRefundDO::getChannelCode, reqVO.getChannelCode()) + .likeIfPresent(PayRefundDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .likeIfPresent(PayRefundDO::getMerchantRefundId, reqVO.getMerchantRefundId()) + .likeIfPresent(PayRefundDO::getChannelOrderNo, reqVO.getChannelOrderNo()) + .likeIfPresent(PayRefundDO::getChannelRefundNo, reqVO.getChannelRefundNo()) + .eqIfPresent(PayRefundDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayRefundDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayRefundDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(PayRefundDO::getStatus, status); + } +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/transfer/PayTransferMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/transfer/PayTransferMapper.java new file mode 100644 index 0000000..b6f8a8a --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/transfer/PayTransferMapper.java @@ -0,0 +1,53 @@ +package com.tashow.cloud.pay.dal.mysql.transfer; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.controller.admin.transfer.vo.PayTransferPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.transfer.PayTransferDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface PayTransferMapper extends BaseMapperX { + + default int updateByIdAndStatus(Long id, List status, PayTransferDO updateObj) { + return update(updateObj, new LambdaQueryWrapper() + .eq(PayTransferDO::getId, id).in(PayTransferDO::getStatus, status)); + } + + default PayTransferDO selectByAppIdAndMerchantTransferId(Long appId, String merchantTransferId){ + return selectOne(PayTransferDO::getAppId, appId, + PayTransferDO::getMerchantTransferId, merchantTransferId); + } + + default PageResult selectPage(PayTransferPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PayTransferDO::getNo, reqVO.getNo()) + .eqIfPresent(PayTransferDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayTransferDO::getChannelCode, reqVO.getChannelCode()) + .eqIfPresent(PayTransferDO::getMerchantTransferId, reqVO.getMerchantTransferId()) + .eqIfPresent(PayTransferDO::getType, reqVO.getType()) + .eqIfPresent(PayTransferDO::getStatus, reqVO.getStatus()) + .likeIfPresent(PayTransferDO::getUserName, reqVO.getUserName()) + .eqIfPresent(PayTransferDO::getChannelTransferNo, reqVO.getChannelTransferNo()) + .betweenIfPresent(PayTransferDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayTransferDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(PayTransferDO::getStatus, status); + } + + default PayTransferDO selectByAppIdAndNo(Long appId, String no) { + return selectOne(PayTransferDO::getAppId, appId, + PayTransferDO::getNo, no); + } + +} + + + + diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletMapper.java new file mode 100644 index 0000000..76e75ba --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletMapper.java @@ -0,0 +1,134 @@ +package com.tashow.cloud.pay.dal.mysql.wallet; + + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.controller.admin.wallet.vo.wallet.PayWalletPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PayWalletMapper extends BaseMapperX { + + default PayWalletDO selectByUserIdAndType(Long userId, Integer userType) { + return selectOne(PayWalletDO::getUserId, userId, + PayWalletDO::getUserType, userType); + } + + default PageResult selectPage(PayWalletPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PayWalletDO::getUserId, reqVO.getUserId()) + .eqIfPresent(PayWalletDO::getUserType, reqVO.getUserType()) + .betweenIfPresent(PayWalletDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayWalletDO::getId)); + } + + /** + * 当消费退款时候, 更新钱包 + * + * @param id 钱包 id + * @param price æ¶ˆè´¹é‡‘é¢ + */ + default int updateWhenConsumptionRefund(Long id, Integer price) { + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" balance = balance + " + price + + ", total_expense = total_expense - " + price) + .eq(PayWalletDO::getId, id); + return update(null, lambdaUpdateWrapper); + } + + /** + * 当消费时候, 更新钱包 + * + * @param price æ¶ˆè´¹é‡‘é¢ + * @param id 钱包 id + */ + default int updateWhenConsumption(Long id, Integer price){ + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" balance = balance - " + price + + ", total_expense = total_expense + " + price) + .eq(PayWalletDO::getId, id) + .ge(PayWalletDO::getBalance, price); // cas 逻辑 + return update(null, lambdaUpdateWrapper); + } + + /** + * 当充值的时候,更新钱包 + * + * @param id 钱包 id + * @param price é’±åŒ…é‡‘é¢ + */ + default int updateWhenRecharge(Long id, Integer price){ + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" balance = balance + " + price + + ", total_recharge = total_recharge + " + price) + .eq(PayWalletDO::getId, id); + return update(null, lambdaUpdateWrapper); + } + + /** + * 增加余é¢çš„æ—¶å€™ï¼Œæ›´æ–°é’±åŒ… + * + * @param id 钱包 id + * @param price é’±åŒ…é‡‘é¢ + */ + default void updateWhenAdd(Long id, Integer price) { + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" balance = balance + " + price) + .eq(PayWalletDO::getId, id); + update(null, lambdaUpdateWrapper); + } + + /** + * å†»ç»“é’±åŒ…éƒ¨åˆ†ä½™é¢ + * + * @param id 钱包 id + * @param price å†»ç»“é‡‘é¢ + */ + default int freezePrice(Long id, Integer price){ + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" balance = balance - " + price + + ", freeze_price = freeze_price + " + price) + .eq(PayWalletDO::getId, id) + .ge(PayWalletDO::getBalance, price); // cas 逻辑 + return update(null, lambdaUpdateWrapper); + } + + /** + * è§£å†»é’±åŒ…ä½™é¢ + * + * @param id 钱包 id + * @param price è§£å†»é‡‘é¢ + */ + default int unFreezePrice(Long id, Integer price){ + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" balance = balance + " + price + + ", freeze_price = freeze_price - " + price) + .eq(PayWalletDO::getId, id) + .ge(PayWalletDO::getFreezePrice, price); // cas 逻辑 + return update(null, lambdaUpdateWrapper); + } + + /** + * 当充值退款时, 更新钱包 + * + * @param id 钱包 id + * @param price é€€æ¬¾é‡‘é¢ + */ + default int updateWhenRechargeRefund(Long id, Integer price){ + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" freeze_price = freeze_price - " + price + + ", total_recharge = total_recharge - " + price) + .eq(PayWalletDO::getId, id) + .ge(PayWalletDO::getFreezePrice, price) + .ge(PayWalletDO::getTotalRecharge, price);// cas 逻辑 + return update(null, lambdaUpdateWrapper); + } + +} + + + + diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletRechargeMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletRechargeMapper.java new file mode 100644 index 0000000..174d6da --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletRechargeMapper.java @@ -0,0 +1,30 @@ +package com.tashow.cloud.pay.dal.mysql.wallet; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargeDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PayWalletRechargeMapper extends BaseMapperX { + + default int updateByIdAndPaid(Long id, boolean wherePayStatus, PayWalletRechargeDO updateObj) { + return update(updateObj, new LambdaQueryWrapperX() + .eq(PayWalletRechargeDO::getId, id).eq(PayWalletRechargeDO::getPayStatus, wherePayStatus)); + } + + default int updateByIdAndRefunded(Long id, Integer whereRefundStatus, PayWalletRechargeDO updateObj) { + return update(updateObj, new LambdaQueryWrapperX() + .eq(PayWalletRechargeDO::getId, id).eq(PayWalletRechargeDO::getRefundStatus, whereRefundStatus)); + } + + default PageResult selectPage(PageParam pageReqVO, Long walletId, Boolean payStatus) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .eq(PayWalletRechargeDO::getWalletId, walletId) + .eq(PayWalletRechargeDO::getPayStatus, payStatus) + .orderByDesc(PayWalletRechargeDO::getId)); + } + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletRechargePackageMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletRechargePackageMapper.java new file mode 100644 index 0000000..ebd17c8 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletRechargePackageMapper.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.pay.dal.mysql.wallet; + + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackagePageReqVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface PayWalletRechargePackageMapper extends BaseMapperX { + + default PageResult selectPage(WalletRechargePackagePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(PayWalletRechargePackageDO::getName, reqVO.getName()) + .eqIfPresent(PayWalletRechargePackageDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayWalletRechargePackageDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayWalletRechargePackageDO::getPayPrice)); + } + + default PayWalletRechargePackageDO selectByName(String name) { + return selectOne(PayWalletRechargePackageDO::getName, name); + } + + default List selectListByStatus(Integer status) { + return selectList(PayWalletRechargePackageDO::getStatus, status); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletTransactionMapper.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletTransactionMapper.java new file mode 100644 index 0000000..0d9210e --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/mysql/wallet/PayWalletTransactionMapper.java @@ -0,0 +1,65 @@ +package com.tashow.cloud.pay.dal.mysql.wallet; + + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletTransactionDO; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO.TYPE_EXPENSE; +import static com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO.TYPE_INCOME; + +@Mapper +public interface PayWalletTransactionMapper extends BaseMapperX { + + default PageResult selectPage(Long walletId, Integer type, + PageParam pageParam, LocalDateTime[] createTime) { + LambdaQueryWrapperX query = new LambdaQueryWrapperX() + .eqIfPresent(PayWalletTransactionDO::getWalletId, walletId); + if (Objects.equals(type, TYPE_INCOME)) { + query.gt(PayWalletTransactionDO::getPrice, 0); + } else if (Objects.equals(type, TYPE_EXPENSE)) { + query.lt(PayWalletTransactionDO::getPrice, 0); + } + query.betweenIfPresent(PayWalletTransactionDO::getCreateTime, createTime); + query.orderByDesc(PayWalletTransactionDO::getId); + return selectPage(pageParam, query); + } + + default Integer selectPriceSum(Long walletId, Integer type, LocalDateTime[] createTime) { + // SQL sum 查询 + List> result = selectMaps(new QueryWrapperX() + .select("SUM(price) AS priceSum") + .gt(Objects.equals(type, TYPE_INCOME), "price", 0) // æ”¶å…¥ + .lt(Objects.equals(type, TYPE_EXPENSE), "price", 0) // 支出 + .eq("wallet_id", walletId) + .between("create_time", createTime[0], createTime[1])); + // 获得 sum 结果 + Map first = CollUtil.getFirst(result); + return MapUtil.getInt(first, "priceSum", 0); + } + + default PayWalletTransactionDO selectByNo(String no) { + return selectOne(PayWalletTransactionDO::getNo, no); + } + + default PayWalletTransactionDO selectByBiz(String bizId, Integer bizType) { + return selectOne(PayWalletTransactionDO::getBizId, bizId, + PayWalletTransactionDO::getBizType, bizType); + } + +} + + + + diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/RedisKeyConstants.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/RedisKeyConstants.java new file mode 100644 index 0000000..168eb8f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/RedisKeyConstants.java @@ -0,0 +1,36 @@ +package com.tashow.cloud.pay.dal.redis; + +/** + * 支付 Redis Key 枚举类 + * + * @author èŠ‹é“æºç  + */ +public interface RedisKeyConstants { + + /** + * 通知任务的分布å¼é” + * + * KEY æ ¼å¼ï¼špay_notify:lock:%d // 傿•°æ¥è‡ª DefaultLockKeyBuilder ç±» + * VALUE æ•°æ®æ ¼å¼ï¼šHASH // RLock.class:Redisson çš„ Lock é”,使用 Hash æ•°æ®ç»“æž„ + * 过期时间:ä¸å›ºå®š + */ + String PAY_NOTIFY_LOCK = "pay_notify:lock:%d"; + + /** + * 支付钱包的分布å¼é” + * + * KEY æ ¼å¼ï¼špay_wallet:lock:%d + * VALUE æ•°æ®æ ¼å¼ï¼šHASH // RLock.class:Redisson çš„ Lock é”,使用 Hash æ•°æ®ç»“æž„ + * 过期时间:ä¸å›ºå®š + */ + String PAY_WALLET_LOCK = "pay_wallet:lock:%d"; + + /** + * 支付åºå·çš„缓存 + * + * KEY æ ¼å¼ï¼špay_no:{prefix} + * VALUE æ•°æ®æ ¼å¼ï¼šç¼–å·è‡ªå¢ž + */ + String PAY_NO = "pay_no:"; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/no/PayNoRedisDAO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/no/PayNoRedisDAO.java new file mode 100644 index 0000000..77083b3 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/no/PayNoRedisDAO.java @@ -0,0 +1,40 @@ +package com.tashow.cloud.pay.dal.redis.no; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import com.tashow.cloud.pay.dal.redis.RedisKeyConstants; +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import java.time.Duration; +import java.time.LocalDateTime; + +/** + * 支付åºå·çš„ Redis DAO + * + * @author èŠ‹é“æºç  + */ +@Repository +public class PayNoRedisDAO { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + /** + * 生æˆåºå· + * + * @param prefix å‰ç¼€ + * @return åºå· + */ + public String generate(String prefix) { + // 递增åºå· + String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN); + String key = RedisKeyConstants.PAY_NO + noPrefix; + Long no = stringRedisTemplate.opsForValue().increment(key); + // 设置过期时间 + stringRedisTemplate.expire(key, Duration.ofMinutes(1L)); + return noPrefix + no; + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/notify/PayNotifyLockRedisDAO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/notify/PayNotifyLockRedisDAO.java new file mode 100644 index 0000000..affec36 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/notify/PayNotifyLockRedisDAO.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.pay.dal.redis.notify; + +import jakarta.annotation.Resource; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.stereotype.Repository; + +import java.util.concurrent.TimeUnit; + +import static com.tashow.cloud.pay.dal.redis.RedisKeyConstants.PAY_NOTIFY_LOCK; + +/** + * æ”¯ä»˜é€šçŸ¥çš„é” Redis DAO + * + * @author èŠ‹é“æºç  + */ +@Repository +public class PayNotifyLockRedisDAO { + + @Resource + private RedissonClient redissonClient; + + public void lock(Long id, Long timeoutMillis, Runnable runnable) { + String lockKey = formatKey(id); + RLock lock = redissonClient.getLock(lockKey); + try { + lock.lock(timeoutMillis, TimeUnit.MILLISECONDS); + // 执行逻辑 + runnable.run(); + } finally { + lock.unlock(); + } + } + + private static String formatKey(Long id) { + return String.format(PAY_NOTIFY_LOCK, id); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/wallet/PayWalletLockRedisDAO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/wallet/PayWalletLockRedisDAO.java new file mode 100644 index 0000000..33d5a7f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/dal/redis/wallet/PayWalletLockRedisDAO.java @@ -0,0 +1,42 @@ +package com.tashow.cloud.pay.dal.redis.wallet; + +import jakarta.annotation.Resource; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.stereotype.Repository; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import static com.tashow.cloud.pay.dal.redis.RedisKeyConstants.PAY_WALLET_LOCK; + +/** + * æ”¯ä»˜é’±åŒ…çš„é” Redis DAO + * + * @author èŠ‹é“æºç  + */ +@Repository +public class PayWalletLockRedisDAO { + + @Resource + private RedissonClient redissonClient; + + public V lock(Long id, Long timeoutMillis, Callable callable) throws Exception { + String lockKey = formatKey(id); + RLock lock = redissonClient.getLock(lockKey); + try { + lock.lock(timeoutMillis, TimeUnit.MILLISECONDS); + // 执行逻辑 + return callable.call(); + } catch (Exception e) { + throw e; + } finally { + lock.unlock(); + } + } + + private static String formatKey(Long id) { + return String.format(PAY_WALLET_LOCK, id); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/job/config/PayJobConfiguration.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/job/config/PayJobConfiguration.java new file mode 100644 index 0000000..e849b22 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/job/config/PayJobConfiguration.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.pay.framework.job.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +@Configuration(proxyBeanMethods = false) +public class PayJobConfiguration { + + public static final String NOTIFY_THREAD_POOL_TASK_EXECUTOR = "NOTIFY_THREAD_POOL_TASK_EXECUTOR"; + + @Bean(NOTIFY_THREAD_POOL_TASK_EXECUTOR) + public ThreadPoolTaskExecutor notifyThreadPoolTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(8); // 设置核心线程数 + executor.setMaxPoolSize(16); // 设置最大线程数 + executor.setKeepAliveSeconds(60); // 设置空闲时间 + executor.setQueueCapacity(100); // è®¾ç½®é˜Ÿåˆ—å¤§å° + executor.setThreadNamePrefix("notify-task-"); // é…置线程池的å‰ç¼€ + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + // 进行加载 + executor.initialize(); + return executor; + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/job/core/package-info.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/job/core/package-info.java new file mode 100644 index 0000000..279fbaf --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/job/core/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ + */ +package com.tashow.cloud.pay.framework.job.core; diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/package-info.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/package-info.java new file mode 100644 index 0000000..d8641a4 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 pay 模å—çš„ framework å°è£… + * + * @author èŠ‹é“æºç  + */ +package com.tashow.cloud.pay.framework; diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/config/PayConfiguration.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/config/PayConfiguration.java new file mode 100644 index 0000000..49ad8ab --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/config/PayConfiguration.java @@ -0,0 +1,9 @@ +package com.tashow.cloud.pay.framework.pay.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(PayProperties.class) +public class PayConfiguration { +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/config/PayProperties.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/config/PayProperties.java new file mode 100644 index 0000000..4dc1b68 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/config/PayProperties.java @@ -0,0 +1,69 @@ +package com.tashow.cloud.pay.framework.pay.config; + +import lombok.Data; +import org.hibernate.validator.constraints.URL; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.constraints.NotEmpty; + +@ConfigurationProperties(prefix = "yudao.pay") +@Validated +@Data +public class PayProperties { + + private static final String ORDER_NO_PREFIX = "P"; + private static final String REFUND_NO_PREFIX = "R"; + + private static final String WALLET_PAY_APP_KEY_DEFAULT = "wallet"; + + /** + * æ”¯ä»˜å›žè°ƒåœ°å€ + * + * 实际上,对应的 PayNotifyController çš„ notifyOrder 方法的 URL + * + * 回调顺åºï¼šæ”¯ä»˜æ¸ é“ï¼ˆæ”¯ä»˜å®æ”¯ä»˜ã€å¾®ä¿¡æ”¯ä»˜ï¼‰ => yudao-module-pay çš„ orderNotifyUrl åœ°å€ => 业务的 PayAppDO.orderNotifyUrl åœ°å€ + */ + @NotEmpty(message = "支付回调地å€ä¸èƒ½ä¸ºç©º") + @URL(message = "支付回调地å€çš„æ ¼å¼å¿…须是 URL") + private String orderNotifyUrl; + + /** + * é€€æ¬¾å›žè°ƒåœ°å€ + * + * 实际上,对应的 PayNotifyController çš„ notifyRefund 方法的 URL + * + * 回调顺åºï¼šæ”¯ä»˜æ¸ é“ï¼ˆæ”¯ä»˜å®æ”¯ä»˜ã€å¾®ä¿¡æ”¯ä»˜ï¼‰ => yudao-module-pay çš„ refundNotifyUrl åœ°å€ => 业务的 PayAppDO.notifyRefundUrl åœ°å€ + */ + @NotEmpty(message = "支付回调地å€ä¸èƒ½ä¸ºç©º") + @URL(message = "支付回调地å€çš„æ ¼å¼å¿…须是 URL") + private String refundNotifyUrl; + + /** + * è½¬è´¦å›žè°ƒåœ°å€ + * + * 实际上,对应的 PayNotifyController çš„ notifyTransfer 方法的 URL + * + * 回调顺åºï¼šæ”¯ä»˜æ¸ é“ï¼ˆæ”¯ä»˜å®æ”¯ä»˜ã€å¾®ä¿¡æ”¯ä»˜ï¼‰ => yudao-module-pay çš„ transferNotifyUrl åœ°å€ => 业务的 PayAppDO.transferNotifyUrl åœ°å€ + */ + private String transferNotifyUrl; + + /** + * æ”¯ä»˜è®¢å• no çš„å‰ç¼€ + */ + @NotEmpty(message = "æ”¯ä»˜è®¢å• no çš„å‰ç¼€ä¸èƒ½ä¸ºç©º") + private String orderNoPrefix = ORDER_NO_PREFIX; + + /** + * é€€æ¬¾è®¢å• no çš„å‰ç¼€ + */ + @NotEmpty(message = "é€€æ¬¾è®¢å• no çš„å‰ç¼€ä¸èƒ½ä¸ºç©º") + private String refundNoPrefix = REFUND_NO_PREFIX; + + /** + * 钱包支付应用 AppKey + */ + @NotEmpty(message = "钱包支付应用 AppKey ä¸èƒ½ä¸ºç©º") + private String walletPayAppKey = WALLET_PAY_APP_KEY_DEFAULT; + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/core/WalletPayClient.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/core/WalletPayClient.java new file mode 100644 index 0000000..ba32bbd --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/pay/core/WalletPayClient.java @@ -0,0 +1,195 @@ +package com.tashow.cloud.pay.framework.pay.core; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderExtensionDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletTransactionDO; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import com.tashow.cloud.pay.service.wallet.PayWalletService; +import com.tashow.cloud.pay.service.wallet.PayWalletTransactionService; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.REFUND_NOT_FOUND; + +/** + * 钱包支付的 PayClient 实现类 + * + * @author jason + */ +@Slf4j +public class WalletPayClient extends AbstractPayClient { + + public static final String USER_ID_KEY = "user_id"; + public static final String USER_TYPE_KEY = "user_type"; + + private PayWalletService wallService; + private PayWalletTransactionService walletTransactionService; + private PayOrderService orderService; + private PayRefundService refundService; + + public WalletPayClient(Long channelId, NonePayClientConfig config) { + super(channelId, PayChannelEnum.WALLET.getCode(), config); + } + + @Override + protected void doInit() { + if (wallService == null) { + wallService = SpringUtil.getBean(PayWalletService.class); + } + if (walletTransactionService == null) { + walletTransactionService = SpringUtil.getBean(PayWalletTransactionService.class); + } + } + + @Override + protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { + try { + Long userId = MapUtil.getLong(reqDTO.getChannelExtras(), USER_ID_KEY); + Integer userType = MapUtil.getInt(reqDTO.getChannelExtras(), USER_TYPE_KEY); + Assert.notNull(userId, "用户 id ä¸èƒ½ä¸ºç©º"); + Assert.notNull(userType, "用户类型ä¸èƒ½ä¸ºç©º"); + PayWalletTransactionDO transaction = wallService.orderPay(userId, userType, reqDTO.getOutTradeNo(), + reqDTO.getPrice()); + return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(), + transaction.getCreateTime(), + reqDTO.getOutTradeNo(), transaction); + } catch (Throwable ex) { + log.error("[doUnifiedOrder] 失败", ex); + Integer errorCode = INTERNAL_SERVER_ERROR.getCode(); + String errorMsg = INTERNAL_SERVER_ERROR.getMsg(); + if (ex instanceof ServiceException) { + ServiceException serviceException = (ServiceException) ex; + errorCode = serviceException.getCode(); + errorMsg = serviceException.getMessage(); + } + return PayOrderRespDTO.closedOf(String.valueOf(errorCode), errorMsg, + reqDTO.getOutTradeNo(), ""); + } + } + + @Override + protected PayOrderRespDTO doParseOrderNotify(Map params, String body) { + throw new UnsupportedOperationException("钱包支付无支付回调"); + } + + @Override + protected PayOrderRespDTO doGetOrder(String outTradeNo) { + if (orderService == null) { + orderService = SpringUtil.getBean(PayOrderService.class); + } + PayOrderExtensionDO orderExtension = orderService.getOrderExtensionByNo(outTradeNo); + // 支付交易拓展å•ä¸å­˜åœ¨ï¼Œ è¿”å›žå…³é—­çŠ¶æ€ + if (orderExtension == null) { + return PayOrderRespDTO.closedOf(String.valueOf(PAY_ORDER_EXTENSION_NOT_FOUND.getCode()), + PAY_ORDER_EXTENSION_NOT_FOUND.getMsg(), outTradeNo, ""); + } + // å…³é—­çŠ¶æ€ + if (PayOrderStatusEnum.isClosed(orderExtension.getStatus())) { + return PayOrderRespDTO.closedOf(orderExtension.getChannelErrorCode(), + orderExtension.getChannelErrorMsg(), outTradeNo, ""); + } + // æˆåŠŸçŠ¶æ€ + if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { + PayWalletTransactionDO walletTransaction = walletTransactionService.getWalletTransaction( + String.valueOf(orderExtension.getOrderId()), PayWalletBizTypeEnum.PAYMENT); + Assert.notNull(walletTransaction, "æ”¯ä»˜å• {} é’±åŒ…æµæ°´ä¸èƒ½ä¸ºç©º", outTradeNo); + return PayOrderRespDTO.successOf(walletTransaction.getNo(), walletTransaction.getCreator(), + walletTransaction.getCreateTime(), outTradeNo, walletTransaction); + } + // 其它状æ€ä¸ºæ— æ•ˆçŠ¶æ€ + log.error("[doGetOrder] æ”¯ä»˜å• {} 的状æ€ä¸æ­£ç¡®", outTradeNo); + throw new IllegalStateException(String.format("支付å•[%s] 状æ€ä¸æ­£ç¡®", outTradeNo)); + } + + @Override + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { + try { + PayWalletTransactionDO payWalletTransaction = wallService.orderRefund(reqDTO.getOutRefundNo(), + reqDTO.getRefundPrice(), reqDTO.getReason()); + return PayRefundRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreateTime(), + reqDTO.getOutRefundNo(), payWalletTransaction); + } catch (Throwable ex) { + log.error("[doUnifiedRefund] 失败", ex); + Integer errorCode = INTERNAL_SERVER_ERROR.getCode(); + String errorMsg = INTERNAL_SERVER_ERROR.getMsg(); + if (ex instanceof ServiceException) { + ServiceException serviceException = (ServiceException) ex; + errorCode = serviceException.getCode(); + errorMsg = serviceException.getMessage(); + } + return PayRefundRespDTO.failureOf(String.valueOf(errorCode), errorMsg, + reqDTO.getOutRefundNo(), ""); + } + } + + @Override + protected PayRefundRespDTO doParseRefundNotify(Map params, String body) { + throw new UnsupportedOperationException("钱包支付无退款回调"); + } + + @Override + protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) { + if (refundService == null) { + refundService = SpringUtil.getBean(PayRefundService.class); + } + PayRefundDO payRefund = refundService.getRefundByNo(outRefundNo); + // 支付退款å•ä¸å­˜åœ¨ï¼Œ è¿”å›žé€€æ¬¾å¤±è´¥çŠ¶æ€ + if (payRefund == null) { + return PayRefundRespDTO.failureOf(String.valueOf(REFUND_NOT_FOUND), REFUND_NOT_FOUND.getMsg(), + outRefundNo, ""); + } + // 退款失败 + if (PayRefundStatusRespEnum.isFailure(payRefund.getStatus())) { + return PayRefundRespDTO.failureOf(payRefund.getChannelErrorCode(), payRefund.getChannelErrorMsg(), + outRefundNo, ""); + } + // 退款æˆåŠŸ + if (PayRefundStatusRespEnum.isSuccess(payRefund.getStatus())) { + PayWalletTransactionDO walletTransaction = walletTransactionService.getWalletTransaction( + String.valueOf(payRefund.getId()), PayWalletBizTypeEnum.PAYMENT_REFUND); + Assert.notNull(walletTransaction, "æ”¯ä»˜é€€æ¬¾å• {} é’±åŒ…æµæ°´ä¸èƒ½ä¸ºç©º", outRefundNo); + return PayRefundRespDTO.successOf(walletTransaction.getNo(), walletTransaction.getCreateTime(), + outRefundNo, walletTransaction); + } + // 其它状æ€ä¸ºæ— æ•ˆçŠ¶æ€ + log.error("[doGetRefund] æ”¯ä»˜é€€æ¬¾å• {} 的状æ€ä¸æ­£ç¡®", outRefundNo); + throw new IllegalStateException(String.format("支付退款å•[%s] 状æ€ä¸æ­£ç¡®", outRefundNo)); + } + + @Override + protected PayTransferRespDTO doParseTransferNotify(Map params, String body) throws Throwable { + throw new UnsupportedOperationException("未实现"); + } + + @Override + public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { + throw new UnsupportedOperationException("待实现"); + } + + @Override + protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) { + throw new UnsupportedOperationException("待实现"); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/rpc/config/RpcConfiguration.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/rpc/config/RpcConfiguration.java new file mode 100644 index 0000000..0aa0715 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/rpc/config/RpcConfiguration.java @@ -0,0 +1,10 @@ +package com.tashow.cloud.pay.framework.rpc.config; + +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@EnableFeignClients(clients = {SocialClientApi.class}) +public class RpcConfiguration { +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/rpc/package-info.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/rpc/package-info.java new file mode 100644 index 0000000..849d767 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/rpc/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ + */ +package com.tashow.cloud.pay.framework.rpc; diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/security/config/SecurityConfiguration.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/security/config/SecurityConfiguration.java new file mode 100644 index 0000000..54fc80b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/security/config/SecurityConfiguration.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.pay.framework.security.config; + +import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; +import cn.iocoder.yudao.module.pay.enums.ApiConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; + +/** + * Pay 模å—çš„ Security é…ç½® + */ +@Configuration("paySecurityConfiguration") +public class SecurityConfiguration { + + @Bean("payAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + + @Override + public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { + // Swagger æŽ¥å£æ–‡æ¡£ + registry.requestMatchers("/v3/api-docs/**").permitAll() + .requestMatchers("/webjars/**").permitAll() + .requestMatchers("/swagger-ui").permitAll() + .requestMatchers("/swagger-ui/**").permitAll(); + // Spring Boot Actuator 的安全é…ç½® + registry.requestMatchers("/actuator").permitAll() + .requestMatchers("/actuator/**").permitAll(); + // Druid 监控 + registry.requestMatchers("/druid/**").permitAll(); + // RPC æœåŠ¡çš„å®‰å…¨é…ç½® + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); + } + + }; + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/security/core/package-info.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/security/core/package-info.java new file mode 100644 index 0000000..2336c92 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/framework/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ + */ +package com.tashow.cloud.pay.framework.security.core; diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/notify/PayNotifyJob.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/notify/PayNotifyJob.java new file mode 100644 index 0000000..ff983e0 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/notify/PayNotifyJob.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.pay.job.notify; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import com.tashow.cloud.pay.service.notify.PayNotifyService; +import com.xxl.job.core.handler.annotation.XxlJob; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 支付通知 Job + * é€šè¿‡ä¸æ–­æ‰«æå¾…通知的 PayNotifyTaskDO è®°å½•ï¼Œå›žè°ƒä¸šåŠ¡çº¿çš„å›žè°ƒæŽ¥å£ + * + * @author èŠ‹é“æºç  + */ +@Component +@Slf4j +public class PayNotifyJob { + + @Resource + private PayNotifyService payNotifyService; + + @XxlJob("payNotifyJob") + @TenantJob // 多租户 + public String execute() throws Exception { + int notifyCount = payNotifyService.executeNotify(); + log.info("[execute][执行支付通知 ({}) 个]", notifyCount); + return StrUtil.format("执行支付通知 ({}) 个",notifyCount); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/order/PayOrderExpireJob.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/order/PayOrderExpireJob.java new file mode 100644 index 0000000..2df0ca3 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/order/PayOrderExpireJob.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.pay.job.order; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.xxl.job.core.handler.annotation.XxlJob; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 支付订å•的过期 Job + * + * æ”¯ä»˜è¶…è¿‡è¿‡æœŸæ—¶é—´æ—¶ï¼Œæ”¯ä»˜æ¸ é“æ˜¯ä¸ä¼šé€šçŸ¥è¿›è¡Œè¿‡æœŸï¼Œæ‰€ä»¥éœ€è¦å®šæ—¶è¿›è¡Œè¿‡æœŸå…³é—­ã€‚ + * + * @author èŠ‹é“æºç  + */ +@Component +@Slf4j +public class PayOrderExpireJob { + + @Resource + private PayOrderService orderService; + + @XxlJob("payOrderExpireJob") + @TenantJob // 多租户 + public String execute(String param) { + int count = orderService.expireOrder(); + log.info("[execute][支付过期 ({}) 个]", count); + return StrUtil.format("支付过期 ({}) 个",count); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/order/PayOrderSyncJob.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/order/PayOrderSyncJob.java new file mode 100644 index 0000000..6291bbd --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/order/PayOrderSyncJob.java @@ -0,0 +1,46 @@ +package com.tashow.cloud.pay.job.order; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.xxl.job.core.handler.annotation.XxlJob; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.LocalDateTime; + +/** + * 支付订å•çš„åŒæ­¥ Job + * + * 由于支付订å•的状æ€ï¼Œæ˜¯ç”±æ”¯ä»˜æ¸ é“å¼‚æ­¥é€šçŸ¥è¿›è¡ŒåŒæ­¥ï¼Œè€ƒè™‘到异步通知å¯èƒ½ä¼šå¤±è´¥ï¼ˆå°æ¦‚率),所以需è¦å®šæ—¶è¿›è¡ŒåŒæ­¥ã€‚ + * + * @author èŠ‹é“æºç  + */ +@Component +@Slf4j +public class PayOrderSyncJob { + + /** + * åŒæ­¥åˆ›å»ºæ—¶é—´åœ¨ N åˆ†é’Ÿä¹‹å†…çš„è®¢å• + * + * ä¸ºä»€ä¹ˆåŒæ­¥ 10 分钟之内的订å•? + * 因为一个订å•å‘起支付,到支付æˆåŠŸï¼Œå¤§å¤šæ•°åœ¨ 10 分钟内,需è¦ä¿è¯è½®è¯¢åˆ°ã€‚ + * 如果设置为 30ã€60 或者更大时间范围,会导致轮询的订å•å¤ªå¤šï¼Œå½±å“æ€§èƒ½ã€‚当然,你也å¯ä»¥æ ¹æ®è‡ªå·±çš„业务情况æ¥å¤„ç†ã€‚ + */ + private static final Duration CREATE_TIME_DURATION_BEFORE = Duration.ofMinutes(10); + + @Resource + private PayOrderService orderService; + + @XxlJob("payOrderSyncJob") + @TenantJob // 多租户 + public String execute() { + LocalDateTime minCreateTime = LocalDateTime.now().minus(CREATE_TIME_DURATION_BEFORE); + int count = orderService.syncOrder(minCreateTime); + log.info("[execute][åŒæ­¥æ”¯ä»˜è®¢å• ({}) 个]", count); + return StrUtil.format("åŒæ­¥æ”¯ä»˜è®¢å• ({}) 个",count); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/refund/PayRefundSyncJob.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/refund/PayRefundSyncJob.java new file mode 100644 index 0000000..fd45031 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/refund/PayRefundSyncJob.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.pay.job.refund; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import com.xxl.job.core.handler.annotation.XxlJob; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 退款订å•çš„åŒæ­¥ Job + * + * 由于退款订å•的状æ€ï¼Œæ˜¯ç”±æ”¯ä»˜æ¸ é“å¼‚æ­¥é€šçŸ¥è¿›è¡ŒåŒæ­¥ï¼Œè€ƒè™‘到异步通知å¯èƒ½ä¼šå¤±è´¥ï¼ˆå°æ¦‚率),所以需è¦å®šæ—¶è¿›è¡ŒåŒæ­¥ã€‚ + * + * @author èŠ‹é“æºç  + */ +@Component +@Slf4j +public class PayRefundSyncJob { + + @Resource + private PayRefundService refundService; + + @XxlJob("payRefundSyncJob") + @TenantJob // 多租户 + public String execute() { + int count = refundService.syncRefund(); + log.info("[execute][åŒæ­¥é€€æ¬¾è®¢å• ({}) 个]", count); + return StrUtil.format("åŒæ­¥é€€æ¬¾è®¢å• ({}) 个",count); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/transfer/PayTransferSyncJob.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/transfer/PayTransferSyncJob.java new file mode 100644 index 0000000..420b108 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/job/transfer/PayTransferSyncJob.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.pay.job.transfer; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import com.tashow.cloud.pay.service.transfer.PayTransferService; +import com.xxl.job.core.handler.annotation.XxlJob; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 转账订å•çš„åŒæ­¥ Job + * + * 由于转账订å•çš„è½¬è´¦ç»“æžœï¼Œæœ‰äº›æ¸ é“æ˜¯å¼‚æ­¥é€šçŸ¥è¿›è¡ŒåŒæ­¥çš„,考虑到异步通知å¯èƒ½ä¼šå¤±è´¥ï¼ˆå°æ¦‚率),所以需è¦å®šæ—¶è¿›è¡ŒåŒæ­¥ã€‚ + * + * @author jason + */ +@Component +@Slf4j +public class PayTransferSyncJob { + + @Resource + private PayTransferService transferService; + + @XxlJob("payTransferSyncJob") + @TenantJob // 多租户 + public String execute(String param) { + int count = transferService.syncTransfer(); + log.info("[execute][åŒæ­¥è½¬è´¦è®¢å• ({}) 个]", count); + return StrUtil.format("åŒæ­¥è½¬è´¦è®¢å• ({}) 个",count); + } +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/package-info.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/package-info.java new file mode 100644 index 0000000..e7fbd6d --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/package-info.java @@ -0,0 +1,10 @@ +/** + * pay 模å—,我们放支付业务,æä¾›ä¸šåŠ¡çš„æ”¯ä»˜èƒ½åŠ›ã€‚ + * 例如说:商户ã€åº”ç”¨ã€æ”¯ä»˜ã€é€€æ¬¾ç­‰ç­‰ + * + * 1. Controller URL:以 /pay/ 开头,é¿å…和其它 Module å†²çª + * 2. DataObject 表å:以 pay_ 开头,方便在数æ®åº“中区分 + * + * 注æ„,由于 Pay 模å—å’Œ Trade 模å—,容易é‡å,所以类å都加载 Pay çš„å‰ç¼€~ + */ +package com.tashow.cloud.pay; diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/app/PayAppService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/app/PayAppService.java new file mode 100644 index 0000000..013ffc5 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/app/PayAppService.java @@ -0,0 +1,115 @@ +package com.tashow.cloud.pay.service.app; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppCreateReqVO; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppPageReqVO; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppUpdateReqVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 支付应用 Service æŽ¥å£ + * + * @author 芋艿 + */ +public interface PayAppService { + + /** + * 创建支付应用 + * + * @param createReqVO 创建 + * @return ç¼–å· + */ + Long createApp(@Valid PayAppCreateReqVO createReqVO); + + /** + * 更新支付应用 + * + * @param updateReqVO æ›´æ–° + */ + void updateApp(@Valid PayAppUpdateReqVO updateReqVO); + + /** + * ä¿®æ”¹åº”ç”¨çŠ¶æ€ + * + * @param id åº”ç”¨ç¼–å· + * @param status çŠ¶æ€ + */ + void updateAppStatus(Long id, Integer status); + + /** + * 删除支付应用 + * + * @param id ç¼–å· + */ + void deleteApp(Long id); + + /** + * 获得支付应用 + * + * @param id ç¼–å· + * @return 支付应用 + */ + PayAppDO getApp(Long id); + + /** + * 获得支付应用列表 + * + * @param ids ç¼–å· + * @return 支付应用列表 + */ + List getAppList(Collection ids); + + /** + * 获得支付应用列表 + * + * @return 支付应用列表 + */ + List getAppList(); + + /** + * 获得支付应用分页 + * + * @param pageReqVO 分页查询 + * @return 支付应用分页 + */ + PageResult getAppPage(PayAppPageReqVO pageReqVO); + + /** + * 获得指定编å·çš„商户 Map + * + * @param ids 应用编å·é›†åˆ + * @return 商户 Map + */ + default Map getAppMap(Collection ids) { + List list = getAppList(ids); + return CollectionUtils.convertMap(list, PayAppDO::getId); + } + + /** + * æ”¯ä»˜åº”ç”¨çš„åˆæ³•性 + *

+ * 如果ä¸åˆæ³•,抛出 {@link ServiceException} 业务异常 + * + * @param id åº”ç”¨ç¼–å· + * @return 应用 + */ + PayAppDO validPayApp(Long id); + + /** + * æ”¯ä»˜åº”ç”¨çš„åˆæ³•性 + *

+ * 如果ä¸åˆæ³•,抛出 {@link ServiceException} 业务异常 + * + * @param appKey 应用标识 + * @return 应用 + */ + PayAppDO validPayApp(String appKey); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/app/PayAppServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/app/PayAppServiceImpl.java new file mode 100644 index 0000000..a9d4c27 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/app/PayAppServiceImpl.java @@ -0,0 +1,162 @@ +package com.tashow.cloud.pay.service.app; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppCreateReqVO; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppPageReqVO; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppUpdateReqVO; +import com.tashow.cloud.pay.convert.app.PayAppConvert; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.mysql.app.PayAppMapper; +import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; + +/** + * 支付应用 Service 实现类 + * + * @author aquan + */ +@Service +@Validated +public class PayAppServiceImpl implements PayAppService { + + @Resource + private PayAppMapper appMapper; + + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ–报错 + private PayOrderService orderService; + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ–报错 + private PayRefundService refundService; + + @Override + public Long createApp(PayAppCreateReqVO createReqVO) { + // éªŒè¯ appKey 是å¦é‡å¤ + validateAppKeyUnique(null, createReqVO.getAppKey()); + + // æ’å…¥ + PayAppDO app = PayAppConvert.INSTANCE.convert(createReqVO); + appMapper.insert(app); + // 返回 + return app.getId(); + } + + @Override + public void updateApp(PayAppUpdateReqVO updateReqVO) { + // 校验存在 + validateAppExists(updateReqVO.getId()); + // éªŒè¯ appKey 是å¦é‡å¤ + validateAppKeyUnique(updateReqVO.getId(), updateReqVO.getAppKey()); + + // æ›´æ–° + PayAppDO updateObj = PayAppConvert.INSTANCE.convert(updateReqVO); + appMapper.updateById(updateObj); + } + + void validateAppKeyUnique(Long id, String appKey) { + PayAppDO app = appMapper.selectByAppKey(appKey); + if (app == null) { + return; + } + // 如果 id 为空,说明ä¸ç”¨æ¯”较是å¦ä¸ºç›¸åŒ appKey 的应用 + if (id == null) { + throw exception(APP_KEY_EXISTS); + } + if (!app.getId().equals(id)) { + throw exception(APP_KEY_EXISTS); + } + } + + @Override + public void updateAppStatus(Long id, Integer status) { + // 校验商户存在 + validateAppExists(id); + // æ›´æ–°çŠ¶æ€ + appMapper.updateById(new PayAppDO().setId(id).setStatus(status)); + } + + @Override + public void deleteApp(Long id) { + // 校验存在 + validateAppExists(id); + // æ ¡éªŒå…³è”æ•°æ®æ˜¯å¦å­˜åœ¨ + if (orderService.getOrderCountByAppId(id) > 0) { + throw exception(APP_EXIST_ORDER_CANT_DELETE); + } + if (refundService.getRefundCountByAppId(id) > 0) { + throw exception(APP_EXIST_REFUND_CANT_DELETE); + } + + // 删除 + appMapper.deleteById(id); + } + + private void validateAppExists(Long id) { + if (appMapper.selectById(id) == null) { + throw exception(APP_NOT_FOUND); + } + } + + @Override + public PayAppDO getApp(Long id) { + return appMapper.selectById(id); + } + + @Override + public List getAppList(Collection ids) { + return appMapper.selectBatchIds(ids); + } + + @Override + public List getAppList() { + return appMapper.selectList(); + } + + @Override + public PageResult getAppPage(PayAppPageReqVO pageReqVO) { + return appMapper.selectPage(pageReqVO); + } + + @Override + public PayAppDO validPayApp(Long appId) { + PayAppDO app = appMapper.selectById(appId); + return validatePayApp(app); + } + + @Override + public PayAppDO validPayApp(String appKey) { + PayAppDO app = appMapper.selectByAppKey(appKey); + return validatePayApp(app); + } + + /** + * 校验支付应用实体的有效性:存在 + å¼€å¯ + * + * @param app 待校验的支付应用实体 + * @return 校验通过的支付应用实体 + */ + private PayAppDO validatePayApp(PayAppDO app) { + // 校验是å¦å­˜åœ¨ + if (app == null) { + throw exception(ErrorCodeConstants.APP_NOT_FOUND); + } + // 校验是å¦ç¦ç”¨ + if (CommonStatusEnum.isDisable(app.getStatus())) { + throw exception(ErrorCodeConstants.APP_IS_DISABLE); + } + return app; + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/channel/PayChannelService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/channel/PayChannelService.java new file mode 100644 index 0000000..9b92d48 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/channel/PayChannelService.java @@ -0,0 +1,104 @@ +package com.tashow.cloud.pay.service.channel; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelCreateReqVO; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelUpdateReqVO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * æ”¯ä»˜æ¸ é“ Service æŽ¥å£ + * + * @author aquan + */ +public interface PayChannelService { + + /** + * åˆ›å»ºæ”¯ä»˜æ¸ é“ + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createChannel(@Valid PayChannelCreateReqVO createReqVO); + + /** + * æ›´æ–°æ”¯ä»˜æ¸ é“ + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateChannel(@Valid PayChannelUpdateReqVO updateReqVO); + + /** + * åˆ é™¤æ”¯ä»˜æ¸ é“ + * + * @param id ç¼–å· + */ + void deleteChannel(Long id); + + /** + * èŽ·å¾—æ”¯ä»˜æ¸ é“ + * + * @param id ç¼–å· + * @return æ”¯ä»˜æ¸ é“ + */ + PayChannelDO getChannel(Long id); + + /** + * æ ¹æ®æ”¯ä»˜åº”用 ID 集åˆï¼ŒèŽ·å¾—æ”¯ä»˜æ¸ é“列表 + * + * @param appIds 应用编å·é›†åˆ + * @return 支付渠é“列表 + */ + List getChannelListByAppIds(Collection appIds); + + /** + * æ ¹æ®æ¡ä»¶èŽ·å–æ¸ é“ + * + * @param appId åº”ç”¨ç¼–å· + * @param code 渠é“ç¼–ç  + * @return æ•°é‡ + */ + PayChannelDO getChannelByAppIdAndCode(Long appId, String code); + + /** + * 支付渠é“çš„åˆæ³•性 + * + * 如果ä¸åˆæ³•,抛出 {@link ServiceException} 业务异常 + * + * @param id 渠é“ç¼–å· + * @return 渠é“ä¿¡æ¯ + */ + PayChannelDO validPayChannel(Long id); + + /** + * 支付渠é“çš„åˆæ³•性 + * + * 如果ä¸åˆæ³•,抛出 {@link ServiceException} 业务异常 + * + * @param appId åº”ç”¨ç¼–å· + * @param code æ”¯ä»˜æ¸ é“ + * @return 渠é“ä¿¡æ¯ + */ + PayChannelDO validPayChannel(Long appId, String code); + + /** + * 获得指定应用的开å¯çš„æ¸ é“列表 + * + * @param appId åº”ç”¨ç¼–å· + * @return 渠é“列表 + */ + List getEnableChannelList(Long appId); + + /** + * 获得指定编å·çš„æ”¯ä»˜å®¢æˆ·ç«¯ + * + * @param id ç¼–å· + * @return 支付客户端 + */ + PayClient getPayClient(Long id); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/channel/PayChannelServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/channel/PayChannelServiceImpl.java new file mode 100644 index 0000000..f5e5fe2 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/channel/PayChannelServiceImpl.java @@ -0,0 +1,170 @@ +package com.tashow.cloud.pay.service.channel; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelCreateReqVO; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelUpdateReqVO; +import com.tashow.cloud.pay.convert.channel.PayChannelConvert; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.dal.mysql.channel.PayChannelMapper; +import com.tashow.cloud.pay.framework.pay.core.WalletPayClient; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import jakarta.validation.Validator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; + +/** + * æ”¯ä»˜æ¸ é“ Service 实现类 + * + * @author aquan + */ +@Service +@Slf4j +@Validated +public class PayChannelServiceImpl implements PayChannelService { + + @Resource + private PayClientFactory payClientFactory; + + @Resource + private PayChannelMapper payChannelMapper; + + @Resource + private Validator validator; + + /** + * åˆå§‹åŒ–,为了注册钱包 + */ + @PostConstruct + public void init() { + payClientFactory.registerPayClientClass(PayChannelEnum.WALLET, WalletPayClient.class); + } + + @Override + public Long createChannel(PayChannelCreateReqVO reqVO) { + // æ–­è¨€æ˜¯å¦æœ‰é‡å¤çš„ + PayChannelDO dbChannel = getChannelByAppIdAndCode(reqVO.getAppId(), reqVO.getCode()); + if (dbChannel != null) { + throw exception(CHANNEL_EXIST_SAME_CHANNEL_ERROR); + } + + // æ–°å¢žæ¸ é“ + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO) + .setConfig(parseConfig(reqVO.getCode(), reqVO.getConfig())); + payChannelMapper.insert(channel); + return channel.getId(); + } + + @Override + public void updateChannel(PayChannelUpdateReqVO updateReqVO) { + // 校验存在 + PayChannelDO dbChannel = validateChannelExists(updateReqVO.getId()); + + // æ›´æ–° + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO) + .setConfig(parseConfig(dbChannel.getCode(), updateReqVO.getConfig())); + payChannelMapper.updateById(channel); + } + + /** + * è§£æžå¹¶æ ¡éªŒé…ç½® + * + * @param code 渠é“ç¼–ç  + * @param configStr é…ç½® + * @return 支付é…ç½® + */ + private PayClientConfig parseConfig(String code, String configStr) { + // è§£æžé…ç½® + Class payClass = PayChannelEnum.getByCode(code).getConfigClass(); + if (ObjectUtil.isNull(payClass)) { + throw exception(CHANNEL_NOT_FOUND); + } + PayClientConfig config = JsonUtils.parseObject2(configStr, payClass); + Assert.notNull(config); + + // 验è¯å‚æ•° + config.validate(validator); + return config; + } + + @Override + public void deleteChannel(Long id) { + // 校验存在 + validateChannelExists(id); + + // 删除 + payChannelMapper.deleteById(id); + } + + private PayChannelDO validateChannelExists(Long id) { + PayChannelDO channel = payChannelMapper.selectById(id); + if (channel == null) { + throw exception(CHANNEL_NOT_FOUND); + } + return channel; + } + + @Override + public PayChannelDO getChannel(Long id) { + return payChannelMapper.selectById(id); + } + + @Override + public List getChannelListByAppIds(Collection appIds) { + return payChannelMapper.selectListByAppIds(appIds); + } + + @Override + public PayChannelDO getChannelByAppIdAndCode(Long appId, String code) { + return payChannelMapper.selectByAppIdAndCode(appId, code); + } + + @Override + public PayChannelDO validPayChannel(Long id) { + PayChannelDO channel = payChannelMapper.selectById(id); + validPayChannel(channel); + return channel; + } + + @Override + public PayChannelDO validPayChannel(Long appId, String code) { + PayChannelDO channel = payChannelMapper.selectByAppIdAndCode(appId, code); + validPayChannel(channel); + return channel; + } + + private void validPayChannel(PayChannelDO channel) { + if (channel == null) { + throw exception(CHANNEL_NOT_FOUND); + } + if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) { + throw exception(CHANNEL_IS_DISABLE); + } + } + + @Override + public List getEnableChannelList(Long appId) { + return payChannelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus()); + } + + @Override + public PayClient getPayClient(Long id) { + PayChannelDO channel = validPayChannel(id); + return payClientFactory.createOrUpdatePayClient(id, channel.getCode(), channel.getConfig()); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoOrderService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoOrderService.java new file mode 100644 index 0000000..8648e34 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoOrderService.java @@ -0,0 +1,65 @@ +package com.tashow.cloud.pay.service.demo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoOrderDO; +import jakarta.validation.Valid; + +/** + * ç¤ºä¾‹è®¢å• Service æŽ¥å£ + * + * @author èŠ‹é“æºç  + */ +public interface PayDemoOrderService { + + /** + * åˆ›å»ºç¤ºä¾‹è®¢å• + * + * @param userId ç”¨æˆ·ç¼–å· + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createDemoOrder(Long userId, @Valid PayDemoOrderCreateReqVO createReqVO); + + /** + * èŽ·å¾—ç¤ºä¾‹è®¢å• + * + * @param id ç¼–å· + * @return ç¤ºä¾‹è®¢å• + */ + PayDemoOrderDO getDemoOrder(Long id); + + /** + * 获得示例订å•分页 + * + * @param pageReqVO 分页查询 + * @return 示例订å•分页 + */ + PageResult getDemoOrderPage(PageParam pageReqVO); + + /** + * 更新示例订å•为已支付 + * + * @param id ç¼–å· + * @param payOrderId 支付订å•å· + */ + void updateDemoOrderPaid(Long id, Long payOrderId); + + /** + * å‘起示例订å•的退款 + * + * @param id ç¼–å· + * @param userIp ç”¨æˆ·ç¼–å· + */ + void refundDemoOrder(Long id, String userIp); + + /** + * 更新示例订å•为已退款 + * + * @param id ç¼–å· + * @param payRefundId 退款订å•å· + */ + void updateDemoOrderRefunded(Long id, Long payRefundId); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoOrderServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoOrderServiceImpl.java new file mode 100644 index 0000000..4d0aff6 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoOrderServiceImpl.java @@ -0,0 +1,264 @@ +package com.tashow.cloud.pay.service.demo; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; +import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; +import com.tashow.cloud.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoOrderDO; +import com.tashow.cloud.pay.dal.mysql.demo.PayDemoOrderMapper; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static cn.hutool.core.util.ObjectUtil.notEqual; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; + +/** + * ç¤ºä¾‹è®¢å• Service 实现类 + * + * @author èŠ‹é“æºç  + */ +@Service +@Validated +@Slf4j +public class PayDemoOrderServiceImpl implements PayDemoOrderService { + + /** + * 接入的支付应用标识 + * + * 从 [æ”¯ä»˜ç®¡ç† -> 应用信æ¯] 里添加 + */ + private static final String PAY_APP_KEY = "demo"; + + /** + * 商å“ä¿¡æ¯ Map + * + * key:商å“ç¼–å· + * value:[商å“åã€å•†å“ä»·æ ¼] + */ + private final Map spuNames = new HashMap<>(); + + @Resource + private PayOrderApi payOrderApi; + @Resource + private PayRefundApi payRefundApi; + + @Resource + private PayDemoOrderMapper payDemoOrderMapper; + + public PayDemoOrderServiceImpl() { + spuNames.put(1L, new Object[]{"åŽä¸ºæ‰‹æœº", 1}); + spuNames.put(2L, new Object[]{"å°ç±³ç”µè§†", 10}); + spuNames.put(3L, new Object[]{"苹果手表", 100}); + spuNames.put(4L, new Object[]{"åŽç¡•笔记本", 1000}); + spuNames.put(5L, new Object[]{"è”šæ¥æ±½è½¦", 200000}); + } + + @Override + public Long createDemoOrder(Long userId, PayDemoOrderCreateReqVO createReqVO) { + // 1.1 èŽ·å¾—å•†å“ + Object[] spu = spuNames.get(createReqVO.getSpuId()); + Assert.notNull(spu, "商å“({}) ä¸å­˜åœ¨", createReqVO.getSpuId()); + String spuName = (String) spu[0]; + Integer price = (Integer) spu[1]; + // 1.2 æ’å…¥ demo è®¢å• + PayDemoOrderDO demoOrder = new PayDemoOrderDO().setUserId(userId) + .setSpuId(createReqVO.getSpuId()).setSpuName(spuName) + .setPrice(price).setPayStatus(false).setRefundPrice(0); + payDemoOrderMapper.insert(demoOrder); + + // 2.1 åˆ›å»ºæ”¯ä»˜å• + Long payOrderId = payOrderApi.createOrder(new PayOrderCreateReqDTO() + .setAppKey(PAY_APP_KEY).setUserIp(getClientIP()) // 支付应用 + .setMerchantOrderId(demoOrder.getId().toString()) // 业务的订å•ç¼–å· + .setSubject(spuName).setBody("").setPrice(price) // ä»·æ ¼ä¿¡æ¯ + .setExpireTime(addTime(Duration.ofHours(2L)))).getCheckedData(); // 支付的过期时间 + // 2.2 更新支付å•到 demo è®¢å• + payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(demoOrder.getId()) + .setPayOrderId(payOrderId)); + // 返回 + return demoOrder.getId(); + } + + @Override + public PayDemoOrderDO getDemoOrder(Long id) { + return payDemoOrderMapper.selectById(id); + } + + @Override + public PageResult getDemoOrderPage(PageParam pageReqVO) { + return payDemoOrderMapper.selectPage(pageReqVO); + } + + @Override + public void updateDemoOrderPaid(Long id, Long payOrderId) { + // 1.1 æ ¡éªŒè®¢å•æ˜¯å¦å­˜åœ¨ + PayDemoOrderDO order = payDemoOrderMapper.selectById(id); + if (order == null) { + log.error("[updateDemoOrderPaid][order({}) payOrder({}) ä¸å­˜åœ¨è®¢å•,请进行处ç†ï¼]", id, payOrderId); + throw exception(DEMO_ORDER_NOT_FOUND); + } + // 1.2 校验订å•已支付 + if (order.getPayStatus()) { + // 特殊:如果订å•已支付,且支付å•å·ç›¸åŒï¼Œç›´æŽ¥è¿”回,说明é‡å¤å›žè°ƒ + if (ObjectUtil.equals(order.getPayOrderId(), payOrderId)) { + log.warn("[updateDemoOrderPaid][order({}) 已支付,且支付å•å·ç›¸åŒ({}),直接返回]", order, payOrderId); + return; + } + // 异常:支付å•å·ä¸åŒï¼Œè¯´æ˜Žæ”¯ä»˜å•å·é”™è¯¯ + log.error("[updateDemoOrderPaid][order({}) 支付å•ä¸åŒ¹é…({}),请进行处ç†ï¼order æ•°æ®æ˜¯ï¼š{}]", + order, payOrderId, toJsonString(order)); + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR); + } + + // 2. 校验支付订å•çš„åˆæ³•性 + PayOrderRespDTO payOrder = validatePayOrderPaid(order, payOrderId); + + // 3. æ›´æ–° PayDemoOrderDO 状æ€ä¸ºå·²æ”¯ä»˜ + int updateCount = payDemoOrderMapper.updateByIdAndPayed(id, false, + new PayDemoOrderDO().setPayStatus(true).setPayTime(LocalDateTime.now()) + .setPayChannelCode(payOrder.getChannelCode())); + if (updateCount == 0) { + throw exception(DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); + } + } + + /** + * 校验支付订å•çš„åˆæ³•性 + * + * @param order äº¤æ˜“è®¢å• + * @param payOrderId 支付订å•ç¼–å· + * @return æ”¯ä»˜è®¢å• + */ + private PayOrderRespDTO validatePayOrderPaid(PayDemoOrderDO order, Long payOrderId) { + // 1. æ ¡éªŒæ”¯ä»˜å•æ˜¯å¦å­˜åœ¨ + PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId).getCheckedData(); + if (payOrder == null) { + log.error("[validatePayOrderPaid][order({}) payOrder({}) ä¸å­˜åœ¨ï¼Œè¯·è¿›è¡Œå¤„ç†ï¼]", order.getId(), payOrderId); + throw exception(PAY_ORDER_NOT_FOUND); + } + // 2.1 校验支付å•已支付 + if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) { + log.error("[validatePayOrderPaid][order({}) payOrder({}) 未支付,请进行处ç†ï¼payOrder æ•°æ®æ˜¯ï¼š{}]", + order.getId(), payOrderId, toJsonString(payOrder)); + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS); + } + // 2.1 校验支付金é¢ä¸€è‡´ + if (notEqual(payOrder.getPrice(), order.getPrice())) { + log.error("[validatePayOrderPaid][order({}) payOrder({}) 支付金é¢ä¸åŒ¹é…,请进行处ç†ï¼order æ•°æ®æ˜¯ï¼š{},payOrder æ•°æ®æ˜¯ï¼š{}]", + order.getId(), payOrderId, toJsonString(order), toJsonString(payOrder)); + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH); + } + // 2.2 校验支付订å•匹é…(二次) + if (notEqual(payOrder.getMerchantOrderId(), order.getId().toString())) { + log.error("[validatePayOrderPaid][order({}) 支付å•ä¸åŒ¹é…({}),请进行处ç†ï¼payOrder æ•°æ®æ˜¯ï¼š{}]", + order.getId(), payOrderId, toJsonString(payOrder)); + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR); + } + return payOrder; + } + + @Override + public void refundDemoOrder(Long id, String userIp) { + // 1. æ ¡éªŒè®¢å•æ˜¯å¦å¯ä»¥é€€æ¬¾ + PayDemoOrderDO order = validateDemoOrderCanRefund(id); + + // 2.1 生æˆé€€æ¬¾å•å· + // 一般æ¥è¯´ï¼Œç”¨æˆ·å‘起退款的时候,都会å•独æ’入一个售åŽç»´æƒè¡¨ï¼Œç„¶åŽä½¿ç”¨è¯¥è¡¨çš„ id 作为 refundId + // 这里我们是个简å•çš„ demo,所以没有售åŽç»´æƒè¡¨ï¼Œç›´æŽ¥ä½¿ç”¨è®¢å• id + "-refund" æ¥æ¼”示 + String refundId = order.getId() + "-refund"; + // 2.2 åˆ›å»ºé€€æ¬¾å• + Long payRefundId = payRefundApi.createRefund(new PayRefundCreateReqDTO() + .setAppKey(PAY_APP_KEY).setUserIp(getClientIP()) // 支付应用 + .setMerchantOrderId(String.valueOf(order.getId())) // 支付å•å· + .setMerchantRefundId(refundId) + .setReason("想退钱").setPrice(order.getPrice())).getCheckedData();// ä»·æ ¼ä¿¡æ¯ + // 2.3 更新退款å•到 demo è®¢å• + payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(id) + .setPayRefundId(payRefundId).setRefundPrice(order.getPrice())); + } + + private PayDemoOrderDO validateDemoOrderCanRefund(Long id) { + // æ ¡éªŒè®¢å•æ˜¯å¦å­˜åœ¨ + PayDemoOrderDO order = payDemoOrderMapper.selectById(id); + if (order == null) { + throw exception(DEMO_ORDER_NOT_FOUND); + } + // æ ¡éªŒè®¢å•æ˜¯å¦æ”¯ä»˜ + if (!order.getPayStatus()) { + throw exception(DEMO_ORDER_REFUND_FAIL_NOT_PAID); + } + // æ ¡éªŒè®¢å•æ˜¯å¦å·²é€€æ¬¾ + if (order.getPayRefundId() != null) { + throw exception(DEMO_ORDER_REFUND_FAIL_REFUNDED); + } + return order; + } + + @Override + public void updateDemoOrderRefunded(Long id, Long payRefundId) { + // 1. 校验并获得退款订å•(å¯é€€æ¬¾ï¼‰ + PayRefundRespDTO payRefund = validateDemoOrderCanRefunded(id, payRefundId); + // 2.2 更新退款å•到 demo è®¢å• + payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(id) + .setRefundTime(payRefund.getSuccessTime())); + } + + private PayRefundRespDTO validateDemoOrderCanRefunded(Long id, Long payRefundId) { + // 1.1 æ ¡éªŒç¤ºä¾‹è®¢å• + PayDemoOrderDO order = payDemoOrderMapper.selectById(id); + if (order == null) { + throw exception(DEMO_ORDER_NOT_FOUND); + } + // 1.2 校验退款订å•åŒ¹é… + if (Objects.equals(order.getPayRefundId(), payRefundId)) { + log.error("[validateDemoOrderCanRefunded][order({}) 退款å•ä¸åŒ¹é…({}),请进行处ç†ï¼order æ•°æ®æ˜¯ï¼š{}]", + id, payRefundId, toJsonString(order)); + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR); + } + + // 2.1 æ ¡éªŒé€€æ¬¾è®¢å• + PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId).getCheckedData(); + if (payRefund == null) { + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND); + } + // 2.2 + if (!PayRefundStatusEnum.isSuccess(payRefund.getStatus())) { + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS); + } + // 2.3 校验退款金é¢ä¸€è‡´ + if (notEqual(payRefund.getRefundPrice(), order.getPrice())) { + log.error("[validateDemoOrderCanRefunded][order({}) payRefund({}) 退款金é¢ä¸åŒ¹é…,请进行处ç†ï¼order æ•°æ®æ˜¯ï¼š{},payRefund æ•°æ®æ˜¯ï¼š{}]", + id, payRefundId, toJsonString(order), toJsonString(payRefund)); + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH); + } + // 2.4 校验退款订å•匹é…(二次) + if (notEqual(payRefund.getMerchantOrderId(), id.toString())) { + log.error("[validateDemoOrderCanRefunded][order({}) 退款å•ä¸åŒ¹é…({}),请进行处ç†ï¼payRefund æ•°æ®æ˜¯ï¼š{}]", + id, payRefundId, toJsonString(payRefund)); + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR); + } + return payRefund; + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoTransferService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoTransferService.java new file mode 100644 index 0000000..91bf40a --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoTransferService.java @@ -0,0 +1,38 @@ +package com.tashow.cloud.pay.service.demo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoTransferDO; +import jakarta.validation.Valid; + +/** + * 示例转账业务 Service æŽ¥å£ + * + * @author jason + */ +public interface PayDemoTransferService { + + /** + * åˆ›å»ºè½¬è´¦ä¸šåŠ¡ç¤ºä¾‹è®¢å• + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createDemoTransfer(@Valid PayDemoTransferCreateReqVO createReqVO); + + /** + * 获得转账业务示例订å•分页 + * + * @param pageVO åˆ†é¡µæŸ¥è¯¢å‚æ•° + */ + PageResult getDemoTransferPage(PageParam pageVO); + + /** + * 更新转账业务示例订å•çš„è½¬è´¦çŠ¶æ€ + * + * @param id ç¼–å· + * @param payTransferId 转账å•ç¼–å· + */ + void updateDemoTransferStatus(Long id, Long payTransferId); +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoTransferServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoTransferServiceImpl.java new file mode 100644 index 0000000..e075533 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/demo/PayDemoTransferServiceImpl.java @@ -0,0 +1,99 @@ +package com.tashow.cloud.pay.service.demo; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; +import com.tashow.cloud.pay.convert.demo.PayDemoTransferConvert; +import com.tashow.cloud.pay.dal.dataobject.demo.PayDemoTransferDO; +import com.tashow.cloud.pay.dal.dataobject.transfer.PayTransferDO; +import com.tashow.cloud.pay.dal.mysql.demo.PayDemoTransferMapper; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; +import com.tashow.cloud.pay.service.transfer.PayTransferService; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import jakarta.validation.Validator; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.WAITING; + +/** + * 示例转账业务 Service 实现类 + * + * @author jason + */ +@Service +@Validated +public class PayDemoTransferServiceImpl implements PayDemoTransferService { + + /** + * æŽ¥å…¥çš„å®žåŠ›åº”ç”¨ç¼–å· + + * 从 [æ”¯ä»˜ç®¡ç† -> 应用信æ¯] 里添加 + */ + private static final Long TRANSFER_APP_ID = 8L; + @Resource + private PayDemoTransferMapper demoTransferMapper; + @Resource + private PayTransferService payTransferService; + @Resource + private Validator validator; + + @Override + public Long createDemoTransfer(@Valid PayDemoTransferCreateReqVO vo) { + // 1 æ ¡éªŒå‚æ•° + vo.validate(validator); + // 2 ä¿å­˜ç¤ºä¾‹è½¬è´¦ä¸šåŠ¡è¡¨ + PayDemoTransferDO demoTransfer = PayDemoTransferConvert.INSTANCE.convert(vo) + .setAppId(TRANSFER_APP_ID).setTransferStatus(WAITING.getStatus()); + demoTransferMapper.insert(demoTransfer); + return demoTransfer.getId(); + } + + @Override + public PageResult getDemoTransferPage(PageParam pageVO) { + return demoTransferMapper.selectPage(pageVO); + } + + @Override + public void updateDemoTransferStatus(Long id, Long payTransferId) { + PayTransferDO payTransfer = validateDemoTransferStatusCanUpdate(id, payTransferId); + // 更新示例订å•çŠ¶æ€ + if (payTransfer != null) { + demoTransferMapper.updateById(new PayDemoTransferDO().setId(id) + .setPayTransferId(payTransferId) + .setPayChannelCode(payTransfer.getChannelCode()) + .setTransferStatus(payTransfer.getStatus()) + .setTransferTime(payTransfer.getSuccessTime())); + } + } + + private PayTransferDO validateDemoTransferStatusCanUpdate(Long id, Long payTransferId) { + PayDemoTransferDO demoTransfer = demoTransferMapper.selectById(id); + if (demoTransfer == null) { + throw exception(DEMO_TRANSFER_NOT_FOUND); + } + if (PayTransferStatusEnum.isSuccess(demoTransfer.getTransferStatus()) + || PayTransferStatusEnum.isClosed(demoTransfer.getTransferStatus())) { + // 无需更新返回 null + return null; + } + PayTransferDO transfer = payTransferService.getTransfer(payTransferId); + if (transfer == null) { + throw exception(PAY_TRANSFER_NOT_FOUND); + } + if (!Objects.equals(demoTransfer.getPrice(), transfer.getPrice())) { + throw exception(DEMO_TRANSFER_FAIL_PRICE_NOT_MATCH); + } + if (ObjectUtil.notEqual(transfer.getMerchantTransferId(), id.toString())) { + throw exception(DEMO_TRANSFER_FAIL_TRANSFER_ID_ERROR); + } + // TODO æ ¡éªŒè´¦å· + return transfer; + } +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/notify/PayNotifyService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/notify/PayNotifyService.java new file mode 100644 index 0000000..f25f13a --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/notify/PayNotifyService.java @@ -0,0 +1,57 @@ +package com.tashow.cloud.pay.service.notify; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyLogDO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyTaskDO; + +import java.util.List; + +/** + * 回调通知 Service æŽ¥å£ + * + * @author èŠ‹é“æºç  + */ +public interface PayNotifyService { + + /** + * 创建回调通知任务 + * + * @param type 类型 + * @param dataId æ•°æ®ç¼–å· + */ + void createPayNotifyTask(Integer type, Long dataId); + + /** + * 执行回调通知 + * + * 注æ„,该方法æä¾›ç»™å®šæ—¶ä»»åŠ¡è°ƒç”¨ã€‚ç›®å‰æ˜¯ yudao-server 进行调用 + * @return é€šçŸ¥æ•°é‡ + */ + int executeNotify() throws InterruptedException; + + /** + * 获得回调通知 + * + * @param id ç¼–å· + * @return 回调通知 + */ + PayNotifyTaskDO getNotifyTask(Long id); + + /** + * 获得回调通知分页 + * + * @param pageReqVO 分页查询 + * @return 回调通知分页 + */ + PageResult getNotifyTaskPage(PayNotifyTaskPageReqVO pageReqVO); + + /** + * 获得回调日志列表 + * + * @param taskId ä»»åŠ¡ç¼–å· + * @return 日志列表 + */ + List getNotifyLogList(Long taskId); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/notify/PayNotifyServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/notify/PayNotifyServiceImpl.java new file mode 100644 index 0000000..26d3495 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/notify/PayNotifyServiceImpl.java @@ -0,0 +1,308 @@ +package com.tashow.cloud.pay.service.notify; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayTransferNotifyReqDTO; +import com.tashow.cloud.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyLogDO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyTaskDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import com.tashow.cloud.pay.dal.dataobject.transfer.PayTransferDO; +import com.tashow.cloud.pay.dal.mysql.notify.PayNotifyLogMapper; +import com.tashow.cloud.pay.dal.mysql.notify.PayNotifyTaskMapper; +import com.tashow.cloud.pay.dal.redis.notify.PayNotifyLockRedisDAO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import com.tashow.cloud.pay.service.transfer.PayTransferService; +import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; +import static com.tashow.cloud.pay.framework.job.config.PayJobConfiguration.NOTIFY_THREAD_POOL_TASK_EXECUTOR; + +/** + * 支付通知 Core Service 实现类 + * + * @author èŠ‹é“æºç  + */ +@Service +@Valid +@Slf4j +public class PayNotifyServiceImpl implements PayNotifyService { + + /** + * 通知超时时间,å•ä½ï¼šç§’ + */ + public static final int NOTIFY_TIMEOUT = 120; + /** + * {@link #NOTIFY_TIMEOUT} 的毫秒 + */ + public static final long NOTIFY_TIMEOUT_MILLIS = 120 * DateUtils.SECOND_MILLIS; + + @Resource + @Lazy // 循环ä¾èµ–,é¿å…报错 + private PayOrderService orderService; + @Resource + @Lazy // 循环ä¾èµ–,é¿å…报错 + private PayRefundService refundService; + @Resource + @Lazy // 循环ä¾èµ–,é¿å…报错 + private PayTransferService transferService; + + @Resource + private PayNotifyTaskMapper notifyTaskMapper; + @Resource + private PayNotifyLogMapper notifyLogMapper; + + @Resource(name = NOTIFY_THREAD_POOL_TASK_EXECUTOR) + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + + @Resource + private PayNotifyLockRedisDAO notifyLockCoreRedisDAO; + + @Override + @Transactional(rollbackFor = Exception.class) + public void createPayNotifyTask(Integer type, Long dataId) { + PayNotifyTaskDO task = new PayNotifyTaskDO().setType(type).setDataId(dataId); + task.setStatus(PayNotifyStatusEnum.WAITING.getStatus()).setNextNotifyTime(LocalDateTime.now()) + .setNotifyTimes(0).setMaxNotifyTimes(PayNotifyTaskDO.NOTIFY_FREQUENCY.length + 1); + // 补充 appId + notifyUrl 字段 + if (Objects.equals(task.getType(), PayNotifyTypeEnum.ORDER.getType())) { + PayOrderDO order = orderService.getOrder(task.getDataId()); // ä¸è¿›è¡Œéžç©ºåˆ¤æ–­ï¼Œæœ‰é—®é¢˜ç›´æŽ¥å¼‚常 + task.setAppId(order.getAppId()). + setMerchantOrderId(order.getMerchantOrderId()).setNotifyUrl(order.getNotifyUrl()); + } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { + PayRefundDO refundDO = refundService.getRefund(task.getDataId()); + task.setAppId(refundDO.getAppId()) + .setMerchantOrderId(refundDO.getMerchantOrderId()).setNotifyUrl(refundDO.getNotifyUrl()); + } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.TRANSFER.getType())) { + PayTransferDO transfer = transferService.getTransfer(task.getDataId()); + task.setAppId(transfer.getAppId()).setMerchantTransferId(transfer.getMerchantTransferId()) + .setNotifyUrl(transfer.getNotifyUrl()); + } + + // 执行æ’å…¥ + notifyTaskMapper.insert(task); + + // 必须在事务æäº¤åŽï¼Œåœ¨å‘起任务,å¦åˆ™ PayNotifyTaskDO 还没入库,就æå‰å›žè°ƒæŽ¥å…¥çš„业务 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + executeNotify(task); + } + }); + } + + @Override + public int executeNotify() throws InterruptedException { + // 获得需è¦é€šçŸ¥çš„任务 + List tasks = notifyTaskMapper.selectListByNotify(); + if (CollUtil.isEmpty(tasks)) { + return 0; + } + + // é历,é€ä¸ªé€šçŸ¥ + CountDownLatch latch = new CountDownLatch(tasks.size()); + tasks.forEach(task -> threadPoolTaskExecutor.execute(() -> { + try { + executeNotify(task); + } finally { + latch.countDown(); + } + })); + // ç­‰å¾…å®Œæˆ + awaitExecuteNotify(latch); + // 返回执行完æˆçš„任务数(æˆåŠŸ + 失败) + return tasks.size(); + } + + /** + * ç­‰å¾…å…¨éƒ¨æ”¯ä»˜é€šçŸ¥çš„å®Œæˆ + * æ¯ 1 秒会打å°ä¸€æ¬¡å‰©ä½™ä»»åŠ¡æ•°é‡ + * + * @param latch Latch + * @throws InterruptedException 如果被打断 + */ + private void awaitExecuteNotify(CountDownLatch latch) throws InterruptedException { + long size = latch.getCount(); + for (int i = 0; i < NOTIFY_TIMEOUT; i++) { + if (latch.await(1L, TimeUnit.SECONDS)) { + return; + } + log.info("[awaitExecuteNotify][任务处ç†ä¸­ï¼Œ 总任务数({}) 剩余任务数({})]", size, latch.getCount()); + } + log.error("[awaitExecuteNotify][任务未处ç†å®Œï¼Œæ€»ä»»åŠ¡æ•°({}) 剩余任务数({})]", size, latch.getCount()); + } + + /** + * åŒæ­¥æ‰§è¡Œå•个支付通知 + * + * @param task 通知任务 + */ + public void executeNotify(PayNotifyTaskDO task) { + // 分布å¼é”,é¿å…å¹¶å‘问题 + notifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> { + // 校验,当å‰ä»»åŠ¡æ˜¯å¦å·²ç»è¢«é€šçŸ¥è¿‡ + // 虽然已ç»é€šè¿‡åˆ†å¸ƒå¼åŠ é”,但是å¯èƒ½åŒæ—¶æ»¡è¶³é€šçŸ¥çš„æ¡ä»¶ï¼Œç„¶åŽéƒ½åŽ»èŽ·å¾—é”。此时,第一个执行完åŽï¼Œç¬¬äºŒä¸ªè¿˜æ˜¯èƒ½æ‹¿åˆ°é”,然åŽä¼šå†æ‰§è¡Œä¸€æ¬¡ã€‚ + // 因此,此处我们通过第 notifyTimes 通知次数是å¦åŒ¹é…æ¥åˆ¤æ–­ + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); + if (ObjectUtil.notEqual(task.getNotifyTimes(), dbTask.getNotifyTimes())) { + log.warn("[executeNotifySync][task({}) ä»»åŠ¡è¢«å¿½ç•¥ï¼ŒåŽŸå› æ˜¯å®ƒçš„é€šçŸ¥ä¸æ˜¯ç¬¬ ({}) 次,å¯èƒ½æ˜¯å› ä¸ºå¹¶å‘执行了]", + JsonUtils.toJsonString(task), dbTask.getNotifyTimes()); + return; + } + + // 执行通知 + getSelf().executeNotify0(dbTask); + }); + } + + @Transactional(rollbackFor = Exception.class) + public void executeNotify0(PayNotifyTaskDO task) { + // å‘起回调 + CommonResult invokeResult = null; + Throwable invokeException = null; + try { + invokeResult = executeNotifyInvoke(task); + } catch (Throwable e) { + invokeException = e; + } + + // 处ç†ç»“æžœ + Integer newStatus = processNotifyResult(task, invokeResult, invokeException); + + // 记录 PayNotifyLog 日志 + String response = invokeException != null ? ExceptionUtil.getRootCauseMessage(invokeException) : + JsonUtils.toJsonString(invokeResult); + notifyLogMapper.insert(PayNotifyLogDO.builder().taskId(task.getId()) + .notifyTimes(task.getNotifyTimes() + 1).status(newStatus).response(response).build()); + } + + /** + * 执行å•个支付任务的 HTTP 调用 + * + * @param task 通知任务 + * @return HTTP å“应 + */ + private CommonResult executeNotifyInvoke(PayNotifyTaskDO task) { + // 拼接 body 傿•° + Object request; + if (Objects.equals(task.getType(), PayNotifyTypeEnum.ORDER.getType())) { + request = PayOrderNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId()) + .payOrderId(task.getDataId()).build(); + } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { + request = PayRefundNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId()) + .payRefundId(task.getDataId()).build(); + } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.TRANSFER.getType())) { + request = new PayTransferNotifyReqDTO().setMerchantTransferId(task.getMerchantTransferId()) + .setPayTransferId(task.getDataId()); + } else { + throw new RuntimeException("未知的通知任务类型:" + JsonUtils.toJsonString(task)); + } + // 拼接 header 傿•° + Map headers = new HashMap<>(); + TenantUtils.addTenantHeader(headers, task.getTenantId()); + + // å‘起请求 + try (HttpResponse response = HttpUtil.createPost(task.getNotifyUrl()) + .body(JsonUtils.toJsonString(request)).addHeaders(headers) + .timeout((int) NOTIFY_TIMEOUT_MILLIS).execute()) { + // è§£æžç»“æžœ + return JsonUtils.parseObject(response.body(), CommonResult.class); + } + } + + /** + * 处ç†å¹¶æ›´æ–°é€šçŸ¥ç»“æžœ + * + * @param task 通知任务 + * @param invokeResult 通知结果 + * @param invokeException 通知异常 + * @return æœ€ç»ˆä»»åŠ¡çš„çŠ¶æ€ + */ + @VisibleForTesting + Integer processNotifyResult(PayNotifyTaskDO task, CommonResult invokeResult, Throwable invokeException) { + // 设置通用的更新 PayNotifyTaskDO 的字段 + PayNotifyTaskDO updateTask = new PayNotifyTaskDO() + .setId(task.getId()) + .setLastExecuteTime(LocalDateTime.now()) + .setNotifyTimes(task.getNotifyTimes() + 1); + + // 情况一:调用æˆåŠŸ + if (invokeResult != null && invokeResult.isSuccess()) { + updateTask.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()); + notifyTaskMapper.updateById(updateTask); + return updateTask.getStatus(); + } + + // 情况二:调用失败ã€è°ƒç”¨å¼‚常 + // 2.1 超过最大回调次数 + if (updateTask.getNotifyTimes() >= PayNotifyTaskDO.NOTIFY_FREQUENCY.length) { + updateTask.setStatus(PayNotifyStatusEnum.FAILURE.getStatus()); + notifyTaskMapper.updateById(updateTask); + return updateTask.getStatus(); + } + // 2.2 未超过最大回调次数 + updateTask.setNextNotifyTime(addTime(Duration.ofSeconds(PayNotifyTaskDO.NOTIFY_FREQUENCY[updateTask.getNotifyTimes()]))); + updateTask.setStatus(invokeException != null ? PayNotifyStatusEnum.REQUEST_FAILURE.getStatus() + : PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus()); + notifyTaskMapper.updateById(updateTask); + return updateTask.getStatus(); + } + + @Override + public PayNotifyTaskDO getNotifyTask(Long id) { + return notifyTaskMapper.selectById(id); + } + + @Override + public PageResult getNotifyTaskPage(PayNotifyTaskPageReqVO pageReqVO) { + return notifyTaskMapper.selectPage(pageReqVO); + } + + @Override + public List getNotifyLogList(Long taskId) { + return notifyLogMapper.selectListByTaskId(taskId); + } + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private PayNotifyServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/order/PayOrderService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/order/PayOrderService.java new file mode 100644 index 0000000..b4d2085 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/order/PayOrderService.java @@ -0,0 +1,159 @@ +package com.tashow.cloud.pay.service.order; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderExportReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderPageReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderSubmitReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderSubmitRespVO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderExtensionDO; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; + +/** + * æ”¯ä»˜è®¢å• Service æŽ¥å£ + * + * @author aquan + */ +public interface PayOrderService { + + /** + * èŽ·å¾—æ”¯ä»˜è®¢å• + * + * @param id ç¼–å· + * @return æ”¯ä»˜è®¢å• + */ + PayOrderDO getOrder(Long id); + + /** + * èŽ·å¾—æ”¯ä»˜è®¢å• + * + * @param appId åº”ç”¨ç¼–å· + * @param merchantOrderId 商户订å•ç¼–å· + * @return æ”¯ä»˜è®¢å• + */ + PayOrderDO getOrder(Long appId, String merchantOrderId); + + /** + * 获得支付订å•列表 + * + * @param ids ç¼–å·æ•°ç»„ + * @return 支付订å•列表 + */ + List getOrderList(Collection ids); + + /** + * èŽ·å¾—æŒ‡å®šåº”ç”¨çš„è®¢å•æ•°é‡ + * + * @param appId åº”ç”¨ç¼–å· + * @return è®¢å•æ•°é‡ + */ + Long getOrderCountByAppId(Long appId); + + /** + * 获得支付订å•分页 + * + * @param pageReqVO 分页查询 + * @return 支付订å•分页 + */ + PageResult getOrderPage(PayOrderPageReqVO pageReqVO); + + /** + * 获得支付订å•列表, 用于 Excel 导出 + * + * @param exportReqVO 查询æ¡ä»¶ + * @return 支付订å•列表 + */ + List getOrderList(PayOrderExportReqVO exportReqVO); + + /** + * åˆ›å»ºæ”¯ä»˜å• + * + * @param reqDTO 创建请求 + * @return 支付å•ç¼–å· + */ + Long createOrder(@Valid PayOrderCreateReqDTO reqDTO); + + /** + * æäº¤æ”¯ä»˜ + * 此时,会å‘起支付渠é“的调用 + * + * @param reqVO æäº¤è¯·æ±‚ + * @param userIp æäº¤ IP + * @return æäº¤ç»“æžœ + */ + PayOrderSubmitRespVO submitOrder(@Valid PayOrderSubmitReqVO reqVO, + @NotEmpty(message = "æäº¤ IP ä¸èƒ½ä¸ºç©º") String userIp); + + /** + * é€šçŸ¥æ”¯ä»˜å•æˆåŠŸ + * + * @param channelId 渠é“ç¼–å· + * @param notify 通知 + */ + void notifyOrder(Long channelId, PayOrderRespDTO notify); + + /** + * 更新支付订å•çš„é€€æ¬¾é‡‘é¢ + * + * @param id ç¼–å· + * @param incrRefundPrice å¢žåŠ çš„é€€æ¬¾é‡‘é¢ + */ + void updateOrderRefundPrice(Long id, Integer incrRefundPrice); + + /** + * 更新支付订å•ä»·æ ¼ + * + * @param id 支付å•ç¼–å· + * @param payPrice 支付å•ä»·æ ¼ + */ + void updatePayOrderPrice(Long id, Integer payPrice); + + /** + * èŽ·å¾—æ”¯ä»˜è®¢å• + * + * @param id ç¼–å· + * @return æ”¯ä»˜è®¢å• + */ + PayOrderExtensionDO getOrderExtension(Long id); + + /** + * èŽ·å¾—æ”¯ä»˜è®¢å• + * + * @param no æ”¯ä»˜è®¢å• no + * @return æ”¯ä»˜è®¢å• + */ + PayOrderExtensionDO getOrderExtensionByNo(String no); + + /** + * åŒæ­¥è®¢å•çš„æ”¯ä»˜çŠ¶æ€ + * + * @param minCreateTime 最å°åˆ›å»ºæ—¶é—´ + * @return åŒæ­¥åˆ°å·²æ”¯ä»˜çš„è®¢å•æ•°é‡ + */ + int syncOrder(LocalDateTime minCreateTime); + + /** + * åŒæ­¥è®¢å•çš„æ”¯ä»˜çŠ¶æ€ + * + * 1. Quietly 表示,å³ä½¿åŒæ­¥å¤±è´¥ï¼Œä¹Ÿä¸ä¼šæŠ›å‡ºå¼‚常 + * 2. ä»€ä¹ˆæ—¶å€™å›žå‡ºçŽ°å¼‚å¸¸ï¼Ÿå› ä¸ºæ˜¯ä¸»åŠ¨åŒæ­¥ï¼Œå¯èƒ½å’Œæ”¯ä»˜æ¸ é“的异步回调存在并å‘冲çªï¼Œå¯¼è‡´æŠ›å‡ºå¼‚常 + * + * @param id 订å•ç¼–å· + */ + void syncOrderQuietly(Long id); + + /** + * 将已过期的订å•,状æ€ä¿®æ”¹ä¸ºå·²å…³é—­ + * + * @return è¿‡æœŸçš„è®¢å•æ•°é‡ + */ + int expireOrder(); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/order/PayOrderServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/order/PayOrderServiceImpl.java new file mode 100644 index 0000000..8d555ef --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/order/PayOrderServiceImpl.java @@ -0,0 +1,605 @@ +package com.tashow.cloud.pay.service.order; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderExportReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderPageReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderSubmitReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderSubmitRespVO; +import com.tashow.cloud.pay.convert.order.PayOrderConvert; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderExtensionDO; +import com.tashow.cloud.pay.dal.mysql.order.PayOrderExtensionMapper; +import com.tashow.cloud.pay.dal.mysql.order.PayOrderMapper; +import com.tashow.cloud.pay.dal.redis.no.PayNoRedisDAO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import com.tashow.cloud.pay.framework.pay.config.PayProperties; +import com.tashow.cloud.pay.service.app.PayAppService; +import com.tashow.cloud.pay.service.channel.PayChannelService; +import com.tashow.cloud.pay.service.notify.PayNotifyService; +import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; + +/** + * æ”¯ä»˜è®¢å• Service 实现类 + * + * @author aquan + */ +@Service +@Validated +@Slf4j +public class PayOrderServiceImpl implements PayOrderService { + + @Resource + private PayProperties payProperties; + + @Resource + private PayOrderMapper orderMapper; + @Resource + private PayOrderExtensionMapper orderExtensionMapper; + @Resource + private PayNoRedisDAO noRedisDAO; + + @Resource + private PayAppService appService; + @Resource + private PayChannelService channelService; + @Resource + private PayNotifyService notifyService; + + @Override + public PayOrderDO getOrder(Long id) { + return orderMapper.selectById(id); + } + + @Override + public PayOrderDO getOrder(Long appId, String merchantOrderId) { + return orderMapper.selectByAppIdAndMerchantOrderId(appId, merchantOrderId); + } + + @Override + public List getOrderList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return orderMapper.selectBatchIds(ids); + } + + @Override + public Long getOrderCountByAppId(Long appId) { + return orderMapper.selectCountByAppId(appId); + } + + @Override + public PageResult getOrderPage(PayOrderPageReqVO pageReqVO) { + return orderMapper.selectPage(pageReqVO); + } + + @Override + public List getOrderList(PayOrderExportReqVO exportReqVO) { + return orderMapper.selectList(exportReqVO); + } + + @Override + public Long createOrder(PayOrderCreateReqDTO reqDTO) { + // 校验 App + PayAppDO app = appService.validPayApp(reqDTO.getAppKey()); + + // æŸ¥è¯¢å¯¹åº”çš„æ”¯ä»˜äº¤æ˜“å•æ˜¯å¦å·²ç»å­˜åœ¨ã€‚如果是,则直接返回 + PayOrderDO order = orderMapper.selectByAppIdAndMerchantOrderId( + app.getId(), reqDTO.getMerchantOrderId()); + if (order != null) { + log.warn("[createOrder][appId({}) merchantOrderId({}) å·²ç»å­˜åœ¨å¯¹åº”的支付å•({})]", order.getAppId(), + order.getMerchantOrderId(), toJsonString(order)); // ç†è®ºæ¥è¯´ï¼Œä¸ä¼šå‡ºçŽ°è¿™ä¸ªæƒ…å†µ + return order.getId(); + } + + // åˆ›å»ºæ”¯ä»˜äº¤æ˜“å• + order = PayOrderConvert.INSTANCE.convert(reqDTO).setAppId(app.getId()) + // 商户相关字段 + .setNotifyUrl(app.getOrderNotifyUrl()) + // 订å•相关字段 + .setStatus(PayOrderStatusEnum.WAITING.getStatus()) + // 退款相关字段 + .setRefundPrice(0); + orderMapper.insert(order); + return order.getId(); + } + + @Override // 注æ„,这里ä¸èƒ½æ·»åŠ äº‹åŠ¡æ³¨è§£ï¼Œé¿å…调用支付渠é“失败时,将 PayOrderExtensionDO 回滚了 + public PayOrderSubmitRespVO submitOrder(PayOrderSubmitReqVO reqVO, String userIp) { + // 1.1 获得 PayOrderDO ,并校验其是å¦å­˜åœ¨ + PayOrderDO order = validateOrderCanSubmit(reqVO.getId()); + // 1.32 æ ¡éªŒæ”¯ä»˜æ¸ é“æ˜¯å¦æœ‰æ•ˆ + PayChannelDO channel = validateChannelCanSubmit(order.getAppId(), reqVO.getChannelCode()); + PayClient client = channelService.getPayClient(channel.getId()); + + // 2. æ’å…¥ PayOrderExtensionDO + String no = noRedisDAO.generate(payProperties.getOrderNoPrefix()); + PayOrderExtensionDO orderExtension = PayOrderConvert.INSTANCE.convert(reqVO, userIp) + .setOrderId(order.getId()).setNo(no) + .setChannelId(channel.getId()).setChannelCode(channel.getCode()) + .setStatus(PayOrderStatusEnum.WAITING.getStatus()); + orderExtensionMapper.insert(orderExtension); + + // 3. è°ƒç”¨ä¸‰æ–¹æŽ¥å£ + PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO, userIp) + // 商户相关的字段 + .setOutTradeNo(orderExtension.getNo()) // 注æ„,此处使用的是 PayOrderExtensionDO.no å±žæ€§ï¼ + .setSubject(order.getSubject()).setBody(order.getBody()) + .setNotifyUrl(genChannelOrderNotifyUrl(channel)) + .setReturnUrl(reqVO.getReturnUrl()) + // 订å•相关字段 + .setPrice(order.getPrice()).setExpireTime(order.getExpireTime()); + PayOrderRespDTO unifiedOrderResp = client.unifiedOrder(unifiedOrderReqDTO); + + // 4. 如果调用直接支付æˆåŠŸï¼Œåˆ™ç›´æŽ¥æ›´æ–°æ”¯ä»˜å•状æ€ä¸ºæˆåŠŸã€‚ä¾‹å¦‚è¯´ï¼šä»˜æ¬¾ç æ”¯ä»˜ï¼Œå…å¯†æ”¯ä»˜æ—¶ï¼Œå°±ç›´æŽ¥éªŒè¯æ”¯ä»˜æˆåŠŸ + if (unifiedOrderResp != null) { + try { + getSelf().notifyOrder(channel, unifiedOrderResp); + } catch (Exception e) { + // 兼容 https://gitee.com/zhijiantianya/yudao-cloud/issues/I8SM9H 场景 + // æ”¯ä»˜å®æˆ–微信扫ç ä¹‹åŽæ—¶ï¼Œç”±äºŽ PayClient 是直接返回支付æˆåŠŸï¼Œè€Œæ”¯ä»˜ä¹Ÿä¼šæœ‰å›žè°ƒï¼Œå¯¼è‡´å­˜åœ¨å¹¶å‘æ›´æ–°é—®é¢˜ï¼Œæ­¤æ—¶ä¸€èˆ¬æ˜¯å¯ä»¥ try catch 直接忽略 + log.warn("[submitOrder][order({}) channel({}) 支付结果({}) 通知时å‘生异常,å¯èƒ½æ˜¯å¹¶å‘问题]", + order, channel, unifiedOrderResp, e); + } + // 如有渠é“错误ç ï¼Œåˆ™æŠ›å‡ºä¸šåŠ¡å¼‚å¸¸ï¼Œæç¤ºç”¨æˆ· + if (StrUtil.isNotEmpty(unifiedOrderResp.getChannelErrorCode())) { + throw exception(PAY_ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(), + unifiedOrderResp.getChannelErrorMsg()); + } + // 此处需è¦è¯»å–æœ€æ–°çš„çŠ¶æ€ + order = orderMapper.selectById(order.getId()); + } + return PayOrderConvert.INSTANCE.convert(order, unifiedOrderResp); + } + + private PayOrderDO validateOrderCanSubmit(Long id) { + PayOrderDO order = orderMapper.selectById(id); + if (order == null) { // 是å¦å­˜åœ¨ + throw exception(PAY_ORDER_NOT_FOUND); + } + if (PayOrderStatusEnum.isSuccess(order.getStatus())) { // 校验状æ€ï¼Œå‘现已支付 + throw exception(PAY_ORDER_STATUS_IS_SUCCESS); + } + if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状æ€ï¼Œå¿…须是待支付 + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); + } + if (LocalDateTimeUtils.beforeNow(order.getExpireTime())) { // 校验是å¦è¿‡æœŸ + throw exception(PAY_ORDER_IS_EXPIRED); + } + + // ã€é‡è¦ã€‘æ ¡éªŒæ˜¯å¦æ”¯ä»˜æ‹“展å•å·²æ”¯ä»˜ï¼Œåªæ˜¯æ²¡æœ‰å›žè°ƒã€æˆ–者数æ®ä¸æ­£å¸¸ + validateOrderActuallyPaid(id); + return order; + } + + /** + * 校验支付订å•实际已支付 + * + * @param id æ”¯ä»˜ç¼–å· + */ + @VisibleForTesting + void validateOrderActuallyPaid(Long id) { + List orderExtensions = orderExtensionMapper.selectListByOrderId(id); + orderExtensions.forEach(orderExtension -> { + // 情况一:校验数æ®åº“中的 orderExtension æ˜¯ä¸æ˜¯å·²æ”¯ä»˜ + if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { + log.warn("[validateOrderCanSubmit][order({}) çš„ extension({}) 已支付,å¯èƒ½æ˜¯æ•°æ®ä¸ä¸€è‡´]", + id, orderExtension.getId()); + throw exception(PAY_ORDER_EXTENSION_IS_PAID); + } + // 情况二:调用三方接å£ï¼ŒæŸ¥è¯¢æ”¯ä»˜å•状æ€ï¼Œæ˜¯ä¸æ˜¯å·²æ”¯ä»˜ + PayClient payClient = channelService.getPayClient(orderExtension.getChannelId()); + if (payClient == null) { + log.error("[validateOrderCanSubmit][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", orderExtension.getChannelId()); + return; + } + PayOrderRespDTO respDTO = payClient.getOrder(orderExtension.getNo()); + if (respDTO != null && PayOrderStatusRespEnum.isSuccess(respDTO.getStatus())) { + log.warn("[validateOrderCanSubmit][order({}) çš„ PayOrderRespDTO({}) 已支付,å¯èƒ½æ˜¯å›žè°ƒå»¶è¿Ÿ]", + id, toJsonString(respDTO)); + throw exception(PAY_ORDER_EXTENSION_IS_PAID); + } + }); + } + + private PayChannelDO validateChannelCanSubmit(Long appId, String channelCode) { + // 校验 App + appService.validPayApp(appId); + // æ ¡éªŒæ”¯ä»˜æ¸ é“æ˜¯å¦æœ‰æ•ˆ + PayChannelDO channel = channelService.validPayChannel(appId, channelCode); + PayClient client = channelService.getPayClient(channel.getId()); + if (client == null) { + log.error("[validatePayChannelCanSubmit][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", channel.getId()); + throw exception(CHANNEL_NOT_FOUND); + } + return channel; + } + + /** + * æ ¹æ®æ”¯ä»˜æ¸ é“的编ç ï¼Œç”Ÿæˆæ”¯ä»˜æ¸ é“çš„å›žè°ƒåœ°å€ + * + * @param channel æ”¯ä»˜æ¸ é“ + * @return 支付渠é“çš„å›žè°ƒåœ°å€ é…ç½®åœ°å€ + "/" + channel id + */ + private String genChannelOrderNotifyUrl(PayChannelDO channel) { + return payProperties.getOrderNotifyUrl() + "/" + channel.getId(); + } + + @Override + public void notifyOrder(Long channelId, PayOrderRespDTO notify) { + // æ ¡éªŒæ”¯ä»˜æ¸ é“æ˜¯å¦æœ‰æ•ˆ + PayChannelDO channel = channelService.validPayChannel(channelId); + // 更新支付订å•为已支付 + TenantUtils.execute(channel.getTenantId(), () -> getSelf().notifyOrder(channel, notify)); + } + + /** + * 通知并更新订å•的支付结果 + * + * @param channel æ”¯ä»˜æ¸ é“ + * @param notify 通知 + */ + @Transactional(rollbackFor = Exception.class) + // 注æ„,如果是方法内调用该方法,需è¦é€šè¿‡ getSelf().notifyPayOrder(channel, notify) 调用,å¦åˆ™äº‹åŠ¡ä¸ç”Ÿæ•ˆ + public void notifyOrder(PayChannelDO channel, PayOrderRespDTO notify) { + // 情况一:支付æˆåŠŸçš„å›žè°ƒ + if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) { + notifyOrderSuccess(channel, notify); + return; + } + // 情况二:支付失败的回调 + if (PayOrderStatusRespEnum.isClosed(notify.getStatus())) { + notifyOrderClosed(channel, notify); + } + // 情况三:WAITINGï¼šæ— éœ€å¤„ç† + // 情况四:REFUNDï¼šé€šè¿‡é€€æ¬¾å›žè°ƒå¤„ç† + } + + private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { + // 1. æ›´æ–° PayOrderExtensionDO 支付æˆåŠŸ + PayOrderExtensionDO orderExtension = updateOrderSuccess(notify); + // 2. æ›´æ–° PayOrderDO 支付æˆåŠŸ + Boolean paid = updateOrderSuccess(channel, orderExtension, notify); + if (paid) { // 如果之å‰å·²ç»æˆåŠŸå›žè°ƒï¼Œåˆ™ç›´æŽ¥è¿”å›žï¼Œä¸ç”¨é‡å¤è®°å½•支付通知记录;例如说:支付平å°é‡å¤å›žè°ƒ + return; + } + + // 3. æ’入支付通知记录 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.ORDER.getType(), + orderExtension.getOrderId()); + } + + /** + * æ›´æ–° PayOrderExtensionDO 支付æˆåŠŸ + * + * @param notify 通知 + * @return PayOrderExtensionDO 对象 + */ + private PayOrderExtensionDO updateOrderSuccess(PayOrderRespDTO notify) { + // 1. 查询 PayOrderExtensionDO + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo()); + if (orderExtension == null) { + throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); + } + if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // å¦‚æžœå·²ç»æ˜¯æˆåŠŸï¼Œç›´æŽ¥è¿”å›žï¼Œä¸ç”¨é‡å¤æ›´æ–° + log.info("[updateOrderExtensionSuccess][orderExtension({}) å·²ç»æ˜¯å·²æ”¯ä»˜ï¼Œæ— éœ€æ›´æ–°]", orderExtension.getId()); + return orderExtension; + } + if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状æ€ï¼Œå¿…须是待支付 + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + } + + // 2. æ›´æ–° PayOrderExtensionDO + int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(), + PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build()); + if (updateCounts == 0) { // 校验状æ€ï¼Œå¿…须是待支付 + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + } + log.info("[updateOrderExtensionSuccess][orderExtension({}) 更新为已支付]", orderExtension.getId()); + return orderExtension; + } + + /** + * æ›´æ–° PayOrderDO 支付æˆåŠŸ + * + * @param channel æ”¯ä»˜æ¸ é“ + * @param orderExtension æ”¯ä»˜æ‹“å±•å• + * @param notify 通知回调 + * @return 是å¦ä¹‹å‰å·²ç»æˆåŠŸå›žè°ƒ + */ + private Boolean updateOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, + PayOrderRespDTO notify) { + // 1. 判断 PayOrderDO 是å¦å¤„于待支付 + PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); + if (order == null) { + throw exception(PAY_ORDER_NOT_FOUND); + } + if (PayOrderStatusEnum.isSuccess(order.getStatus()) // å¦‚æžœå·²ç»æ˜¯æˆåŠŸï¼Œç›´æŽ¥è¿”å›žï¼Œä¸ç”¨é‡å¤æ›´æ–° + && Objects.equals(order.getExtensionId(), orderExtension.getId())) { + log.info("[updateOrderExtensionSuccess][order({}) å·²ç»æ˜¯å·²æ”¯ä»˜ï¼Œæ— éœ€æ›´æ–°]", order.getId()); + return true; + } + if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状æ€ï¼Œå¿…须是待支付 + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); + } + + // 2. æ›´æ–° PayOrderDO + int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(), + PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()) + .channelId(channel.getId()).channelCode(channel.getCode()) + .successTime(notify.getSuccessTime()).extensionId(orderExtension.getId()).no(orderExtension.getNo()) + .channelOrderNo(notify.getChannelOrderNo()).channelUserId(notify.getChannelUserId()) + .channelFeeRate(channel.getFeeRate()) + .channelFeePrice(MoneyUtils.calculateRatePrice(order.getPrice(), channel.getFeeRate())) + .build()); + if (updateCounts == 0) { // 校验状æ€ï¼Œå¿…须是待支付 + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); + } + log.info("[updateOrderExtensionSuccess][order({}) 更新为已支付]", order.getId()); + return false; + } + + private void notifyOrderClosed(PayChannelDO channel, PayOrderRespDTO notify) { + updateOrderExtensionClosed(channel, notify); + } + + private void updateOrderExtensionClosed(PayChannelDO channel, PayOrderRespDTO notify) { + // 1. 查询 PayOrderExtensionDO + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo()); + if (orderExtension == null) { + throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); + } + if (PayOrderStatusEnum.isClosed(orderExtension.getStatus())) { // å¦‚æžœå·²ç»æ˜¯å…³é—­ï¼Œç›´æŽ¥è¿”回,ä¸ç”¨é‡å¤æ›´æ–° + log.info("[updateOrderExtensionClosed][orderExtension({}) å·²ç»æ˜¯æ”¯ä»˜å…³é—­ï¼Œæ— éœ€æ›´æ–°]", orderExtension.getId()); + return; + } + // 一般出现先是支付æˆåŠŸï¼Œç„¶åŽæ”¯ä»˜å…³é—­ï¼Œéƒ½æ˜¯å…¨éƒ¨é€€æ¬¾å¯¼è‡´å…³é—­çš„åœºæ™¯ã€‚è¿™ä¸ªæƒ…å†µï¼Œæˆ‘ä»¬ä¸æ›´æ–°æ”¯ä»˜æ‹“展å•,åªé€šè¿‡é€€æ¬¾æµç¨‹ï¼Œæ›´æ–°æ”¯ä»˜å• + if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { + log.info("[updateOrderExtensionClosed][orderExtension({}) 是已支付,无需更新为支付关闭]", orderExtension.getId()); + return; + } + if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状æ€ï¼Œå¿…须是待支付 + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + } + + // 2. æ›´æ–° PayOrderExtensionDO + int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(), + PayOrderExtensionDO.builder().status(PayOrderStatusEnum.CLOSED.getStatus()).channelNotifyData(toJsonString(notify)) + .channelErrorCode(notify.getChannelErrorCode()).channelErrorMsg(notify.getChannelErrorMsg()).build()); + if (updateCounts == 0) { // 校验状æ€ï¼Œå¿…须是待支付 + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + } + log.info("[updateOrderExtensionClosed][orderExtension({}) 更新为支付关闭]", orderExtension.getId()); + } + + @Override + public void updateOrderRefundPrice(Long id, Integer incrRefundPrice) { + PayOrderDO order = orderMapper.selectById(id); + if (order == null) { + throw exception(PAY_ORDER_NOT_FOUND); + } + if (!PayOrderStatusEnum.isSuccessOrRefund(order.getStatus())) { + throw exception(PAY_ORDER_REFUND_FAIL_STATUS_ERROR); + } + if (order.getRefundPrice() + incrRefundPrice > order.getPrice()) { + throw exception(REFUND_PRICE_EXCEED); + } + + // æ›´æ–°è®¢å• + PayOrderDO updateObj = new PayOrderDO() + .setRefundPrice(order.getRefundPrice() + incrRefundPrice) + .setStatus(PayOrderStatusEnum.REFUND.getStatus()); + int updateCount = orderMapper.updateByIdAndStatus(id, order.getStatus(), updateObj); + if (updateCount == 0) { + throw exception(PAY_ORDER_REFUND_FAIL_STATUS_ERROR); + } + } + + @Override + public void updatePayOrderPrice(Long id, Integer payPrice) { + PayOrderDO order = orderMapper.selectById(id); + if (order == null) { + throw exception(PAY_ORDER_NOT_FOUND); + } + if (ObjectUtil.notEqual(PayOrderStatusEnum.WAITING.getStatus(), order.getStatus())) { + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); + } + if (ObjectUtil.equal(order.getPrice(), payPrice)) { + return; + } + + orderMapper.updateById(new PayOrderDO().setId(order.getId()).setPrice(payPrice)); + } + + @Override + public PayOrderExtensionDO getOrderExtension(Long id) { + return orderExtensionMapper.selectById(id); + } + + @Override + public PayOrderExtensionDO getOrderExtensionByNo(String no) { + return orderExtensionMapper.selectByNo(no); + } + + @Override + public int syncOrder(LocalDateTime minCreateTime) { + // 1. 查询指定创建时间å‰çš„å¾…æ”¯ä»˜è®¢å• + List orderExtensions = orderExtensionMapper.selectListByStatusAndCreateTimeGe( + PayOrderStatusEnum.WAITING.getStatus(), minCreateTime); + if (CollUtil.isEmpty(orderExtensions)) { + return 0; + } + // 2. é历执行 + int count = 0; + for (PayOrderExtensionDO orderExtension : orderExtensions) { + count += syncOrder(orderExtension) ? 1 : 0; + } + return count; + } + + @Override + public void syncOrderQuietly(Long id) { + // 1. æŸ¥è¯¢å¾…æ”¯ä»˜è®¢å• + List orderExtensions = orderExtensionMapper.selectListByOrderIdAndStatus(id, + PayOrderStatusEnum.WAITING.getStatus()); + + // 2. é历执行 + for (PayOrderExtensionDO orderExtension : orderExtensions) { + syncOrder(orderExtension); + } + } + + /** + * åŒæ­¥å•ä¸ªæ”¯ä»˜æ‹“å±•å• + * + * @param orderExtension æ”¯ä»˜æ‹“å±•å• + * @return 是å¦å·²æ”¯ä»˜ + */ + private boolean syncOrder(PayOrderExtensionDO orderExtension) { + try { + // 1.1 查询支付订å•ä¿¡æ¯ + PayClient payClient = channelService.getPayClient(orderExtension.getChannelId()); + if (payClient == null) { + log.error("[syncOrder][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", orderExtension.getChannelId()); + return false; + } + PayOrderRespDTO respDTO = payClient.getOrder(orderExtension.getNo()); + // 如果查询到订å•ä¸å­˜åœ¨ï¼ŒPayClient 返回的状æ€ä¸ºå…³é—­ã€‚但此时ä¸èƒ½å…³é—­è®¢å•。存在以下一ç§åœºæ™¯ï¼š + // æ‹‰èµ·æ¸ é“æ”¯ä»˜åŽï¼ŒçŸ­æ—¶é—´å†…ç”¨æˆ·æœªåŠæ—¶å®Œæˆæ”¯ä»˜ï¼Œä½†æ˜¯è¯¥è®¢å•åŒæ­¥å®šæ—¶ä»»åŠ¡æ°å·§è‡ªåŠ¨è§¦å‘了,主动查询结果为订å•ä¸å­˜åœ¨ã€‚ + // 当用户支付æˆåŠŸä¹‹åŽï¼Œè¯¥è®¢å•状æ€åœ¨æ¸ é“的回调中无法从已关闭改为已支付,造æˆé‡å¤§å½±å“。 + // 考虑此定时任务是异常场景的兜底æ“作,因此这里ä¸åšå˜æ›´ï¼Œä¼˜å…ˆä»¥å›žè°ƒä¸ºå‡†ã€‚ + // 让订å•自动éšç€æ”¯ä»˜æ¸ é“é‚£è¾¹ä¸€èµ·ç­‰åˆ°è¿‡æœŸï¼Œç¡®ä¿æ¸ é“先过期关闭支付入å£ï¼Œè€ŒåŽé€šè¿‡è®¢å•过期定时任务关闭自己的订å•。 + if (PayOrderStatusRespEnum.isClosed(respDTO.getStatus())) { + return false; + } + // 1.2 回调支付结果 + notifyOrder(orderExtension.getChannelId(), respDTO); + + // 2. 如果是已支付,则返回 true + return PayOrderStatusRespEnum.isSuccess(respDTO.getStatus()); + } catch (Throwable e) { + log.error("[syncOrder][orderExtension({}) åŒæ­¥æ”¯ä»˜çжæ€å¼‚常]", orderExtension.getId(), e); + return false; + } + } + + @Override + public int expireOrder() { + // 1. æŸ¥è¯¢è¿‡æœŸçš„å¾…æ”¯ä»˜è®¢å• + List orders = orderMapper.selectListByStatusAndExpireTimeLt( + PayOrderStatusEnum.WAITING.getStatus(), LocalDateTime.now()); + if (CollUtil.isEmpty(orders)) { + return 0; + } + + // 2. é历执行 + int count = 0; + for (PayOrderDO order : orders) { + count += expireOrder(order) ? 1 : 0; + } + return count; + } + + /** + * åŒæ­¥å•ä¸ªæ”¯ä»˜å• + * + * @param order æ”¯ä»˜å• + * @return 是å¦å·²è¿‡æœŸ + */ + private boolean expireOrder(PayOrderDO order) { + try { + // 1. 需è¦å…ˆå¤„ç†å…³è”的支付拓展å•,é¿å…错误的过期已支付 or å·²é€€æ¬¾çš„è®¢å• + List orderExtensions = orderExtensionMapper.selectListByOrderId(order.getId()); + for (PayOrderExtensionDO orderExtension : orderExtensions) { + if (PayOrderStatusEnum.isClosed(orderExtension.getStatus())) { + continue; + } + // 情况一:校验数æ®åº“中的 orderExtension æ˜¯ä¸æ˜¯å·²æ”¯ä»˜ + if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { + log.error("[expireOrder][order({}) çš„ extension({}) 已支付,å¯èƒ½æ˜¯æ•°æ®ä¸ä¸€è‡´]", + order.getId(), orderExtension.getId()); + return false; + } + // 情况二:调用三方接å£ï¼ŒæŸ¥è¯¢æ”¯ä»˜å•状æ€ï¼Œæ˜¯ä¸æ˜¯å·²æ”¯ä»˜/已退款 + PayClient payClient = channelService.getPayClient(orderExtension.getChannelId()); + if (payClient == null) { + log.error("[expireOrder][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", orderExtension.getChannelId()); + return false; + } + PayOrderRespDTO respDTO = payClient.getOrder(orderExtension.getNo()); + if (PayOrderStatusRespEnum.isRefund(respDTO.getStatus())) { + // 补充说明:按é“ç†ï¼Œåº”该是 WAITING => SUCCESS => REFUND 状æ€ï¼Œå¦‚果直接 WAITING => REFUND 状æ€ï¼Œè¯´æ˜Žä¸­é—´ä¸¢äº†è¿‡ç¨‹ + // 此时,需è¦äººå·¥ä»‹å…¥ï¼Œæ‰‹å·¥è¡¥é½æ•°æ®ï¼Œä¿æŒ WAITING => SUCCESS => REFUND 的过程 + log.error("[expireOrder][extension({}) çš„ PayOrderRespDTO({}) 已退款,å¯èƒ½æ˜¯å›žè°ƒå»¶è¿Ÿ]", + orderExtension.getId(), toJsonString(respDTO)); + return false; + } + if (PayOrderStatusRespEnum.isSuccess(respDTO.getStatus())) { + notifyOrder(orderExtension.getChannelId(), respDTO); + return false; + } + // å…œåº•é€»è¾‘ï¼šå°†æ”¯ä»˜æ‹“å±•å•æ›´æ–°ä¸ºå·²å…³é—­ + PayOrderExtensionDO updateObj = new PayOrderExtensionDO().setStatus(PayOrderStatusEnum.CLOSED.getStatus()) + .setChannelNotifyData(toJsonString(respDTO)); + if (orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), PayOrderStatusEnum.WAITING.getStatus(), + updateObj) == 0) { + log.error("[expireOrder][extension({}) 更新为支付关闭失败]", orderExtension.getId()); + return false; + } + log.info("[expireOrder][extension({}) 更新为支付关闭æˆåŠŸ]", orderExtension.getId()); + } + + // 2. 都没有上述情况,å¯ä»¥å®‰å¿ƒæ›´æ–°ä¸ºå·²å…³é—­ + PayOrderDO updateObj = new PayOrderDO().setStatus(PayOrderStatusEnum.CLOSED.getStatus()); + if (orderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), updateObj) == 0) { + log.error("[expireOrder][order({}) 更新为支付关闭失败]", order.getId()); + return false; + } + log.info("[expireOrder][order({}) 更新为支付关闭失败]", order.getId()); + return true; + } catch (Throwable e) { + log.error("[expireOrder][order({}) 过期订å•异常]", order.getId(), e); + return false; + } + } + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private PayOrderServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/refund/PayRefundService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/refund/PayRefundService.java new file mode 100644 index 0000000..463b006 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/refund/PayRefundService.java @@ -0,0 +1,82 @@ +package com.tashow.cloud.pay.service.refund; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundExportReqVO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; + +import java.util.List; + +/** + * é€€æ¬¾è®¢å• Service æŽ¥å£ + * + * @author aquan + */ +public interface PayRefundService { + + /** + * èŽ·å¾—é€€æ¬¾è®¢å• + * + * @param id ç¼–å· + * @return é€€æ¬¾è®¢å• + */ + PayRefundDO getRefund(Long id); + + /** + * èŽ·å¾—é€€æ¬¾è®¢å• + * + * @param no 外部退款å•å· + * @return é€€æ¬¾è®¢å• + */ + PayRefundDO getRefundByNo(String no); + + /** + * èŽ·å¾—æŒ‡å®šåº”ç”¨çš„é€€æ¬¾æ•°é‡ + * + * @param appId åº”ç”¨ç¼–å· + * @return é€€æ¬¾æ•°é‡ + */ + Long getRefundCountByAppId(Long appId); + + /** + * 获得退款订å•分页 + * + * @param pageReqVO 分页查询 + * @return 退款订å•分页 + */ + PageResult getRefundPage(PayRefundPageReqVO pageReqVO); + + /** + * 获得退款订å•列表, 用于 Excel 导出 + * + * @param exportReqVO 查询æ¡ä»¶ + * @return 退款订å•列表 + */ + List getRefundList(PayRefundExportReqVO exportReqVO); + + /** + * 创建退款申请 + * + * @param reqDTO é€€æ¬¾ç”³è¯·ä¿¡æ¯ + * @return 退款å•å· + */ + Long createPayRefund(PayRefundCreateReqDTO reqDTO); + + /** + * 渠é“的退款通知 + * + * @param channelId 渠é“ç¼–å· + * @param notify 通知 + */ + void notifyRefund(Long channelId, PayRefundRespDTO notify); + + /** + * åŒæ­¥æ¸ é“é€€æ¬¾çš„é€€æ¬¾çŠ¶æ€ + * + * @return åŒæ­¥åˆ°çжæ€çš„退款数é‡ï¼ŒåŒ…括退款æˆåŠŸã€é€€æ¬¾å¤±è´¥ + */ + int syncRefund(); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/refund/PayRefundServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/refund/PayRefundServiceImpl.java new file mode 100644 index 0000000..d1bdce7 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/refund/PayRefundServiceImpl.java @@ -0,0 +1,332 @@ +package com.tashow.cloud.pay.service.refund; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundExportReqVO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundPageReqVO; +import com.tashow.cloud.pay.convert.refund.PayRefundConvert; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import com.tashow.cloud.pay.dal.mysql.refund.PayRefundMapper; +import com.tashow.cloud.pay.dal.redis.no.PayNoRedisDAO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; +import com.tashow.cloud.pay.framework.pay.config.PayProperties; +import com.tashow.cloud.pay.service.app.PayAppService; +import com.tashow.cloud.pay.service.channel.PayChannelService; +import com.tashow.cloud.pay.service.notify.PayNotifyService; +import com.tashow.cloud.pay.service.order.PayOrderService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; + +/** + * é€€æ¬¾è®¢å• Service 实现类 + * + * @author jason + */ +@Service +@Slf4j +@Validated +public class PayRefundServiceImpl implements PayRefundService { + + @Resource + private PayProperties payProperties; + + @Resource + private PayRefundMapper refundMapper; + @Resource + private PayNoRedisDAO noRedisDAO; + + @Resource + private PayOrderService orderService; + @Resource + private PayAppService appService; + @Resource + private PayChannelService channelService; + @Resource + private PayNotifyService notifyService; + + @Override + public PayRefundDO getRefund(Long id) { + return refundMapper.selectById(id); + } + + @Override + public PayRefundDO getRefundByNo(String no) { + return refundMapper.selectByNo(no); + } + + @Override + public Long getRefundCountByAppId(Long appId) { + return refundMapper.selectCountByAppId(appId); + } + + @Override + public PageResult getRefundPage(PayRefundPageReqVO pageReqVO) { + return refundMapper.selectPage(pageReqVO); + } + + @Override + public List getRefundList(PayRefundExportReqVO exportReqVO) { + return refundMapper.selectList(exportReqVO); + } + + @Override + public Long createPayRefund(PayRefundCreateReqDTO reqDTO) { + // 1.1 校验 App + PayAppDO app = appService.validPayApp(reqDTO.getAppKey()); + // 1.2 æ ¡éªŒæ”¯ä»˜è®¢å• + PayOrderDO order = validatePayOrderCanRefund(reqDTO, app.getId()); + // 1.3 æ ¡éªŒæ”¯ä»˜æ¸ é“æ˜¯å¦æœ‰æ•ˆ + PayChannelDO channel = channelService.validPayChannel(order.getChannelId()); + PayClient client = channelService.getPayClient(channel.getId()); + if (client == null) { + log.error("[refund][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", channel.getId()); + throw exception(CHANNEL_NOT_FOUND); + } + // 1.4 æ ¡éªŒé€€æ¬¾è®¢å•æ˜¯å¦å·²ç»å­˜åœ¨ + PayRefundDO refund = refundMapper.selectByAppIdAndMerchantRefundId( + app.getId(), reqDTO.getMerchantRefundId()); + if (refund != null) { + throw exception(REFUND_EXISTS); + } + + // 2.1 æ’å…¥é€€æ¬¾å• + String no = noRedisDAO.generate(payProperties.getRefundNoPrefix()); + refund = PayRefundConvert.INSTANCE.convert(reqDTO) + .setNo(no).setAppId(app.getId()).setOrderId(order.getId()).setOrderNo(order.getNo()) + .setChannelId(order.getChannelId()).setChannelCode(order.getChannelCode()) + // 商户相关的字段 + .setNotifyUrl(app.getRefundNotifyUrl()) + // 渠é“相关字段 + .setChannelOrderNo(order.getChannelOrderNo()) + // 退款相关字段 + .setStatus(PayRefundStatusEnum.WAITING.getStatus()) + .setPayPrice(order.getPrice()).setRefundPrice(reqDTO.getPrice()); + refundMapper.insert(refund); + try { + // 2.2 呿¸ é“å‘起退款申请 + PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO() + .setPayPrice(order.getPrice()) + .setRefundPrice(reqDTO.getPrice()) + .setOutTradeNo(order.getNo()) + .setOutRefundNo(refund.getNo()) + .setNotifyUrl(genChannelRefundNotifyUrl(channel)) + .setReason(reqDTO.getReason()); + PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO); + // 2.3 处ç†é€€æ¬¾è¿”回 + getSelf().notifyRefund(channel, refundRespDTO); + } catch (Throwable e) { + // 注æ„:这里仅打å°å¼‚常,ä¸è¿›è¡ŒæŠ›å‡ºã€‚ + // 原因是:虽然调用支付渠é“进行退款å‘生异常(网络请求超时),实际退款æˆåŠŸã€‚è¿™ä¸ªç»“æžœï¼ŒåŽç»­é€šè¿‡é€€æ¬¾å›žè°ƒã€æˆ–者退款轮询补å¿å¯ä»¥æ‹¿åˆ°ã€‚ + // 最终,在异常的情况下,支付中心会异步回调业务的退款回调接å£ï¼Œæä¾›é€€æ¬¾ç»“æžœ + log.error("[createPayRefund][退款 id({}) requestDTO({}) å‘生异常]", + refund.getId(), reqDTO, e); + } + + // è¿”å›žé€€æ¬¾ç¼–å· + return refund.getId(); + } + + /** + * æ ¡éªŒæ”¯ä»˜è®¢å•æ˜¯å¦å¯ä»¥é€€æ¬¾ + * + * @param reqDTO é€€æ¬¾ç”³è¯·ä¿¡æ¯ + * @return æ”¯ä»˜è®¢å• + */ + private PayOrderDO validatePayOrderCanRefund(PayRefundCreateReqDTO reqDTO, Long appId) { + PayOrderDO order = orderService.getOrder(appId, reqDTO.getMerchantOrderId()); + if (order == null) { + throw exception(PAY_ORDER_NOT_FOUND); + } + // 校验状æ€ï¼Œå¿…é¡»æ˜¯å·²æ”¯ä»˜ã€æˆ–者已退款 + if (!PayOrderStatusEnum.isSuccessOrRefund(order.getStatus())) { + throw exception(PAY_ORDER_REFUND_FAIL_STATUS_ERROR); + } + + // 校验金é¢ï¼Œé€€æ¬¾é‡‘é¢ä¸èƒ½å¤§äºŽåŽŸå®šçš„é‡‘é¢ + if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()) { + throw exception(REFUND_PRICE_EXCEED); + } + // æ˜¯å¦æœ‰é€€æ¬¾ä¸­çš„è®¢å• + if (refundMapper.selectCountByAppIdAndOrderId(appId, order.getId(), + PayRefundStatusEnum.WAITING.getStatus()) > 0) { + throw exception(REFUND_HAS_REFUNDING); + } + return order; + } + + /** + * æ ¹æ®æ”¯ä»˜æ¸ é“的编ç ï¼Œç”Ÿæˆæ”¯ä»˜æ¸ é“çš„å›žè°ƒåœ°å€ + * + * @param channel æ”¯ä»˜æ¸ é“ + * @return 支付渠é“çš„å›žè°ƒåœ°å€ é…ç½®åœ°å€ + "/" + channel id + */ + private String genChannelRefundNotifyUrl(PayChannelDO channel) { + return payProperties.getRefundNotifyUrl() + "/" + channel.getId(); + } + + @Override + public void notifyRefund(Long channelId, PayRefundRespDTO notify) { + // æ ¡éªŒæ”¯ä»˜æ¸ é“æ˜¯å¦æœ‰æ•ˆ + PayChannelDO channel = channelService.validPayChannel(channelId); + // æ›´æ–°é€€æ¬¾è®¢å• + TenantUtils.execute(channel.getTenantId(), () -> getSelf().notifyRefund(channel, notify)); + } + + /** + * 通知并更新订å•的退款结果 + * + * @param channel æ”¯ä»˜æ¸ é“ + * @param notify 通知 + */ + // 注æ„,如果是方法内调用该方法,需è¦é€šè¿‡ getSelf().notifyRefund(channel, notify) 调用,å¦åˆ™äº‹åŠ¡ä¸ç”Ÿæ•ˆ + @Transactional(rollbackFor = Exception.class) + public void notifyRefund(PayChannelDO channel, PayRefundRespDTO notify) { + // 情况一:退款æˆåŠŸ + if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) { + notifyRefundSuccess(channel, notify); + return; + } + // 情况二:退款失败 + if (PayRefundStatusRespEnum.isFailure(notify.getStatus())) { + notifyRefundFailure(channel, notify); + } + } + + private void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) { + // 1.1 查询 PayRefundDO + PayRefundDO refund = refundMapper.selectByAppIdAndNo( + channel.getAppId(), notify.getOutRefundNo()); + if (refund == null) { + throw exception(REFUND_NOT_FOUND); + } + if (PayRefundStatusEnum.isSuccess(refund.getStatus())) { // å¦‚æžœå·²ç»æ˜¯æˆåŠŸï¼Œç›´æŽ¥è¿”å›žï¼Œä¸ç”¨é‡å¤æ›´æ–° + log.info("[notifyRefundSuccess][退款订å•({}) å·²ç»æ˜¯é€€æ¬¾æˆåŠŸï¼Œæ— éœ€æ›´æ–°]", refund.getId()); + return; + } + if (!PayRefundStatusEnum.WAITING.getStatus().equals(refund.getStatus())) { + throw exception(REFUND_STATUS_IS_NOT_WAITING); + } + // 1.2 æ›´æ–° PayRefundDO + PayRefundDO updateRefundObj = new PayRefundDO() + .setSuccessTime(notify.getSuccessTime()) + .setChannelRefundNo(notify.getChannelRefundNo()) + .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()) + .setChannelNotifyData(toJsonString(notify)); + int updateCounts = refundMapper.updateByIdAndStatus(refund.getId(), refund.getStatus(), updateRefundObj); + if (updateCounts == 0) { // 校验状æ€ï¼Œå¿…é¡»æ˜¯ç­‰å¾…çŠ¶æ€ + throw exception(REFUND_STATUS_IS_NOT_WAITING); + } + log.info("[notifyRefundSuccess][退款订å•({}) 更新为退款æˆåŠŸ]", refund.getId()); + + // 2. æ›´æ–°è®¢å• + orderService.updateOrderRefundPrice(refund.getOrderId(), refund.getRefundPrice()); + + // 3. æ’入退款通知记录 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.REFUND.getType(), + refund.getId()); + } + + private void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) { + // 1.1 查询 PayRefundDO + PayRefundDO refund = refundMapper.selectByAppIdAndNo( + channel.getAppId(), notify.getOutRefundNo()); + if (refund == null) { + throw exception(REFUND_NOT_FOUND); + } + if (PayRefundStatusEnum.isFailure(refund.getStatus())) { // å¦‚æžœå·²ç»æ˜¯æˆåŠŸï¼Œç›´æŽ¥è¿”å›žï¼Œä¸ç”¨é‡å¤æ›´æ–° + log.info("[notifyRefundSuccess][退款订å•({}) å·²ç»æ˜¯é€€æ¬¾å…³é—­ï¼Œæ— éœ€æ›´æ–°]", refund.getId()); + return; + } + if (!PayRefundStatusEnum.WAITING.getStatus().equals(refund.getStatus())) { + throw exception(REFUND_STATUS_IS_NOT_WAITING); + } + // 1.2 æ›´æ–° PayRefundDO + PayRefundDO updateRefundObj = new PayRefundDO() + .setChannelRefundNo(notify.getChannelRefundNo()) + .setStatus(PayRefundStatusEnum.FAILURE.getStatus()) + .setChannelNotifyData(toJsonString(notify)) + .setChannelErrorCode(notify.getChannelErrorCode()).setChannelErrorMsg(notify.getChannelErrorMsg()); + int updateCounts = refundMapper.updateByIdAndStatus(refund.getId(), refund.getStatus(), updateRefundObj); + if (updateCounts == 0) { // 校验状æ€ï¼Œå¿…é¡»æ˜¯ç­‰å¾…çŠ¶æ€ + throw exception(REFUND_STATUS_IS_NOT_WAITING); + } + log.info("[notifyRefundFailure][退款订å•({}) 更新为退款失败]", refund.getId()); + + // 2. æ’入退款通知记录 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.REFUND.getType(), + refund.getId()); + } + + @Override + public int syncRefund() { + // 1. æŸ¥è¯¢æŒ‡å®šåˆ›å»ºæ—¶é—´å†…çš„å¾…é€€æ¬¾è®¢å• + List refunds = refundMapper.selectListByStatus(PayRefundStatusEnum.WAITING.getStatus()); + if (CollUtil.isEmpty(refunds)) { + return 0; + } + // 2. é历执行 + int count = 0; + for (PayRefundDO refund : refunds) { + count += syncRefund(refund) ? 1 : 0; + } + return count; + } + + /** + * åŒæ­¥å•ä¸ªé€€æ¬¾è®¢å• + * + * @param refund é€€æ¬¾è®¢å• + * @return 是å¦åŒæ­¥åˆ° + */ + private boolean syncRefund(PayRefundDO refund) { + try { + // 1.1 查询退款订å•ä¿¡æ¯ + PayClient payClient = channelService.getPayClient(refund.getChannelId()); + if (payClient == null) { + log.error("[syncRefund][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", refund.getChannelId()); + return false; + } + PayRefundRespDTO respDTO = payClient.getRefund(refund.getOrderNo(), refund.getNo()); + // 1.2 回调退款结果 + notifyRefund(refund.getChannelId(), respDTO); + + // 2. å¦‚æžœåŒæ­¥åˆ°ï¼Œåˆ™è¿”回 true + return PayRefundStatusEnum.isSuccess(respDTO.getStatus()) + || PayRefundStatusEnum.isFailure(respDTO.getStatus()); + } catch (Throwable e) { + log.error("[syncRefund][refund({}) åŒæ­¥é€€æ¬¾çжæ€å¼‚常]", refund.getId(), e); + return false; + } + } + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private PayRefundServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/transfer/PayTransferService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/transfer/PayTransferService.java new file mode 100644 index 0000000..1ec738f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/transfer/PayTransferService.java @@ -0,0 +1,66 @@ +package com.tashow.cloud.pay.service.transfer; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; +import com.tashow.cloud.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; +import com.tashow.cloud.pay.controller.admin.transfer.vo.PayTransferPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.transfer.PayTransferDO; +import jakarta.validation.Valid; + +/** + * 转账 Service æŽ¥å£ + * + * @author jason + */ +public interface PayTransferService { + + /** + * 创建转账å•,并å‘起转账 + * + * 此时,会å‘起转账渠é“的调用 + * + * @param reqVO 请求 + * @param userIp 用户 ip + * @return 渠é“的返回结果 + */ + PayTransferDO createTransfer(@Valid PayTransferCreateReqVO reqVO, String userIp); + + /** + * 创建转账å•,并å‘起转账 + * + * @param reqDTO 创建请求 + * @return 转账å•ç¼–å· + */ + Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO); + + /** + * 获å–è½¬è´¦å• + * @param id 转账å•ç¼–å· + */ + PayTransferDO getTransfer(Long id); + + /** + * 获得转账å•分页 + * + * @param pageReqVO 分页查询 + * @return 转账å•分页 + */ + PageResult getTransferPage(PayTransferPageReqVO pageReqVO); + + /** + * åŒæ­¥æ¸ é“转账å•çŠ¶æ€ + * + * @return åŒæ­¥åˆ°çжæ€çš„转账数é‡ï¼ŒåŒ…括转账æˆåŠŸã€è½¬è´¦å¤±è´¥ã€è½¬è´¦ä¸­çš„ + */ + int syncTransfer(); + + /** + * 渠é“的转账通知 + * + * @param channelId 渠é“ç¼–å· + * @param notify 通知 + */ + void notifyTransfer(Long channelId, PayTransferRespDTO notify); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/transfer/PayTransferServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/transfer/PayTransferServiceImpl.java new file mode 100644 index 0000000..f6892e7 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/transfer/PayTransferServiceImpl.java @@ -0,0 +1,313 @@ +package com.tashow.cloud.pay.service.transfer; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum; +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; +import com.tashow.cloud.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; +import com.tashow.cloud.pay.controller.admin.transfer.vo.PayTransferPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.dal.dataobject.transfer.PayTransferDO; +import com.tashow.cloud.pay.dal.mysql.transfer.PayTransferMapper; +import com.tashow.cloud.pay.dal.redis.no.PayNoRedisDAO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; +import com.tashow.cloud.pay.framework.pay.config.PayProperties; +import com.tashow.cloud.pay.service.app.PayAppService; +import com.tashow.cloud.pay.service.channel.PayChannelService; +import com.tashow.cloud.pay.service.notify.PayNotifyService; +import jakarta.annotation.Resource; +import jakarta.validation.Validator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.tashow.cloud.pay.convert.transfer.PayTransferConvert.INSTANCE; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.*; + +// TODO @jasonï¼šç­‰å½»åº•å®žçŽ°å®Œï¼Œå•æµ‹å†™å†™ï¼› + +/** + * 转账 Service 实现类 + * + * @author jason + */ +@Service +@Slf4j +public class PayTransferServiceImpl implements PayTransferService { + + private static final String TRANSFER_NO_PREFIX = "T"; + + @Resource + private PayProperties payProperties; + + @Resource + private PayTransferMapper transferMapper; + @Resource + private PayAppService appService; + @Resource + private PayChannelService channelService; + @Resource + private PayNotifyService notifyService; + @Resource + private PayNoRedisDAO noRedisDAO; + @Resource + private Validator validator; + + @Override + public PayTransferDO createTransfer(PayTransferCreateReqVO reqVO, String userIp) { + // 1. æ ¡éªŒå‚æ•° + reqVO.validate(validator); + + // 2. 创建转账å•,å‘起转账 + PayTransferCreateReqDTO req = INSTANCE.convert(reqVO).setUserIp(userIp); + Long transferId = createTransfer(req); + + // 3. è¿”å›žè½¬è´¦å• + return getTransfer(transferId); + } + + @Override + public Long createTransfer(PayTransferCreateReqDTO reqDTO) { + // 1.1 校验 App + PayAppDO payApp = appService.validPayApp(reqDTO.getAppKey()); + // 1.2 æ ¡éªŒæ”¯ä»˜æ¸ é“æ˜¯å¦æœ‰æ•ˆ + PayChannelDO channel = channelService.validPayChannel(payApp.getId(), reqDTO.getChannelCode()); + PayClient client = channelService.getPayClient(channel.getId()); + if (client == null) { + log.error("[createTransfer][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", channel.getId()); + throw exception(CHANNEL_NOT_FOUND); + } + // 1.3 校验转账å•å·²ç»å‘起过转账。 + PayTransferDO transfer = validateTransferCanCreate(reqDTO, payApp.getId()); + + if (transfer == null) { + // 2.ä¸å­˜åœ¨åˆ›å»ºè½¬è´¦å•. å¦åˆ™å…许使用相åŒçš„ no 冿¬¡å‘起转账 + String no = noRedisDAO.generate(TRANSFER_NO_PREFIX); + transfer = INSTANCE.convert(reqDTO) + .setChannelId(channel.getId()) + .setNo(no).setStatus(WAITING.getStatus()) + .setNotifyUrl(payApp.getTransferNotifyUrl()) + .setAppId(channel.getAppId()); + transferMapper.insert(transfer); + } + try { + // 3. 调用三方渠é“å‘起转账 + PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer) + .setOutTransferNo(transfer.getNo()); + transferUnifiedReq.setNotifyUrl(genChannelTransferNotifyUrl(channel)); + PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq); + // 4. 通知转账结果 + getSelf().notifyTransfer(channel, unifiedTransferResp); + } catch (Throwable e) { + // 注æ„这里仅打å°å¼‚常,ä¸è¿›è¡ŒæŠ›å‡ºã€‚ + // 原因是:虽然调用支付渠é“进行转账å‘生异常(网络请求超时),实际转账æˆåŠŸã€‚è¿™ä¸ªç»“æžœï¼ŒåŽç»­è½¬è´¦è½®è¯¢å¯ä»¥æ‹¿åˆ°ã€‚ + // æˆ–è€…ä½¿ç”¨ç›¸åŒ no 冿¬¡å‘起转账请求 + log.error("[createTransfer][转账 id({}) requestDTO({}) å‘生异常]", transfer.getId(), reqDTO, e); + } + + return transfer.getId(); + } + + /** + * æ ¹æ®æ”¯ä»˜æ¸ é“的编ç ï¼Œç”Ÿæˆæ”¯ä»˜æ¸ é“çš„å›žè°ƒåœ°å€ + * + * @param channel æ”¯ä»˜æ¸ é“ + * @return 支付渠é“çš„å›žè°ƒåœ°å€ é…ç½®åœ°å€ + "/" + channel id + */ + private String genChannelTransferNotifyUrl(PayChannelDO channel) { + return payProperties.getTransferNotifyUrl() + "/" + channel.getId(); + } + + private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO dto, Long appId) { + PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, dto.getMerchantTransferId()); + if (transfer != null) { + // å·²ç»å­˜åœ¨,并且状æ€ä¸ä¸ºç­‰å¾…状æ€ã€‚说明已ç»è°ƒç”¨æ¸ é“转账并返回结果. + if (!PayTransferStatusEnum.isWaiting(transfer.getStatus())) { + throw exception(PAY_MERCHANT_TRANSFER_EXISTS); + } + if (ObjectUtil.notEqual(dto.getPrice(), transfer.getPrice())) { + throw exception(PAY_SAME_MERCHANT_TRANSFER_PRICE_NOT_MATCH); + } + if (ObjectUtil.notEqual(dto.getType(), transfer.getType())) { + throw exception(PAY_SAME_MERCHANT_TRANSFER_TYPE_NOT_MATCH); + } + } + // 如果状æ€ä¸ºç­‰å¾…状æ€ã€‚ä¸çŸ¥é“渠é“转账是å¦å‘èµ·æˆåŠŸã€‚ å…许使用相åŒçš„ no 冿¬¡å‘起转账,渠é“会ä¿è¯å¹‚ç­‰ + return transfer; + } + + @Transactional(rollbackFor = Exception.class) + // 注æ„,如果是方法内调用该方法,需è¦é€šè¿‡ getSelf().notifyTransfer(channel, notify) 调用,å¦åˆ™äº‹åŠ¡ä¸ç”Ÿæ•ˆ + public void notifyTransfer(PayChannelDO channel, PayTransferRespDTO notify) { + // 转账æˆåŠŸçš„å›žè°ƒ + if (PayTransferStatusRespEnum.isSuccess(notify.getStatus())) { + notifyTransferSuccess(channel, notify); + } + // 转账关闭的回调 + if (PayTransferStatusRespEnum.isClosed(notify.getStatus())) { + notifyTransferClosed(channel, notify); + } + // 转账处ç†ä¸­çš„回调 + if (PayTransferStatusRespEnum.isInProgress(notify.getStatus())) { + notifyTransferInProgress(channel, notify); + } + // WAITING çŠ¶æ€æ— éœ€å¤„ç† + } + + private void notifyTransferInProgress(PayChannelDO channel, PayTransferRespDTO notify) { + // 1.校验 + PayTransferDO transfer = transferMapper.selectByAppIdAndNo(channel.getAppId(), notify.getOutTransferNo()); + if (transfer == null) { + throw exception(PAY_TRANSFER_NOT_FOUND); + } + if (isInProgress(transfer.getStatus())) { // å¦‚æžœå·²ç»æ˜¯è½¬è´¦ä¸­ï¼Œç›´æŽ¥è¿”回,ä¸ç”¨é‡å¤æ›´æ–° + return; + } + if (!isWaiting(transfer.getStatus())) { + throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING); + } + // 2.æ›´æ–° + int updateCounts = transferMapper.updateByIdAndStatus(transfer.getId(), + CollUtil.newArrayList(WAITING.getStatus()), + new PayTransferDO().setStatus(IN_PROGRESS.getStatus())); + if (updateCounts == 0) { + throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING); + } + log.info("[notifyTransferInProgress][transfer({}) 更新为转账进行中状æ€]", transfer.getId()); + } + + + private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) { + // 1.校验 + PayTransferDO transfer = transferMapper.selectByAppIdAndNo(channel.getAppId(), notify.getOutTransferNo()); + if (transfer == null) { + throw exception(PAY_TRANSFER_NOT_FOUND); + } + if (isSuccess(transfer.getStatus())) { // 如果已æˆåŠŸï¼Œç›´æŽ¥è¿”å›žï¼Œä¸ç”¨é‡å¤æ›´æ–° + return; + } + if (!isPendingStatus(transfer.getStatus())) { + throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); + } + // 2.æ›´æ–° + int updateCounts = transferMapper.updateByIdAndStatus(transfer.getId(), + CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()), + new PayTransferDO().setStatus(SUCCESS.getStatus()).setSuccessTime(notify.getSuccessTime()) + .setChannelTransferNo(notify.getChannelTransferNo()) + .setChannelId(channel.getId()).setChannelCode(channel.getCode()) + .setChannelNotifyData(JsonUtils.toJsonString(notify))); + if (updateCounts == 0) { + throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); + } + log.info("[updateTransferSuccess][transfer({}) 更新为已转账]", transfer.getId()); + + // 3. æ’入转账通知记录 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(), + transfer.getId()); + } + + private void notifyTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) { + // 1.校验 + PayTransferDO transfer = transferMapper.selectByAppIdAndNo(channel.getAppId(), notify.getOutTransferNo()); + if (transfer == null) { + throw exception(PAY_TRANSFER_NOT_FOUND); + } + if (isClosed(transfer.getStatus())) { // 如果已是关闭状æ€ï¼Œç›´æŽ¥è¿”回,ä¸ç”¨é‡å¤æ›´æ–° + log.info("[updateTransferClosed][transfer({}) å·²ç»æ˜¯å…³é—­çжæ€ï¼Œæ— éœ€æ›´æ–°]", transfer.getId()); + return; + } + if (!isPendingStatus(transfer.getStatus())) { + throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); + } + + // 2.æ›´æ–° + int updateCount = transferMapper.updateByIdAndStatus(transfer.getId(), + CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()), + new PayTransferDO().setStatus(CLOSED.getStatus()).setChannelId(channel.getId()) + .setChannelCode(channel.getCode()).setChannelTransferNo(notify.getChannelTransferNo()) + .setChannelErrorCode(notify.getChannelErrorCode()).setChannelErrorMsg(notify.getChannelErrorMsg()) + .setChannelNotifyData(JsonUtils.toJsonString(notify))); + if (updateCount == 0) { + throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); + } + log.info("[updateTransferClosed][transfer({}) 更新为关闭状æ€]", transfer.getId()); + + // 3. æ’入转账通知记录 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(), + transfer.getId()); + + } + + @Override + public PayTransferDO getTransfer(Long id) { + return transferMapper.selectById(id); + } + + @Override + public PageResult getTransferPage(PayTransferPageReqVO pageReqVO) { + return transferMapper.selectPage(pageReqVO); + } + + @Override + public int syncTransfer() { + List list = transferMapper.selectListByStatus(WAITING.getStatus()); + if (CollUtil.isEmpty(list)) { + return 0; + } + int count = 0; + for (PayTransferDO transfer : list) { + count += syncTransfer(transfer) ? 1 : 0; + } + return count; + } + + private boolean syncTransfer(PayTransferDO transfer) { + try { + // 1. 查询转账订å•ä¿¡æ¯ + PayClient payClient = channelService.getPayClient(transfer.getChannelId()); + if (payClient == null) { + log.error("[syncTransfer][渠é“ç¼–å·({}) 找ä¸åˆ°å¯¹åº”的支付客户端]", transfer.getChannelId()); + return false; + } + PayTransferRespDTO resp = payClient.getTransfer(transfer.getNo(), + PayTransferTypeEnum.typeOf(transfer.getType())); + + // 2. 回调转账结果 + notifyTransfer(transfer.getChannelId(), resp); + return true; + } catch (Throwable ex) { + log.error("[syncTransfer][transfer({}) åŒæ­¥è½¬è´¦å•状æ€å¼‚常]", transfer.getId(), ex); + return false; + } + } + + public void notifyTransfer(Long channelId, PayTransferRespDTO notify) { + // æ ¡éªŒæ¸ é“æ˜¯å¦æœ‰æ•ˆ + PayChannelDO channel = channelService.validPayChannel(channelId); + // 通知转账结果给对应的业务 + TenantUtils.execute(channel.getTenantId(), () -> getSelf().notifyTransfer(channel, notify)); + } + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private PayTransferServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargePackageService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargePackageService.java new file mode 100644 index 0000000..e2affae --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargePackageService.java @@ -0,0 +1,70 @@ +package com.tashow.cloud.pay.service.wallet; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageCreateReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackagePageReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageUpdateReqVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * é’±åŒ…å……å€¼å¥—é¤ Service æŽ¥å£ + * + * @author jason + */ +public interface PayWalletRechargePackageService { + + /** + * 获å–é’±åŒ…å……å€¼å¥—é¤ + * @param packageId 充值套é¤ç¼–å· + */ + PayWalletRechargePackageDO getWalletRechargePackage(Long packageId); + + /** + * 校验钱包充值套é¤çš„æœ‰æ•ˆæ€§, æ— æ•ˆçš„è¯æŠ›å‡º ServiceException 异常 + * + * @param packageId 充值套é¤ç¼–å· + */ + PayWalletRechargePackageDO validWalletRechargePackage(Long packageId); + + /** + * åˆ›å»ºå……å€¼å¥—é¤ + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createWalletRechargePackage(@Valid WalletRechargePackageCreateReqVO createReqVO); + + /** + * æ›´æ–°å……å€¼å¥—é¤ + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateWalletRechargePackage(@Valid WalletRechargePackageUpdateReqVO updateReqVO); + + /** + * åˆ é™¤å……å€¼å¥—é¤ + * + * @param id ç¼–å· + */ + void deleteWalletRechargePackage(Long id); + + /** + * 获得充值套é¤åˆ†é¡µ + * + * @param pageReqVO 分页查询 + * @return 充值套é¤åˆ†é¡µ + */ + PageResult getWalletRechargePackagePage(WalletRechargePackagePageReqVO pageReqVO); + + /** + * 获得充值套é¤åˆ—表 + * + * @param status çŠ¶æ€ + * @return 充值套é¤åˆ—表 + */ + List getWalletRechargePackageList(Integer status); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargePackageServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargePackageServiceImpl.java new file mode 100644 index 0000000..35211ca --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargePackageServiceImpl.java @@ -0,0 +1,112 @@ +package com.tashow.cloud.pay.service.wallet; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageCreateReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackagePageReqVO; +import com.tashow.cloud.pay.controller.admin.wallet.vo.rechargepackage.WalletRechargePackageUpdateReqVO; +import com.tashow.cloud.pay.convert.wallet.PayWalletRechargePackageConvert; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; +import com.tashow.cloud.pay.dal.mysql.wallet.PayWalletRechargePackageMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; + +/** + * é’±åŒ…å……å€¼å¥—é¤ Service 实现类 + * + * @author jason + */ +@Service +public class PayWalletRechargePackageServiceImpl implements PayWalletRechargePackageService { + + @Resource + private PayWalletRechargePackageMapper walletRechargePackageMapper; + + @Override + public PayWalletRechargePackageDO getWalletRechargePackage(Long packageId) { + return walletRechargePackageMapper.selectById(packageId); + } + + @Override + public PayWalletRechargePackageDO validWalletRechargePackage(Long packageId) { + PayWalletRechargePackageDO rechargePackageDO = walletRechargePackageMapper.selectById(packageId); + if (rechargePackageDO == null) { + throw exception(WALLET_RECHARGE_PACKAGE_NOT_FOUND); + } + if (CommonStatusEnum.DISABLE.getStatus().equals(rechargePackageDO.getStatus())) { + throw exception(WALLET_RECHARGE_PACKAGE_IS_DISABLE); + } + return rechargePackageDO; + } + + @Override + public Long createWalletRechargePackage(WalletRechargePackageCreateReqVO createReqVO) { + // 校验套é¤å是å¦å”¯ä¸€ + validateRechargePackageNameUnique(null, createReqVO.getName()); + + // æ’å…¥ + PayWalletRechargePackageDO walletRechargePackage = PayWalletRechargePackageConvert.INSTANCE.convert(createReqVO); + walletRechargePackageMapper.insert(walletRechargePackage); + // 返回 + return walletRechargePackage.getId(); + } + + @Override + public void updateWalletRechargePackage(WalletRechargePackageUpdateReqVO updateReqVO) { + // 校验存在 + validateWalletRechargePackageExists(updateReqVO.getId()); + // 校验套é¤å是å¦å”¯ä¸€ + validateRechargePackageNameUnique(updateReqVO.getId(), updateReqVO.getName()); + + // æ›´æ–° + PayWalletRechargePackageDO updateObj = PayWalletRechargePackageConvert.INSTANCE.convert(updateReqVO); + walletRechargePackageMapper.updateById(updateObj); + } + + private void validateRechargePackageNameUnique(Long id, String name) { + if (StrUtil.isBlank(name)) { + return; + } + PayWalletRechargePackageDO rechargePackage = walletRechargePackageMapper.selectByName(name); + if (rechargePackage == null) { + return ; + } + if (id == null) { + throw exception(WALLET_RECHARGE_PACKAGE_NAME_EXISTS); + } + if (!id.equals(rechargePackage.getId())) { + throw exception(WALLET_RECHARGE_PACKAGE_NAME_EXISTS); + } + } + + @Override + public void deleteWalletRechargePackage(Long id) { + // 校验存在 + validateWalletRechargePackageExists(id); + // 删除 + walletRechargePackageMapper.deleteById(id); + } + + private void validateWalletRechargePackageExists(Long id) { + if (walletRechargePackageMapper.selectById(id) == null) { + throw exception(WALLET_RECHARGE_PACKAGE_NOT_FOUND); + } + } + + @Override + public PageResult getWalletRechargePackagePage(WalletRechargePackagePageReqVO pageReqVO) { + return walletRechargePackageMapper.selectPage(pageReqVO); + } + + @Override + public List getWalletRechargePackageList(Integer status) { + return walletRechargePackageMapper.selectListByStatus(status); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargeService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargeService.java new file mode 100644 index 0000000..3375e4e --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargeService.java @@ -0,0 +1,63 @@ +package com.tashow.cloud.pay.service.wallet; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargeDO; + +/** + * 钱包充值 Service æŽ¥å£ + * + * @author jason + */ +public interface PayWalletRechargeService { + + /** + * 创建钱包充值记录(å‘起充值) + * + * @param userId 用户 id + * @param userType 用户类型 + * @param createReqVO 钱包充值请求 VO + * @param userIp 用户Ip + * @return 钱包充值记录 + */ + PayWalletRechargeDO createWalletRecharge(Long userId, Integer userType, String userIp, + AppPayWalletRechargeCreateReqVO createReqVO); + + /** + * 获得钱包充值记录分页 + * + * @param userId ç”¨æˆ·ç¼–å· + * @param userType 用户类型 + * @param pageReqVO 分页请求 + * @param payStatus æ˜¯å¦æ”¯ä»˜ + * @return 钱包充值记录分页 + */ + PageResult getWalletRechargePackagePage(Long userId, Integer userType, + PageParam pageReqVO, Boolean payStatus); + + /** + * 更新钱包充值æˆåŠŸ + * + * @param id 钱包充值记录 id + * @param payOrderId æ”¯ä»˜è®¢å• id + */ + void updateWalletRechargerPaid(Long id, Long payOrderId); + + /** + * å‘起钱包充值退款 + * + * @param id é’±åŒ…å……å€¼ç¼–å· + * @param userIp 用户 ip åœ°å€ + */ + void refundWalletRecharge(Long id, String userIp); + + /** + * 更新钱包充值记录为已退款 + * + * @param id 钱包充值 id + * @param payRefundId 退款å•id + */ + void updateWalletRechargeRefunded(Long id, Long payRefundId); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargeServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargeServiceImpl.java new file mode 100644 index 0000000..d621121 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletRechargeServiceImpl.java @@ -0,0 +1,328 @@ +package com.tashow.cloud.pay.service.wallet; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import com.tashow.cloud.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargeDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; +import com.tashow.cloud.pay.dal.mysql.wallet.PayWalletRechargeMapper; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import com.tashow.cloud.pay.framework.pay.config.PayProperties; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Objects; + +import static cn.hutool.core.util.ObjectUtil.notEqual; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.fenToYuanStr; +import static com.tashow.cloud.pay.convert.wallet.PayWalletRechargeConvert.INSTANCE; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.pay.enums.MessageTemplateConstants.WXA_WALLET_RECHARGER_PAID; +import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*; + +/** + * 钱包充值 Service 实现类 + * + * @author jason + */ +@Service +@Slf4j +public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { + + private static final String WALLET_RECHARGE_ORDER_SUBJECT = "钱包余é¢å……值"; + + @Resource + private PayWalletRechargeMapper walletRechargeMapper; + @Resource + private PayWalletService payWalletService; + @Resource + private PayOrderService payOrderService; + @Resource + private PayRefundService payRefundService; + @Resource + private PayWalletRechargePackageService payWalletRechargePackageService; + + @Resource + public SocialClientApi socialClientApi; + + @Resource + private PayProperties payProperties; + + @Override + @Transactional(rollbackFor = Exception.class) + public PayWalletRechargeDO createWalletRecharge(Long userId, Integer userType, String userIp, + AppPayWalletRechargeCreateReqVO reqVO) { + // 1.1 è®¡ç®—å……å€¼é‡‘é¢ + int payPrice; + int bonusPrice = 0; + if (Objects.nonNull(reqVO.getPackageId())) { + PayWalletRechargePackageDO rechargePackage = payWalletRechargePackageService.validWalletRechargePackage(reqVO.getPackageId()); + payPrice = rechargePackage.getPayPrice(); + bonusPrice = rechargePackage.getBonusPrice(); + } else { + payPrice = reqVO.getPayPrice(); + } + // 1.2 æ’入充值记录 + PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType); + PayWalletRechargeDO recharge = INSTANCE.convert(wallet.getId(), payPrice, bonusPrice, reqVO.getPackageId()); + walletRechargeMapper.insert(recharge); + + // 2.1 åˆ›å»ºæ”¯ä»˜å• + Long payOrderId = payOrderService.createOrder(new PayOrderCreateReqDTO() + .setAppKey(payProperties.getWalletPayAppKey()).setUserIp(userIp) + .setMerchantOrderId(recharge.getId().toString()) // 业务的订å•ç¼–å· + .setSubject(WALLET_RECHARGE_ORDER_SUBJECT).setBody("") + .setPrice(recharge.getPayPrice()) + .setExpireTime(addTime(Duration.ofHours(2L)))); // TODO @芋艿:支付超时时间 + // 2.2 æ›´æ–°é’±åŒ…å……å€¼è®°å½•ä¸­æ”¯ä»˜è®¢å• + walletRechargeMapper.updateById(new PayWalletRechargeDO().setId(recharge.getId()).setPayOrderId(payOrderId)); + recharge.setPayOrderId(payOrderId); + return recharge; + } + + @Override + public PageResult getWalletRechargePackagePage(Long userId, Integer userType, + PageParam pageReqVO, Boolean payStatus) { + PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType); + return walletRechargeMapper.selectPage(pageReqVO, wallet.getId(), payStatus); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateWalletRechargerPaid(Long id, Long payOrderId) { + // 1.1 校验钱包充值是å¦å­˜åœ¨ + PayWalletRechargeDO recharge = walletRechargeMapper.selectById(id); + if (recharge == null) { + log.error("[updateWalletRechargerPaid][recharge({}) payOrder({}) ä¸å­˜åœ¨å……值订å•,请进行处ç†ï¼]", id, payOrderId); + throw exception(WALLET_RECHARGE_NOT_FOUND); + } + // 1.2 校验钱包充值是å¦å¯ä»¥æ”¯ä»˜ + if (recharge.getPayStatus()) { + // 特殊:如果订å•已支付,且支付å•å·ç›¸åŒï¼Œç›´æŽ¥è¿”回,说明é‡å¤å›žè°ƒ + if (ObjectUtil.equals(recharge.getPayOrderId(), payOrderId)) { + log.warn("[updateWalletRechargerPaid][recharge({}) 已支付,且支付å•å·ç›¸åŒ({}),直接返回]", recharge, payOrderId); + return; + } + // 异常:支付å•å·ä¸åŒï¼Œè¯´æ˜Žæ”¯ä»˜å•å·é”™è¯¯ + log.error("[updateWalletRechargerPaid][recharge({}) 已支付,但是支付å•å·ä¸åŒ({}),请进行处ç†ï¼]", recharge, payOrderId); + throw exception(WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR); + } + + // 2. 校验支付订å•çš„åˆæ³•性 + PayOrderDO payOrderDO = validatePayOrderPaid(recharge, payOrderId); + + // 3. æ›´æ–°é’±åŒ…å……å€¼çš„æ”¯ä»˜çŠ¶æ€ + int updateCount = walletRechargeMapper.updateByIdAndPaid(id, false, + new PayWalletRechargeDO().setId(id).setPayStatus(true).setPayTime(LocalDateTime.now()) + .setPayChannelCode(payOrderDO.getChannelCode())); + if (updateCount == 0) { + throw exception(WALLET_RECHARGE_UPDATE_PAID_STATUS_NOT_UNPAID); + } + + // 4. æ›´æ–°é’±åŒ…ä½™é¢ + // TODO @jason:这样的è¯ï¼Œæœªæ¥æçŽ°ä¼šä¸ä¼šæŠŠå……值的,也æçŽ°èµ°å“ˆã€‚ç±»ä¼¼å…ˆå…… 100ï¼Œé€ 110ï¼›ç„¶åŽæçŽ° 110ï¼› + // TODO 需è¦é’±åŒ…ä¸­åŠ ä¸ªå¯æçŽ°ä½™é¢ + payWalletService.addWalletBalance(recharge.getWalletId(), String.valueOf(id), + PayWalletBizTypeEnum.RECHARGE, recharge.getTotalPrice()); + + // 5. å‘é€è®¢é˜…æ¶ˆæ¯ + getSelf().sendWalletRechargerPaidMessage(payOrderId, recharge); + } + + @Async + public void sendWalletRechargerPaidMessage(Long payOrderId, PayWalletRechargeDO walletRecharge) { + // 1. èŽ·å¾—ä¼šå‘˜é’±åŒ…ä¿¡æ¯ + PayWalletDO wallet = payWalletService.getWallet(walletRecharge.getWalletId()); + // 2. 构建并å‘逿¨¡ç‰ˆæ¶ˆæ¯ + socialClientApi.sendWxaSubscribeMessage(new SocialWxaSubscribeMessageSendReqDTO() + .setUserId(wallet.getUserId()).setUserType(wallet.getUserType()) + .setTemplateTitle(WXA_WALLET_RECHARGER_PAID) + .setPage("pages/user/wallet/money") // é’±åŒ…è¯¦æƒ…ç•Œé¢ + .addMessage("character_string1", String.valueOf(payOrderId)) // 支付å•ç¼–å· + .addMessage("amount2", fenToYuanStr(walletRecharge.getTotalPrice())) // å……å€¼é‡‘é¢ + .addMessage("time3", LocalDateTimeUtil.formatNormal(walletRecharge.getCreateTime())) // 充值时间 + .addMessage("phrase4", "充值æˆåŠŸ")).checkError(); // å……å€¼çŠ¶æ€ + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void refundWalletRecharge(Long id, String userIp) { + // 1.1 获å–钱包充值记录 + PayWalletRechargeDO walletRecharge = walletRechargeMapper.selectById(id); + if (walletRecharge == null) { + log.error("[refundWalletRecharge][钱包充值记录ä¸å­˜åœ¨ï¼Œé’±åŒ…充值记录 id({})]", id); + throw exception(WALLET_RECHARGE_NOT_FOUND); + } + // 1.2 校验钱包充值是å¦å¯ä»¥å‘起退款 + PayWalletDO wallet = validateWalletRechargeCanRefund(walletRecharge); + + // 2. 冻结退款的余é¢ï¼Œæš‚æ—¶åªå¤„ç†èµ é€çš„ä½™é¢ä¹Ÿå…¨éƒ¨é€€å›ž + payWalletService.freezePrice(wallet.getId(), walletRecharge.getTotalPrice()); + + // 3. åˆ›å»ºé€€æ¬¾å• + String walletRechargeId = String.valueOf(id); + String refundId = walletRechargeId + "-refund"; + Long payRefundId = payRefundService.createPayRefund(new PayRefundCreateReqDTO() + .setAppKey(payProperties.getWalletPayAppKey()).setUserIp(userIp) + .setMerchantOrderId(walletRechargeId) + .setMerchantRefundId(refundId) + .setReason("想退钱").setPrice(walletRecharge.getPayPrice())); + + // 4. 更新充值记录退款å•å· + walletRechargeMapper.updateById(new PayWalletRechargeDO().setPayRefundId(payRefundId) + .setRefundStatus(WAITING.getStatus()).setId(walletRecharge.getId())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateWalletRechargeRefunded(Long id, Long payRefundId) { + // 1.1 获å–钱包充值记录 + PayWalletRechargeDO walletRecharge = walletRechargeMapper.selectById(id); + if (walletRecharge == null) { + log.error("[updateWalletRechargerPaid][钱包充值记录ä¸å­˜åœ¨ï¼Œé’±åŒ…充值记录 id({})]", id); + throw exception(WALLET_RECHARGE_NOT_FOUND); + } + // 1.2 校验钱包充值是å¦å¯ä»¥æ›´æ–°å·²é€€æ¬¾ + PayRefundDO payRefund = validateWalletRechargeCanRefunded(walletRecharge, payRefundId); + + PayWalletRechargeDO updateObj = new PayWalletRechargeDO().setId(id); + // 退款æˆåŠŸ + if (PayRefundStatusEnum.isSuccess(payRefund.getStatus())) { + // 2.1 æ›´æ–°é’±åŒ…ä½™é¢ + payWalletService.reduceWalletBalance(walletRecharge.getWalletId(), id, + PayWalletBizTypeEnum.RECHARGE_REFUND, walletRecharge.getTotalPrice()); + + updateObj.setRefundStatus(SUCCESS.getStatus()).setRefundTime(payRefund.getSuccessTime()) + .setRefundTotalPrice(walletRecharge.getTotalPrice()).setRefundPayPrice(walletRecharge.getPayPrice()) + .setRefundBonusPrice(walletRecharge.getBonusPrice()); + } + // 退款失败 + if (PayRefundStatusRespEnum.isFailure(payRefund.getStatus())) { + // 2.2 è§£å†»ä½™é¢ + payWalletService.unfreezePrice(walletRecharge.getWalletId(), walletRecharge.getTotalPrice()); + + updateObj.setRefundStatus(FAILURE.getStatus()); + } + // 3. 更新钱包充值的退款字段 + walletRechargeMapper.updateByIdAndRefunded(id, WAITING.getStatus(), updateObj); + } + + private PayRefundDO validateWalletRechargeCanRefunded(PayWalletRechargeDO walletRecharge, Long payRefundId) { + // 1. 校验退款订å•åŒ¹é… + if (notEqual(walletRecharge.getPayRefundId(), payRefundId)) { + log.error("[validateWalletRechargeCanRefunded][钱包充值({}) 退款å•ä¸åŒ¹é…({}),请进行处ç†ï¼é’±åŒ…å……å€¼çš„æ•°æ®æ˜¯ï¼š{}]", + walletRecharge.getId(), payRefundId, toJsonString(walletRecharge)); + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR); + } + + // 2.1 æ ¡éªŒé€€æ¬¾è®¢å• + PayRefundDO payRefund = payRefundService.getRefund(payRefundId); + if (payRefund == null) { + log.error("[validateWalletRechargeCanRefunded][payRefund({})ä¸å­˜åœ¨]", payRefundId); + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND); + } + // 2.2 校验退款金é¢ä¸€è‡´ + if (notEqual(payRefund.getRefundPrice(), walletRecharge.getPayPrice())) { + log.error("[validateWalletRechargeCanRefunded][钱包({}) payRefund({}) 退款金é¢ä¸åŒ¹é…,请进行处ç†ï¼é’±åŒ…æ•°æ®æ˜¯ï¼š{},payRefund æ•°æ®æ˜¯ï¼š{}]", + walletRecharge.getId(), payRefundId, toJsonString(walletRecharge), toJsonString(payRefund)); + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH); + } + // 2.3 校验退款订å•å•†æˆ·è®¢å•æ˜¯å¦åŒ¹é… + if (notEqual(payRefund.getMerchantOrderId(), walletRecharge.getId().toString())) { + log.error("[validateWalletRechargeCanRefunded][钱包({}) 退款å•ä¸åŒ¹é…({}),请进行处ç†ï¼payRefund æ•°æ®æ˜¯ï¼š{}]", + walletRecharge.getId(), payRefundId, toJsonString(payRefund)); + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR); + } + return payRefund; + } + + private PayWalletDO validateWalletRechargeCanRefund(PayWalletRechargeDO walletRecharge) { + // æ ¡éªŒå……å€¼è®¢å•æ˜¯å¦æ”¯ä»˜ + if (!walletRecharge.getPayStatus()) { + throw exception(WALLET_RECHARGE_REFUND_FAIL_NOT_PAID); + } + // æ ¡éªŒå……å€¼è®¢å•æ˜¯å¦å·²é€€æ¬¾ + if (walletRecharge.getPayRefundId() != null) { + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUNDED); + } + // æ ¡éªŒé’±åŒ…ä½™é¢æ˜¯å¦è¶³å¤Ÿ + PayWalletDO wallet = payWalletService.getWallet(walletRecharge.getWalletId()); + Assert.notNull(wallet, "用户钱包({}) ä¸å­˜åœ¨", wallet.getId()); + if (wallet.getBalance() < walletRecharge.getTotalPrice()) { + throw exception(WALLET_RECHARGE_REFUND_BALANCE_NOT_ENOUGH); + } + // TODO @芋艿:需è¦è€ƒè™‘下,赠é€çš„金é¢ï¼Œä¼šä¸ä¼šå¯¼è‡´æçŽ°è¶…è¿‡ï¼› + return wallet; + } + + /** + * 校验支付订å•çš„åˆæ³•性 + * + * @param recharge å……å€¼è®¢å• + * @param payOrderId 支付订å•ç¼–å· + * @return æ”¯ä»˜è®¢å• + */ + private PayOrderDO validatePayOrderPaid(PayWalletRechargeDO recharge, Long payOrderId) { + // 1. æ ¡éªŒæ”¯ä»˜å•æ˜¯å¦å­˜åœ¨ + PayOrderDO payOrder = payOrderService.getOrder(payOrderId); + if (payOrder == null) { + log.error("[validatePayOrderPaid][充值订å•({}) payOrder({}) ä¸å­˜åœ¨ï¼Œè¯·è¿›è¡Œå¤„ç†ï¼]", + recharge.getId(), payOrderId); + throw exception(PAY_ORDER_NOT_FOUND); + } + + // 2.1 校验支付å•已支付 + if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) { + log.error("[validatePayOrderPaid][充值订å•({}) payOrder({}) 未支付,请进行处ç†ï¼payOrder æ•°æ®æ˜¯ï¼š{}]", + recharge.getId(), payOrderId, toJsonString(payOrder)); + throw exception(WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS); + } + // 2.2 校验支付金é¢ä¸€è‡´ + if (notEqual(payOrder.getPrice(), recharge.getPayPrice())) { + log.error("[validatePayOrderPaid][充值订å•({}) payOrder({}) 支付金é¢ä¸åŒ¹é…,请进行处ç†ï¼é’±åŒ… æ•°æ®æ˜¯ï¼š{},payOrder æ•°æ®æ˜¯ï¼š{}]", + recharge.getId(), payOrderId, toJsonString(recharge), toJsonString(payOrder)); + throw exception(WALLET_RECHARGE_UPDATE_PAID_PAY_PRICE_NOT_MATCH); + } + // 2.3 校验支付订å•的商户订å•åŒ¹é… + if (notEqual(payOrder.getMerchantOrderId(), recharge.getId().toString())) { + log.error("[validatePayOrderPaid][充值订å•({}) 支付å•ä¸åŒ¹é…({}),请进行处ç†ï¼payOrder æ•°æ®æ˜¯ï¼š{}]", + recharge.getId(), payOrderId, toJsonString(payOrder)); + throw exception(WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR); + } + return payOrder; + } + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private PayWalletRechargeServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletService.java new file mode 100644 index 0000000..214c2c6 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletService.java @@ -0,0 +1,100 @@ +package com.tashow.cloud.pay.service.wallet; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.wallet.PayWalletPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletTransactionDO; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; + +/** + * 钱包 Service æŽ¥å£ + * + * @author jason + */ +public interface PayWalletService { + + /** + * 获å–é’±åŒ…ä¿¡æ¯ + *

+ * 如果ä¸å­˜åœ¨ï¼Œåˆ™åˆ›å»ºé’±åŒ…。由于用户注册时候ä¸ä¼šåˆ›å»ºé’±åŒ… + * + * @param userId ç”¨æˆ·ç¼–å· + * @param userType 用户类型 + */ + PayWalletDO getOrCreateWallet(Long userId, Integer userType); + + /** + * 获å–é’±åŒ…ä¿¡æ¯ + * + * @param walletId 钱包 id + */ + PayWalletDO getWallet(Long walletId); + + /** + * 获得会员钱包分页 + * + * @param pageReqVO 分页查询 + * @return 会员钱包分页 + */ + PageResult getWalletPage(PayWalletPageReqVO pageReqVO); + + /** + * é’±åŒ…è®¢å•æ”¯ä»˜ + * + * @param userId 用户 id + * @param userType 用户类型 + * @param outTradeNo 外部订å•å· + * @param price é‡‘é¢ + */ + PayWalletTransactionDO orderPay(Long userId, Integer userType, String outTradeNo, Integer price); + + /** + * é’±åŒ…è®¢å•æ”¯ä»˜é€€æ¬¾ + * + * @param outRefundNo å¤–éƒ¨é€€æ¬¾å· + * @param refundPrice é€€æ¬¾é‡‘é¢ + * @param reason 退款原因 + */ + PayWalletTransactionDO orderRefund(String outRefundNo, Integer refundPrice, String reason); + + /** + * 扣å‡é’±åŒ…ä½™é¢ + * + * @param walletId 钱包 id + * @param bizId ä¸šåŠ¡å…³è” id + * @param bizType 业务关è”分类 + * @param price 扣å‡é‡‘é¢ + * @return é’±åŒ…æµæ°´ + */ + PayWalletTransactionDO reduceWalletBalance(Long walletId, Long bizId, + PayWalletBizTypeEnum bizType, Integer price); + + /** + * å¢žåŠ é’±åŒ…ä½™é¢ + * + * @param walletId 钱包 id + * @param bizId ä¸šåŠ¡å…³è” id + * @param bizType 业务关è”分类 + * @param price å¢žåŠ é‡‘é¢ + * @return é’±åŒ…æµæ°´ + */ + PayWalletTransactionDO addWalletBalance(Long walletId, String bizId, + PayWalletBizTypeEnum bizType, Integer price); + + /** + * å†»ç»“é’±åŒ…éƒ¨åˆ†ä½™é¢ + * + * @param id é’±åŒ…ç¼–å· + * @param price å†»ç»“é‡‘é¢ + */ + void freezePrice(Long id, Integer price); + + /** + * è§£å†»é’±åŒ…ä½™é¢ + * + * @param id é’±åŒ…ç¼–å· + * @param price è§£å†»é‡‘é¢ + */ + void unfreezePrice(Long id, Integer price); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletServiceImpl.java new file mode 100644 index 0000000..839c572 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletServiceImpl.java @@ -0,0 +1,233 @@ +package com.tashow.cloud.pay.service.wallet; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import com.tashow.cloud.pay.controller.admin.wallet.vo.wallet.PayWalletPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderExtensionDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletTransactionDO; +import com.tashow.cloud.pay.dal.mysql.wallet.PayWalletMapper; +import com.tashow.cloud.pay.dal.redis.wallet.PayWalletLockRedisDAO; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import com.tashow.cloud.pay.service.wallet.bo.WalletTransactionCreateReqBO; +import jakarta.annotation.Resource; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum.PAYMENT; +import static cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum.PAYMENT_REFUND; + +/** + * 钱包 Service 实现类 + * + * @author jason + */ +@Service +@Slf4j +public class PayWalletServiceImpl implements PayWalletService { + + /** + * 通知超时时间,å•ä½ï¼šæ¯«ç§’ + */ + public static final long UPDATE_TIMEOUT_MILLIS = 120 * DateUtils.SECOND_MILLIS; + + @Resource + private PayWalletMapper walletMapper; + @Resource + private PayWalletLockRedisDAO lockRedisDAO; + + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ– + private PayWalletTransactionService walletTransactionService; + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ– + private PayOrderService orderService; + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ– + private PayRefundService refundService; + + @Override + public PayWalletDO getOrCreateWallet(Long userId, Integer userType) { + PayWalletDO wallet = walletMapper.selectByUserIdAndType(userId, userType); + if (wallet == null) { + wallet = new PayWalletDO().setUserId(userId).setUserType(userType) + .setBalance(0).setTotalExpense(0).setTotalRecharge(0); + wallet.setCreateTime(LocalDateTime.now()); + walletMapper.insert(wallet); + } + return wallet; + } + + @Override + public PayWalletDO getWallet(Long walletId) { + return walletMapper.selectById(walletId); + } + + @Override + public PageResult getWalletPage(PayWalletPageReqVO pageReqVO) { + return walletMapper.selectPage(pageReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public PayWalletTransactionDO orderPay(Long userId, Integer userType, String outTradeNo, Integer price) { + // 1. åˆ¤æ–­æ”¯ä»˜äº¤æ˜“æ‹“å±•å•æ˜¯å¦å­˜ + PayOrderExtensionDO orderExtension = orderService.getOrderExtensionByNo(outTradeNo); + if (orderExtension == null) { + throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); + } + PayWalletDO wallet = getOrCreateWallet(userId, userType); + // 2. 扣å‡ä½™é¢ + return reduceWalletBalance(wallet.getId(), orderExtension.getOrderId(), PAYMENT, price); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public PayWalletTransactionDO orderRefund(String outRefundNo, Integer refundPrice, String reason) { + // 1.1 åˆ¤æ–­é€€æ¬¾å•æ˜¯å¦å­˜åœ¨ + PayRefundDO payRefund = refundService.getRefundByNo(outRefundNo); + if (payRefund == null) { + throw exception(REFUND_NOT_FOUND); + } + // 1.2 校验是å¦å¯ä»¥é€€æ¬¾ + Long walletId = validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo()); + PayWalletDO wallet = walletMapper.selectById(walletId); + Assert.notNull(wallet, "钱包 {} ä¸å­˜åœ¨", walletId); + + // 2. å¢žåŠ ä½™é¢ + return addWalletBalance(walletId, String.valueOf(payRefund.getId()), PAYMENT_REFUND, refundPrice); + } + + /** + * 校验是å¦èƒ½é€€æ¬¾ + * + * @param refundId æ”¯ä»˜é€€æ¬¾å• id + * @param walletPayNo 钱包支付 no + */ + private Long validateWalletCanRefund(Long refundId, String walletPayNo) { + // 1. 校验钱包支付交易存在 + PayWalletTransactionDO walletTransaction = walletTransactionService.getWalletTransactionByNo(walletPayNo); + if (walletTransaction == null) { + throw exception(WALLET_TRANSACTION_NOT_FOUND); + } + // 2. 校验退款是å¦å­˜åœ¨ + PayWalletTransactionDO refundTransaction = walletTransactionService.getWalletTransaction( + String.valueOf(refundId), PAYMENT_REFUND); + if (refundTransaction != null) { + throw exception(WALLET_REFUND_EXIST); + } + return walletTransaction.getWalletId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @SneakyThrows + public PayWalletTransactionDO reduceWalletBalance(Long walletId, Long bizId, + PayWalletBizTypeEnum bizType, Integer price) { + // 1. 获å–钱包 + PayWalletDO payWallet = getWallet(walletId); + if (payWallet == null) { + log.error("[reduceWalletBalance][用户钱包({})ä¸å­˜åœ¨]", walletId); + throw exception(WALLET_NOT_FOUND); + } + + // 2. 加é”,更新钱包余é¢ï¼ˆç›®çš„:é¿å…é’±åŒ…æµæ°´çš„并呿›´æ–°æ—¶ï¼Œä½™é¢å˜åŒ–ä¸è¿žè´¯ï¼‰ + return lockRedisDAO.lock(walletId, UPDATE_TIMEOUT_MILLIS, () -> { + // 2. æ‰£é™¤ä½™é¢ + int updateCounts; + switch (bizType) { + case PAYMENT: { + updateCounts = walletMapper.updateWhenConsumption(payWallet.getId(), price); + break; + } + case RECHARGE_REFUND: { + updateCounts = walletMapper.updateWhenRechargeRefund(payWallet.getId(), price); + break; + } + default: { + // TODO 其它类型待实现 + throw new UnsupportedOperationException("待实现"); + } + } + if (updateCounts == 0) { + throw exception(WALLET_BALANCE_NOT_ENOUGH); + } + + // 3. 生æˆé’±åŒ…æµæ°´ + Integer afterBalance = payWallet.getBalance() - price; + WalletTransactionCreateReqBO bo = new WalletTransactionCreateReqBO().setWalletId(payWallet.getId()) + .setPrice(-price).setBalance(afterBalance).setBizId(String.valueOf(bizId)) + .setBizType(bizType.getType()).setTitle(bizType.getDescription()); + return walletTransactionService.createWalletTransaction(bo); + }); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @SneakyThrows + public PayWalletTransactionDO addWalletBalance(Long walletId, String bizId, + PayWalletBizTypeEnum bizType, Integer price) { + // 1. 获å–钱包 + PayWalletDO payWallet = getWallet(walletId); + if (payWallet == null) { + log.error("[addWalletBalance][用户钱包({})ä¸å­˜åœ¨]", walletId); + throw exception(WALLET_NOT_FOUND); + } + + // 2. 加é”,更新钱包余é¢ï¼ˆç›®çš„:é¿å…é’±åŒ…æµæ°´çš„并呿›´æ–°æ—¶ï¼Œä½™é¢å˜åŒ–ä¸è¿žè´¯ï¼‰ + return lockRedisDAO.lock(walletId, UPDATE_TIMEOUT_MILLIS, () -> { + // 3. æ›´æ–°é’±åŒ…é‡‘é¢ + switch (bizType) { + case PAYMENT_REFUND: { // 退款更新 + walletMapper.updateWhenConsumptionRefund(payWallet.getId(), price); + break; + } + case RECHARGE: { // 充值更新 + walletMapper.updateWhenRecharge(payWallet.getId(), price); + break; + } + case UPDATE_BALANCE: // æ›´æ–°ä½™é¢ + case BROKERAGE_WITHDRAW: // 分佣æçް + walletMapper.updateWhenAdd(payWallet.getId(), price); + break; + default: { + throw new UnsupportedOperationException("待实现:" + bizType); + } + } + + // 4. 生æˆé’±åŒ…æµæ°´ + WalletTransactionCreateReqBO transactionCreateReqBO = new WalletTransactionCreateReqBO() + .setWalletId(payWallet.getId()).setPrice(price).setBalance(payWallet.getBalance() + price) + .setBizId(bizId).setBizType(bizType.getType()).setTitle(bizType.getDescription()); + return walletTransactionService.createWalletTransaction(transactionCreateReqBO); + }); + } + + @Override + public void freezePrice(Long id, Integer price) { + int updateCounts = walletMapper.freezePrice(id, price); + if (updateCounts == 0) { + throw exception(WALLET_BALANCE_NOT_ENOUGH); + } + } + + @Override + public void unfreezePrice(Long id, Integer price) { + int updateCounts = walletMapper.unFreezePrice(id, price); + if (updateCounts == 0) { + throw exception(WALLET_FREEZE_PRICE_NOT_ENOUGH); + } + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletTransactionService.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletTransactionService.java new file mode 100644 index 0000000..848a05b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletTransactionService.java @@ -0,0 +1,73 @@ +package com.tashow.cloud.pay.service.wallet; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.transaction.PayWalletTransactionPageReqVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionSummaryRespVO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletTransactionDO; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import com.tashow.cloud.pay.service.wallet.bo.WalletTransactionCreateReqBO; +import jakarta.validation.Valid; + +import java.time.LocalDateTime; + +/** + * é’±åŒ…ä½™é¢æµæ°´ Service æŽ¥å£ + * + * @author jason + */ +public interface PayWalletTransactionService { + + /** + * æŸ¥è¯¢é’±åŒ…ä½™é¢æµæ°´åˆ†é¡µ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param userType 用户类型 + * @param pageVO åˆ†é¡µæŸ¥è¯¢å‚æ•° + */ + PageResult getWalletTransactionPage(Long userId, Integer userType, + AppPayWalletTransactionPageReqVO pageVO); + + /** + * æŸ¥è¯¢é’±åŒ…ä½™é¢æµæ°´åˆ†é¡µ + * + * @param pageVO åˆ†é¡µæŸ¥è¯¢å‚æ•° + */ + PageResult getWalletTransactionPage(PayWalletTransactionPageReqVO pageVO); + + /** + * æ–°å¢žé’±åŒ…ä½™é¢æµæ°´ + * + * @param bo åˆ›å»ºé’±åŒ…æµæ°´ bo + * @return 新建的钱包 do + */ + PayWalletTransactionDO createWalletTransaction(@Valid WalletTransactionCreateReqBO bo); + + /** + * æ ¹æ® no,获å–é’±åŒ…ä½™æµæ°´ + * + * @param no æµæ°´å· + */ + PayWalletTransactionDO getWalletTransactionByNo(String no); + + /** + * 获å–é’±åŒ…æµæ°´ + * + * @param bizId ä¸šåŠ¡ç¼–å· + * @param type 业务类型 + * @return é’±åŒ…æµæ°´ + */ + PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type); + + /** + * èŽ·å¾—é’±åŒ…æµæ°´ç»Ÿè®¡ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param userType 用户类型 + * @param createTime 时间段 + * @return é’±åŒ…æµæ°´ç»Ÿè®¡ + */ + AppPayWalletTransactionSummaryRespVO getWalletTransactionSummary(Long userId, Integer userType, + LocalDateTime[] createTime); + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletTransactionServiceImpl.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletTransactionServiceImpl.java new file mode 100644 index 0000000..e8022fe --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/PayWalletTransactionServiceImpl.java @@ -0,0 +1,95 @@ +package com.tashow.cloud.pay.service.wallet; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.pay.controller.admin.wallet.vo.transaction.PayWalletTransactionPageReqVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO; +import com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionSummaryRespVO; +import com.tashow.cloud.pay.convert.wallet.PayWalletTransactionConvert; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletDO; +import com.tashow.cloud.pay.dal.dataobject.wallet.PayWalletTransactionDO; +import com.tashow.cloud.pay.dal.mysql.wallet.PayWalletTransactionMapper; +import com.tashow.cloud.pay.dal.redis.no.PayNoRedisDAO; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import com.tashow.cloud.pay.service.wallet.bo.WalletTransactionCreateReqBO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; + +import static com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO.TYPE_EXPENSE; +import static com.tashow.cloud.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO.TYPE_INCOME; + +/** + * é’±åŒ…æµæ°´ Service 实现类 + * + * @author jason + */ +@Service +@Slf4j +@Validated +public class PayWalletTransactionServiceImpl implements PayWalletTransactionService { + + /** + * é’±åŒ…æµæ°´çš„ no å‰ç¼€ + */ + private static final String WALLET_NO_PREFIX = "W"; + + @Resource + private PayWalletService payWalletService; + @Resource + private PayWalletTransactionMapper payWalletTransactionMapper; + @Resource + private PayNoRedisDAO noRedisDAO; + + @Override + public PageResult getWalletTransactionPage(Long userId, Integer userType, + AppPayWalletTransactionPageReqVO pageVO) { + PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType); + return payWalletTransactionMapper.selectPage(wallet.getId(), pageVO.getType(), pageVO, pageVO.getCreateTime()); + } + + @Override + public PageResult getWalletTransactionPage(PayWalletTransactionPageReqVO pageVO) { + // 基于 userId + userType 查询钱包 + if (pageVO.getWalletId() == null + && ObjectUtil.isAllNotEmpty(pageVO.getUserId(), pageVO.getUserType())) { + PayWalletDO wallet = payWalletService.getOrCreateWallet(pageVO.getUserId(), pageVO.getUserType()); + if (wallet != null) { + pageVO.setWalletId(wallet.getId()); + } + } + + // 查询分页 + return payWalletTransactionMapper.selectPage(pageVO.getWalletId(), null, pageVO, null); + } + + @Override + public PayWalletTransactionDO createWalletTransaction(WalletTransactionCreateReqBO bo) { + PayWalletTransactionDO transaction = PayWalletTransactionConvert.INSTANCE.convert(bo) + .setNo(noRedisDAO.generate(WALLET_NO_PREFIX)); + payWalletTransactionMapper.insert(transaction); + return transaction; + } + + @Override + public PayWalletTransactionDO getWalletTransactionByNo(String no) { + return payWalletTransactionMapper.selectByNo(no); + } + + @Override + public PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type) { + return payWalletTransactionMapper.selectByBiz(bizId, type.getType()); + } + + @Override + public AppPayWalletTransactionSummaryRespVO getWalletTransactionSummary(Long userId, Integer userType, LocalDateTime[] createTime) { + PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType); + return new AppPayWalletTransactionSummaryRespVO() + .setTotalExpense(payWalletTransactionMapper.selectPriceSum(wallet.getId(), TYPE_EXPENSE, createTime)) + .setTotalIncome(payWalletTransactionMapper.selectPriceSum(wallet.getId(), TYPE_INCOME, createTime)); + } + +} diff --git a/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/bo/WalletTransactionCreateReqBO.java b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/bo/WalletTransactionCreateReqBO.java new file mode 100644 index 0000000..cdadac6 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/java/com/tashow/cloud/pay/service/wallet/bo/WalletTransactionCreateReqBO.java @@ -0,0 +1,58 @@ +package com.tashow.cloud.pay.service.wallet.bo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * åˆ›å»ºé’±åŒ…æµæ°´ BO + * + * @author jason + */ +@Data +public class WalletTransactionCreateReqBO { + + /** + * é’±åŒ…ç¼–å· + * + */ + @NotNull(message = "钱包编å·ä¸èƒ½ä¸ºç©º") + private Long walletId; + + /** + * 交易金é¢ï¼Œå•ä½åˆ† + * + * 正值表示余é¢å¢žåŠ ï¼Œè´Ÿå€¼è¡¨ç¤ºä½™é¢å‡å°‘ + */ + @NotNull(message = "交易金é¢ä¸èƒ½ä¸ºç©º") + private Integer price; + + /** + * 交易åŽä½™é¢ï¼Œå•ä½åˆ† + */ + @NotNull(message = "交易åŽä½™é¢ä¸èƒ½ä¸ºç©º") + private Integer balance; + + /** + * å…³è”业务分类 + * + * 枚举 {@link PayWalletBizTypeEnum#getType()} + */ + @NotNull(message = "å…³è”业务分类ä¸èƒ½ä¸ºç©º") + @InEnum(PayWalletBizTypeEnum.class) + private Integer bizType; + + /** + * å…³è”ä¸šåŠ¡ç¼–å· + */ + @NotEmpty(message = "å…³è”业务编å·ä¸èƒ½ä¸ºç©º") + private String bizId; + + /** + * æµæ°´è¯´æ˜Ž + */ + @NotEmpty(message = "æµæ°´è¯´æ˜Žä¸èƒ½ä¸ºç©º") + private String title; +} diff --git a/tashow-module/tashow-module-pay/src/main/resources/application-dev.yaml b/tashow-module/tashow-module-pay/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..17c1e15 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/resources/application-dev.yaml @@ -0,0 +1,113 @@ +--- #################### 注册中心 + é…置中心相关é…ç½® #################### + +spring: + cloud: + nacos: + server-addr: 127.0.0.1:8848 # Nacos æœåŠ¡å™¨åœ°å€ + username: # Nacos è´¦å· + password: # Nacos å¯†ç  + discovery: # ã€é…置中心】é…置项 + namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # æœåŠ¡å®žä¾‹çš„ç‰ˆæœ¬å·ï¼Œå¯ç”¨äºŽç°åº¦å‘布 + config: # ã€æ³¨å†Œä¸­å¿ƒã€‘é…置项 + namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP + +--- #################### æ•°æ®åº“相关é…ç½® #################### +spring: + # æ•°æ®æºé…置项 + autoconfigure: + exclude: + datasource: + druid: # Druid ã€ç›‘控】相关的全局é…ç½® + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白åå•,ä¸å¡«åˆ™å…许所有访问 + url-pattern: /druid/* + login-username: # 控制å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç  + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # æ…¢ SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # å¤šæ•°æ®æºé…ç½® + druid: # Druid ã€è¿žæŽ¥æ± ã€‘相关的全局é…ç½® + initial-size: 5 # åˆå§‹è¿žæŽ¥æ•° + min-idle: 10 # 最å°è¿žæŽ¥æ± æ•°é‡ + max-active: 20 # æœ€å¤§è¿žæŽ¥æ± æ•°é‡ + max-wait: 600000 # é…置获å–连接等待超时的时间,å•ä½ï¼šæ¯«ç§’ + time-between-eviction-runs-millis: 60000 # é…置间隔多久æ‰è¿›è¡Œä¸€æ¬¡æ£€æµ‹ï¼Œæ£€æµ‹éœ€è¦å…³é—­çš„空闲连接,å•ä½ï¼šæ¯«ç§’ + min-evictable-idle-time-millis: 300000 # é…置一个连接在池中最å°ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•ä½ï¼šæ¯«ç§’ + max-evictable-idle-time-millis: 900000 # é…置一个连接在池中最大生存的时间,å•ä½ï¼šæ¯«ç§’ + validation-query: SELECT 1 FROM DUAL # é…ç½®æ£€æµ‹è¿žæŽ¥æ˜¯å¦æœ‰æ•ˆ + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: root + password: 123456 + slave: # æ¨¡æ‹Ÿä»Žåº“ï¼Œå¯æ ¹æ®è‡ªå·±éœ€è¦ä¿®æ”¹ # æ¨¡æ‹Ÿä»Žåº“ï¼Œå¯æ ¹æ®è‡ªå·±éœ€è¦ä¿®æ”¹ + lazy: true # 开坿‡’加载,ä¿è¯å¯åŠ¨é€Ÿåº¦ + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: root + password: 123456 + + # Redis é…置。Redisson 默认的é…置足够使用,一般ä¸éœ€è¦è¿›è¡Œè°ƒä¼˜ + data: + redis: + host: 400-infra.server.iocoder.cn # åœ°å€ + port: 6379 # ç«¯å£ + database: 1 # æ•°æ®åº“索引 +# password: 123456 # 密ç ï¼Œå»ºè®®ç”Ÿäº§çŽ¯å¢ƒå¼€å¯ + +--- #################### MQ 消æ¯é˜Ÿåˆ—相关é…ç½® #################### + +--- #################### 定时任务相关é…ç½® #################### + +--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® #################### + +# Lock4j é…置项 +lock4j: + acquire-timeout: 3000 # 获å–分布å¼é”超时时间,默认为 3000 毫秒 + expire: 30000 # 分布å¼é”的超时时间,默认为 30 毫秒 + +--- #################### 监控相关é…ç½® #################### + +# Actuator 监控端点的é…置项 +management: + endpoints: + web: + base-path: /actuator # Actuator æä¾›çš„ API 接å£çš„æ ¹ç›®å½•。默认为 /actuator + exposure: + include: '*' # 需è¦å¼€æ”¾çš„ç«¯ç‚¹ã€‚é»˜è®¤å€¼åªæ‰“å¼€ health å’Œ info 两个端点。通过设置 * ,å¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚ + +# Spring Boot Admin é…置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关é…ç½® + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +--- #################### 芋é“相关é…ç½® #################### + +# 芋é“é…置项,设置当å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½® +yudao: + pay: + order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠é“çš„ã€æ”¯ä»˜ã€‘å›žè°ƒåœ°å€ + refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠é“çš„ã€é€€æ¬¾ã€‘å›žè°ƒåœ°å€ + transfer-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/transfer # 支付渠é“çš„ã€è½¬è´¦ã€‘å›žè°ƒåœ°å€ + demo: true # å…³é—­æ¼”ç¤ºæ¨¡å¼ diff --git a/tashow-module/tashow-module-pay/src/main/resources/application-local.yaml b/tashow-module/tashow-module-pay/src/main/resources/application-local.yaml new file mode 100644 index 0000000..97082b3 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/resources/application-local.yaml @@ -0,0 +1,141 @@ +--- #################### 注册中心 + é…置中心相关é…ç½® #################### + +spring: + cloud: + nacos: + server-addr: 127.0.0.1:8848 # Nacos æœåŠ¡å™¨åœ°å€ + username: # Nacos è´¦å· + password: # Nacos å¯†ç  + discovery: # ã€é…置中心】é…置项 + namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # æœåŠ¡å®žä¾‹çš„ç‰ˆæœ¬å·ï¼Œå¯ç”¨äºŽç°åº¦å‘布 + config: # ã€æ³¨å†Œä¸­å¿ƒã€‘é…置项 + namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP + +--- #################### æ•°æ®åº“相关é…ç½® #################### +spring: + # æ•°æ®æºé…置项 + autoconfigure: + exclude: + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # ç¦ç”¨ Spring Boot Admin çš„ Client 的自动é…ç½® + datasource: + druid: # Druid ã€ç›‘控】相关的全局é…ç½® + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白åå•,ä¸å¡«åˆ™å…许所有访问 + url-pattern: /druid/* + login-username: # 控制å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç  + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # æ…¢ SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # å¤šæ•°æ®æºé…ç½® + druid: # Druid ã€è¿žæŽ¥æ± ã€‘相关的全局é…ç½® + initial-size: 1 # åˆå§‹è¿žæŽ¥æ•° + min-idle: 1 # 最å°è¿žæŽ¥æ± æ•°é‡ + max-active: 20 # æœ€å¤§è¿žæŽ¥æ± æ•°é‡ + max-wait: 600000 # é…置获å–连接等待超时的时间,å•ä½ï¼šæ¯«ç§’ + time-between-eviction-runs-millis: 60000 # é…置间隔多久æ‰è¿›è¡Œä¸€æ¬¡æ£€æµ‹ï¼Œæ£€æµ‹éœ€è¦å…³é—­çš„空闲连接,å•ä½ï¼šæ¯«ç§’ + min-evictable-idle-time-millis: 300000 # é…置一个连接在池中最å°ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•ä½ï¼šæ¯«ç§’ + max-evictable-idle-time-millis: 900000 # é…置一个连接在池中最大生存的时间,å•ä½ï¼šæ¯«ç§’ + validation-query: SELECT 1 FROM DUAL # é…ç½®æ£€æµ‹è¿žæŽ¥æ˜¯å¦æœ‰æ•ˆ + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例 + # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 + username: root + password: 123456 + # username: sa # SQL Server 连接的示例 + # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例 + # username: SYSDBA # DM 连接的示例 + # password: SYSDBA # DM 连接的示例 + slave: # æ¨¡æ‹Ÿä»Žåº“ï¼Œå¯æ ¹æ®è‡ªå·±éœ€è¦ä¿®æ”¹ + lazy: true # 开坿‡’加载,ä¿è¯å¯åŠ¨é€Ÿåº¦ + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: 123456 + + # Redis é…置。Redisson 默认的é…置足够使用,一般ä¸éœ€è¦è¿›è¡Œè°ƒä¼˜ + data: + redis: + host: 127.0.0.1 # åœ°å€ + port: 6379 # ç«¯å£ + database: 0 # æ•°æ®åº“索引 + # password: 123456 # 密ç ï¼Œå»ºè®®ç”Ÿäº§çŽ¯å¢ƒå¼€å¯ + +--- #################### MQ 消æ¯é˜Ÿåˆ—相关é…ç½® #################### + +--- #################### 定时任务相关é…ç½® #################### +xxl: + job: + enabled: false # 是å¦å¼€å¯è°ƒåº¦ä¸­å¿ƒï¼Œé»˜è®¤ä¸º true å¼€å¯ + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # è°ƒåº¦ä¸­å¿ƒéƒ¨ç½²è·Ÿåœ°å€ + +--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® #################### + +# Lock4j é…置项 +lock4j: + acquire-timeout: 3000 # 获å–分布å¼é”超时时间,默认为 3000 毫秒 + expire: 30000 # 分布å¼é”的超时时间,默认为 30 毫秒 + +--- #################### 监控相关é…ç½® #################### + +# Actuator 监控端点的é…置项 +management: + endpoints: + web: + base-path: /actuator # Actuator æä¾›çš„ API 接å£çš„æ ¹ç›®å½•。默认为 /actuator + exposure: + include: '*' # 需è¦å¼€æ”¾çš„ç«¯ç‚¹ã€‚é»˜è®¤å€¼åªæ‰“å¼€ health å’Œ info 两个端点。通过设置 * ,å¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚ + +# Spring Boot Admin é…置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关é…ç½® + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +# 日志文件é…ç½® +logging: + level: + # é…置自己写的 MyBatis Mapper æ‰“å°æ—¥å¿— + cn.iocoder.yudao.module.pay.dal.mysql: debug + cn.iocoder.yudao.module.pay.dal.mysql.notify.com.tashow.cloud.payapi.dal.mysql.notify.PayNotifyTaskMapper: INFO # é…ç½® PayNotifyTaskMapper 的日志级别为 info + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先ç¦ç”¨ï¼ŒSpring Boot 3.X 存在部分错误的 WARN æç¤º + +--- #################### 芋é“相关é…ç½® #################### + +# 芋é“é…置项,设置当å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½® +yudao: + env: # 多环境的é…置项 + tag: ${HOSTNAME} + security: + mock-enable: true + access-log: # 访问日志的é…置项 + enable: false + pay: + order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠é“çš„ã€æ”¯ä»˜ã€‘å›žè°ƒåœ°å€ + refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠é“çš„ã€é€€æ¬¾ã€‘å›žè°ƒåœ°å€ + transfer-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/transfer # 支付渠é“çš„ã€è½¬è´¦ã€‘å›žè°ƒåœ°å€ \ No newline at end of file diff --git a/tashow-module/tashow-module-pay/src/main/resources/application.yaml b/tashow-module/tashow-module-pay/src/main/resources/application.yaml new file mode 100644 index 0000000..c3f0203 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/resources/application.yaml @@ -0,0 +1,125 @@ +spring: + application: + name: pay-server + + profiles: + active: local + + main: + allow-circular-references: true # å…许循环ä¾èµ–,因为项目是三层架构,无法é¿å…这个情况。 + allow-bean-definition-overriding: true # å…许 Bean 覆盖,例如说 Feign 等会存在é‡å¤å®šä¹‰çš„æœåŠ¡ + + config: + import: + - optional:classpath:application-${spring.profiles.active}.yaml # åŠ è½½ã€æœ¬åœ°ã€‘é…ç½® + - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载ã€Nacos】的é…ç½® + + # Servlet é…ç½® + servlet: + # 文件上传相关é…置项 + multipart: + max-file-size: 16MB # å•ä¸ªæ–‡ä»¶å¤§å° + max-request-size: 32MB # è®¾ç½®æ€»ä¸Šä¼ çš„æ–‡ä»¶å¤§å° + + # Jackson é…置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 LocalDateTime 的格å¼ï¼Œä½¿ç”¨æ—¶é—´æˆ³ + write-date-timestamps-as-nanoseconds: false # 设置ä¸ä½¿ç”¨ nanoseconds 的格å¼ã€‚例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格å¼ï¼Œä½¿ç”¨æ—¶é—´æˆ³ + fail-on-empty-beans: false # å…许åºåˆ—化无属性的 Bean + + # Cache é…置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 å°æ—¶ + +server: + port: 48085 + +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件å,全路径 + +--- #################### æŽ¥å£æ–‡æ¡£é…ç½® #################### + +springdoc: + api-docs: + enabled: true # 1. 是å¦å¼€å¯ Swagger æŽ¥æ–‡æ¡£çš„å…ƒæ•°æ® + path: /v3/api-docs + swagger-ui: + enabled: true # 2.1 是å¦å¼€å¯ Swagger 文档的官方 UI ç•Œé¢ + path: /swagger-ui + default-flat-param-object: true # å‚è§ https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: false # TODO 芋艿:需è¦å…³é—­å¢žå¼ºï¼Œå…·ä½“原因è§ï¼šhttps://github.com/xiaoymin/knife4j/issues/874 + setting: + language: zh_cn + +# MyBatis Plus çš„é…置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # â€œæ™ºèƒ½â€æ¨¡å¼ï¼ŒåŸºäºŽ IdTypeEnvironmentPostProcessor + æ•°æ®æºçš„ç±»åž‹ï¼Œè‡ªåŠ¨é€‚é…æˆ AUTOã€INPUT 模å¼ã€‚ + # id-type: AUTO # 自增 IDï¼Œé€‚åˆ MySQL 等直接自增的数æ®åº“ + # id-type: INPUT # 用户输入 IDï¼Œé€‚åˆ Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“ + # id-type: ASSIGN_ID # åˆ†é… ID,默认使用雪花算法。注æ„,Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“时,需è¦åŽ»é™¤å®žä½“ç±»ä¸Šçš„ @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制å°çš„ Banner æ‰“å° + type-aliases-package: ${yudao.info.base-package}.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,å¯ä½¿ç”¨ https://www.imaegoo.com/2020/aes-key-generator/ ç½‘ç«™ç”Ÿæˆ + +mybatis-plus-join: + banner: false # 关闭控制å°çš„ Banner æ‰“å° + +# Spring Data Redis é…ç½® +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis çš„ Repository,所以直接ç¦ç”¨ï¼Œä¿è¯å¯åŠ¨é€Ÿåº¦ + +# VO 转æ¢ï¼ˆæ•°æ®ç¿»è¯‘)相关 +easy-trans: + is-enable-global: true # å¯ç”¨å…¨å±€ç¿»è¯‘(拦截所有 SpringMVC ResponseBody 进行自动翻译 )ã€‚å¦‚æžœå¯¹äºŽæ€§èƒ½è¦æ±‚很高å¯å…³é—­æ­¤é…置,或通过 @IgnoreTrans 忽略æŸä¸ªæŽ¥å£ + +--- #################### MQ 消æ¯é˜Ÿåˆ—相关é…ç½® #################### + +--- #################### 定时任务相关é…ç½® #################### + +xxl: + job: + executor: + appname: ${spring.application.name} # 执行器 AppName + logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器è¿è¡Œæ—¥å¿—文件存储ç£ç›˜è·¯å¾„ + accessToken: default_token # 执行器通讯TOKEN + +--- #################### 芋é“相关é…ç½® #################### + +yudao: + info: + version: 1.0.0 + base-package: cn.iocoder.yudao.module.pay + web: + admin-ui: + url: http://dashboard.yudao.iocoder.cn # Admin 管ç†åŽå° UI çš„åœ°å€ + xss: + enable: false + exclude-urls: # 如下 url,仅仅是为了演示,去掉é…置也没关系 + - ${management.endpoints.web.base-path}/** # ä¸å¤„ç† Actuator 的请求 + swagger: + title: 管ç†åŽå° + description: æä¾›ç®¡ç†å‘˜ç®¡ç†çš„æ‰€æœ‰åŠŸèƒ½ + version: ${yudao.info.version} + tenant: # 多租户相关é…置项 + enable: true + ignore-urls: + - /admin-api/pay/notify/** # æ”¯ä»˜å›žè°ƒé€šçŸ¥ï¼Œä¸æºå¸¦ç§Ÿæˆ·ç¼–å· + +debug: false diff --git a/tashow-module/tashow-module-pay/src/main/resources/logback-spring.xml b/tashow-module/tashow-module-pay/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..b1b9f3f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/main/resources/logback-spring.xml @@ -0,0 +1,76 @@ + + + + + + + + + +       + + + ${PATTERN_DEFAULT} + + + + + + + + + + ${PATTERN_DEFAULT} + + + + ${LOG_FILE} + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30} + + + + + + 0 + + 256 + + + + + + + + ${PATTERN_DEFAULT} + + + + + + + + + + + + + + + + + + + + + + diff --git a/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/app/PayAppServiceTest.java b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/app/PayAppServiceTest.java new file mode 100644 index 0000000..9a94c61 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/app/PayAppServiceTest.java @@ -0,0 +1,260 @@ +package com.tashow.cloud.pay.service.app; + +import cn.hutool.core.util.RandomUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppCreateReqVO; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppPageReqVO; +import com.tashow.cloud.pay.controller.admin.app.vo.PayAppUpdateReqVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.mysql.app.PayAppMapper; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static java.util.Collections.singleton; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +/** + * {@link PayAppServiceImpl} çš„å•元测试 + * + * @author aquan + */ +@Disabled // TODO 芋艿:åŽç»­ fix è¡¥å……çš„å•æµ‹ +@Import(PayAppServiceImpl.class) +public class PayAppServiceTest extends BaseDbUnitTest { + + @Resource + private PayAppServiceImpl appService; + + @Resource + private PayAppMapper appMapper; + + @MockBean + private PayOrderService orderService; + @MockBean + private PayRefundService refundService; + + @Test + public void testCreateApp_success() { + // 准备傿•° + PayAppCreateReqVO reqVO = randomPojo(PayAppCreateReqVO.class, o -> + o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())) + .setOrderNotifyUrl(randomURL()) + .setRefundNotifyUrl(randomURL())); + + // 调用 + Long appId = appService.createApp(reqVO); + // 断言 + assertNotNull(appId); + PayAppDO app = appMapper.selectById(appId); + assertPojoEquals(reqVO, app); + } + + @Test + public void testUpdateApp_success() { + // mock æ•°æ® + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setOrderNotifyUrl(randomURL()).setRefundNotifyUrl(randomURL()); + o.setId(dbApp.getId()); // 设置更新的 ID + }); + + // 调用 + appService.updateApp(reqVO); + // æ ¡éªŒæ˜¯å¦æ›´æ–°æ­£ç¡® + PayAppDO app = appMapper.selectById(reqVO.getId()); // èŽ·å–æœ€æ–°çš„ + assertPojoEquals(reqVO, app); + } + + @Test + public void testUpdateApp_notExists() { + // 准备傿•° + PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> + o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus()))); + // 调用, 并断言异常 + assertServiceException(() -> appService.updateApp(reqVO), APP_NOT_FOUND); + } + + @Test + public void testUpdateAppStatus() { + // mock æ•°æ® + PayAppDO dbApp = randomPojo(PayAppDO.class, o -> + o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + appMapper.insert(dbApp);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + + // 准备傿•° + Long id = dbApp.getId(); + Integer status = CommonStatusEnum.ENABLE.getStatus(); + // 调用 + appService.updateAppStatus(id, status); + // 断言 + PayAppDO app = appMapper.selectById(id); // èŽ·å–æœ€æ–°çš„ + assertEquals(status, app.getStatus()); + } + + @Test + public void testDeleteApp_success() { + // mock æ•°æ® + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbApp.getId(); + + // 调用 + appService.deleteApp(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(appMapper.selectById(id)); + } + + @Test + public void testDeleteApp_notExists() { + // 准备傿•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> appService.deleteApp(id), APP_NOT_FOUND); + } + + @Test + public void testDeleteApp_existOrder() { + // mock æ•°æ® + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbApp.getId(); + // mock è®¢å•æœ‰è®¢å• + when(orderService.getOrderCountByAppId(eq(id))).thenReturn(10L); + + // 调用, 并断言异常 + assertServiceException(() -> appService.deleteApp(id), APP_EXIST_ORDER_CANT_DELETE); + } + + @Test + public void testDeleteApp_existRefund() { + // mock æ•°æ® + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbApp.getId(); + // mock è®¢å•æœ‰è®¢å• + when(refundService.getRefundCountByAppId(eq(id))).thenReturn(10L); + + // 调用, 并断言异常 + assertServiceException(() -> appService.deleteApp(id), APP_EXIST_REFUND_CANT_DELETE); + } + + @Test + public void testApp() { + // mock æ•°æ® + PayAppDO dbApp = randomPojo(PayAppDO.class); + appMapper.insert(dbApp);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbApp.getId(); + + // 调用 + PayAppDO app = appService.getApp(id); + // 校验数æ®ä¸€è‡´ + assertPojoEquals(app, dbApp); + } + + @Test + public void testAppMap() { + // mock æ•°æ® + PayAppDO dbApp01 = randomPojo(PayAppDO.class); + appMapper.insert(dbApp01);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + PayAppDO dbApp02 = randomPojo(PayAppDO.class); + appMapper.insert(dbApp02);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbApp01.getId(); + + // 调用 + Map appMap = appService.getAppMap(singleton(id)); + // 校验数æ®ä¸€è‡´ + assertEquals(1, appMap.size()); + assertPojoEquals(dbApp01, appMap.get(id)); + } + + @Test + public void testGetAppPage() { + // mock æ•°æ® + PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到 + o.setName("ç¿ç¿å§çš„æ‚è´§é“º"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2021,11,20)); + }); + + appMapper.insert(dbApp); + // 测试 name ä¸åŒ¹é… + appMapper.insert(cloneIgnoreId(dbApp, o -> o.setName("æ•æ•å§çš„æ‚è´§é“º"))); + // 测试 status ä¸åŒ¹é… + appMapper.insert(cloneIgnoreId(dbApp, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime ä¸åŒ¹é… + appMapper.insert(cloneIgnoreId(dbApp, o -> o.setCreateTime(buildTime(2021,12,21)))); + // 准备傿•° + PayAppPageReqVO reqVO = new PayAppPageReqVO(); + reqVO.setName("ç¿ç¿å§çš„æ‚è´§é“º"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2021, 11, 19, 2021, 11, 21)); + + // 调用 + PageResult pageResult = appService.getAppPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbApp, pageResult.getList().get(0)); + } + + @Test + public void testValidPayApp_success() { + // mock æ•°æ® + PayAppDO dbApp = randomPojo(PayAppDO.class, + o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + appMapper.insert(dbApp);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbApp.getId(); + + // 调用 + PayAppDO app = appService.validPayApp(id); + // 校验数æ®ä¸€è‡´ + assertPojoEquals(app, dbApp); + } + + @Test + public void testValidPayApp_notFound() { + assertServiceException(() -> appService.validPayApp(randomLongId()), APP_NOT_FOUND); + } + + @Test + public void testValidPayApp_disable() { + // mock æ•°æ® + PayAppDO dbApp = randomPojo(PayAppDO.class, + o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + appMapper.insert(dbApp);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbApp.getId(); + + // 调用,并断言异常 + assertServiceException(() -> appService.validPayApp(id), APP_IS_DISABLE); + } + +} diff --git a/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/channel/PayChannelServiceTest.java b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/channel/PayChannelServiceTest.java new file mode 100644 index 0000000..af289f7 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/channel/PayChannelServiceTest.java @@ -0,0 +1,337 @@ +package com.tashow.cloud.pay.service.channel; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelCreateReqVO; +import com.tashow.cloud.pay.controller.admin.channel.vo.PayChannelUpdateReqVO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.dal.mysql.channel.PayChannelMapper; +import com.alibaba.fastjson.JSON; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import jakarta.validation.Validator; +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +@Import({PayChannelServiceImpl.class}) +public class PayChannelServiceTest extends BaseDbUnitTest { + + @Resource + private PayChannelServiceImpl channelService; + + @Resource + private PayChannelMapper channelMapper; + + @MockBean + private PayClientFactory payClientFactory; + @MockBean + private Validator validator; + + @Test + public void testCreateChannel_success() { + // 准备傿•° + WxPayClientConfig config = randomWxPayClientConfig(); + PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { + o.setStatus(randomCommonStatus()); + o.setCode(PayChannelEnum.WX_PUB.getCode()); + o.setConfig(JsonUtils.toJsonString(config)); + }); + + // 调用 + Long channelId = channelService.createChannel(reqVO); + // æ ¡éªŒè®°å½•çš„å±žæ€§æ˜¯å¦æ­£ç¡® + PayChannelDO channel = channelMapper.selectById(channelId); + assertPojoEquals(reqVO, channel, "config"); + assertPojoEquals(config, channel.getConfig()); + } + + @Test + public void testCreateChannel_exists() { + // mock æ•°æ® + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, + o -> o.setConfig(randomWxPayClientConfig())); + channelMapper.insert(dbChannel);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { + o.setAppId(dbChannel.getAppId()); + o.setCode(dbChannel.getCode()); + }); + + // 调用, 并断言异常 + assertServiceException(() -> channelService.createChannel(reqVO), CHANNEL_EXIST_SAME_CHANNEL_ERROR); + } + + @Test + public void testUpdateChannel_success() { + // mock æ•°æ® + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + }); + channelMapper.insert(dbChannel);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + AlipayPayClientConfig config = randomAlipayPayClientConfig(); + PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> { + o.setId(dbChannel.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + o.setConfig(JsonUtils.toJsonString(config)); + }); + + // 调用 + channelService.updateChannel(reqVO); + // æ ¡éªŒæ˜¯å¦æ›´æ–°æ­£ç¡® + PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // èŽ·å–æœ€æ–°çš„ + assertPojoEquals(reqVO, channel, "config"); + assertPojoEquals(config, channel.getConfig()); + } + + @Test + public void testUpdateChannel_notExists() { + // 准备傿•° + AlipayPayClientConfig payClientPublicKeyConfig = randomAlipayPayClientConfig(); + PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setConfig(JSON.toJSONString(payClientPublicKeyConfig)); + }); + + // 调用, 并断言异常 + assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_FOUND); + } + + @Test + public void testDeleteChannel_success() { + // mock æ•°æ® + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + }); + channelMapper.insert(dbChannel);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbChannel.getId(); + + // 调用 + channelService.deleteChannel(id); + // 校验数æ®ä¸å­˜åœ¨äº† + assertNull(channelMapper.selectById(id)); + } + + @Test + public void testDeleteChannel_notExists() { + // 准备傿•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_FOUND); + } + + @Test + public void testGetChannel() { + // mock æ•°æ® + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + }); + channelMapper.insert(dbChannel);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbChannel.getId(); + + // 调用 + PayChannelDO channel = channelService.getChannel(id); + // æ ¡éªŒæ˜¯å¦æ›´æ–°æ­£ç¡® + assertPojoEquals(dbChannel, channel); + } + + @Test + public void testGetChannelListByAppIds() { + // mock æ•°æ® + PayChannelDO dbChannel01 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + }); + channelMapper.insert(dbChannel01);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.WX_PUB.getCode()); + o.setConfig(randomWxPayClientConfig()); + }); + channelMapper.insert(dbChannel02);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long appId = dbChannel01.getAppId(); + + // 调用 + List channels = channelService.getChannelListByAppIds(Collections.singleton(appId)); + // æ ¡éªŒæ˜¯å¦æ›´æ–°æ­£ç¡® + assertEquals(1, channels.size()); + assertPojoEquals(dbChannel01, channels.get(0)); + } + + @Test + public void testGetChannelByAppIdAndCode() { + // mock æ•°æ® + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + }); + channelMapper.insert(dbChannel);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long appId = dbChannel.getAppId(); + String code = dbChannel.getCode(); + + // 调用 + PayChannelDO channel = channelService.getChannelByAppIdAndCode(appId, code); + // 断言 + assertPojoEquals(channel, dbChannel); + } + + @Test + public void testValidPayChannel_notExists() { + // 准备傿•° + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> channelService.validPayChannel(id), CHANNEL_NOT_FOUND); + } + + @Test + public void testValidPayChannel_isDisable() { + // mock æ•°æ® + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); + }); + channelMapper.insert(dbChannel);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbChannel.getId(); + + // 调用, 并断言异常 + assertServiceException(() -> channelService.validPayChannel(id), CHANNEL_IS_DISABLE); + } + + @Test + public void testValidPayChannel_success() { + // mock æ•°æ® + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + channelMapper.insert(dbChannel);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long id = dbChannel.getId(); + + // 调用 + PayChannelDO channel = channelService.validPayChannel(id); + // 断言异常 + assertPojoEquals(channel, dbChannel); + } + + @Test + public void testValidPayChannel_appIdAndCode() { + // mock æ•°æ® + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + channelMapper.insert(dbChannel);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // 准备傿•° + Long appId = dbChannel.getAppId(); + String code = dbChannel.getCode(); + + // 调用 + PayChannelDO channel = channelService.validPayChannel(appId, code); + // 断言异常 + assertPojoEquals(channel, dbChannel); + } + + @Test + public void testGetEnableChannelList() { + // 准备傿•° + Long appId = randomLongId(); + // mock æ•°æ® 01(enable ä¸åŒ¹é…) + PayChannelDO dbChannel01 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); + }); + channelMapper.insert(dbChannel01);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // mock æ•°æ® 02(appId ä¸åŒ¹é…) + PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + channelMapper.insert(dbChannel02);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + // mock æ•°æ® 03 + PayChannelDO dbChannel03 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setAppId(appId); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + channelMapper.insert(dbChannel03);// @Sql: å…ˆæ’入出一æ¡å­˜åœ¨çš„æ•°æ® + + // 调用 + List channel = channelService.getEnableChannelList(appId); + // 断言异常 + assertPojoEquals(channel, dbChannel03); + } + + @Test + public void testGetPayClient() { + // mock æ•°æ® + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + channelMapper.insert(channel); + // mock 傿•° + Long id = channel.getId(); + // mock 方法 + PayClient mockClient = mock(PayClient.class); + when(payClientFactory.createOrUpdatePayClient(eq(id), eq(channel.getCode()), eq(channel.getConfig()))) + .thenReturn(mockClient); + + // 调用 + PayClient client = channelService.getPayClient(id); + // 断言 + assertSame(client, mockClient); + } + + public WxPayClientConfig randomWxPayClientConfig() { + return new WxPayClientConfig() + .setAppId(randomString()) + .setMchId(randomString()) + .setApiVersion(WxPayClientConfig.API_VERSION_V2) + .setMchKey(randomString()); + } + + public AlipayPayClientConfig randomAlipayPayClientConfig() { + return new AlipayPayClientConfig() + .setServerUrl(randomURL()) + .setAppId(randomString()) + .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT) + .setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY) + .setPrivateKey(randomString()) + .setAlipayPublicKey(randomString()); + } + +} diff --git a/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/notify/PayNotifyServiceTest.java b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/notify/PayNotifyServiceTest.java new file mode 100644 index 0000000..93717c7 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/notify/PayNotifyServiceTest.java @@ -0,0 +1,353 @@ +package com.tashow.cloud.pay.service.notify; + +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import com.tashow.cloud.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyLogDO; +import com.tashow.cloud.pay.dal.dataobject.notify.PayNotifyTaskDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import com.tashow.cloud.pay.dal.mysql.notify.PayNotifyLogMapper; +import com.tashow.cloud.pay.dal.mysql.notify.PayNotifyTaskMapper; +import com.tashow.cloud.pay.dal.redis.notify.PayNotifyLockRedisDAO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; +import com.tashow.cloud.pay.framework.job.config.PayJobConfiguration; +import com.tashow.cloud.pay.service.order.PayOrderService; +import com.tashow.cloud.pay.service.refund.PayRefundService; +import com.tashow.cloud.pay.service.refund.PayRefundServiceImpl; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import java.time.Duration; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +/** + * {@link PayRefundServiceImpl} çš„å•元测试类 + * + * @author 芋艿 + */ +@Disabled // TODO 芋艿:åŽç»­ fix è¡¥å……çš„å•æµ‹ +@Import({PayJobConfiguration.class, PayNotifyServiceImpl.class, PayNotifyLockRedisDAO.class}) +public class PayNotifyServiceTest extends BaseDbUnitTest { + + @Resource + private PayNotifyServiceImpl notifyService; + + @MockBean + private PayOrderService orderService; + @MockBean + private PayRefundService refundService; + + @Resource + private PayNotifyTaskMapper notifyTaskMapper; + @Resource + private PayNotifyLogMapper notifyLogMapper; + + @MockBean + private RedissonClient redissonClient; + + @Test + public void testCreatePayNotifyTask_order() { + PayNotifyServiceImpl payNotifyService = mock(PayNotifyServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayNotifyServiceImpl.class))) + .thenReturn(payNotifyService); + + // 准备傿•° + Integer type = PayNotifyTypeEnum.ORDER.getType(); + Long dataId = 1L; + // mock 方法(order) + PayOrderDO order = randomPojo(PayOrderDO.class); + when(orderService.getOrder(eq(1L))).thenReturn(order); + // mock 方法(lock) + mockLock(null); // null 的原因,是咱没办法拿到 taskId 新增 + + // 调用 + notifyService.createPayNotifyTask(type, dataId); + // 断言,task + PayNotifyTaskDO dbTask = notifyTaskMapper.selectOne(null); + assertNotNull(dbTask.getNextNotifyTime()); + assertThat(dbTask) + .extracting("type", "dataId", "status", "notifyTimes", "maxNotifyTimes", + "appId", "merchantOrderId", "notifyUrl") + .containsExactly(type, dataId, PayNotifyStatusEnum.WAITING.getStatus(), 0, 9, + order.getAppId(), order.getMerchantOrderId(), order.getNotifyUrl()); + // 断言,调用 + verify(payNotifyService).executeNotify0(eq(dbTask)); + } + } + + @Test + public void testCreatePayNotifyTask_refund() { + PayNotifyServiceImpl payNotifyService = mock(PayNotifyServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayNotifyServiceImpl.class))) + .thenReturn(payNotifyService); + + // 准备傿•° + Integer type = PayNotifyTypeEnum.REFUND.getType(); + Long dataId = 1L; + // mock 方法(refund) + PayRefundDO refund = randomPojo(PayRefundDO.class); + when(refundService.getRefund(eq(1L))).thenReturn(refund); + // mock 方法(lock) + mockLock(null); // null 的原因,是咱没办法拿到 taskId 新增 + + // 调用 + notifyService.createPayNotifyTask(type, dataId); + // 断言,task + PayNotifyTaskDO dbTask = notifyTaskMapper.selectOne(null); + assertNotNull(dbTask.getNextNotifyTime()); + assertThat(dbTask) + .extracting("type", "dataId", "status", "notifyTimes", "maxNotifyTimes", + "appId", "merchantOrderId", "notifyUrl") + .containsExactly(type, dataId, PayNotifyStatusEnum.WAITING.getStatus(), 0, 9, + refund.getAppId(), refund.getMerchantOrderId(), refund.getNotifyUrl()); + // 断言,调用 + verify(payNotifyService).executeNotify0(eq(dbTask)); + } + } + + @Test + public void testExecuteNotify() throws InterruptedException { + // mock æ•°æ®ï¼ˆnotify) + PayNotifyTaskDO dbTask01 = randomPojo(PayNotifyTaskDO.class, + o -> o.setStatus(PayNotifyStatusEnum.WAITING.getStatus()) + .setNextNotifyTime(addTime(Duration.ofMinutes(-1)))); + notifyTaskMapper.insert(dbTask01); + PayNotifyTaskDO dbTask02 = randomPojo(PayNotifyTaskDO.class, + o -> o.setStatus(PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus()) + .setNextNotifyTime(addTime(Duration.ofMinutes(-1)))); + notifyTaskMapper.insert(dbTask02); + PayNotifyTaskDO dbTask03 = randomPojo(PayNotifyTaskDO.class, + o -> o.setStatus(PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()) + .setNextNotifyTime(addTime(Duration.ofMinutes(-1)))); + notifyTaskMapper.insert(dbTask03); + PayNotifyTaskDO dbTask04 = randomPojo(PayNotifyTaskDO.class, // 䏿»¡è¶³çŠ¶æ€ + o -> o.setStatus(PayNotifyStatusEnum.FAILURE.getStatus()) + .setNextNotifyTime(addTime(Duration.ofMinutes(-1)))); + notifyTaskMapper.insert(dbTask04); + PayNotifyTaskDO dbTask05 = randomPojo(PayNotifyTaskDO.class, // 䏿»¡è¶³çŠ¶æ€ + o -> o.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()) + .setNextNotifyTime(addTime(Duration.ofMinutes(-1)))); + notifyTaskMapper.insert(dbTask05); + PayNotifyTaskDO dbTask06 = randomPojo(PayNotifyTaskDO.class, // 䏿»¡è¶³æ—¶é—´ + o -> o.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()) + .setNextNotifyTime(addTime(Duration.ofMinutes(1)))); + notifyTaskMapper.insert(dbTask06); + // mock 方法(lock) + mockLock(dbTask01.getId()); + mockLock(dbTask02.getId()); + mockLock(dbTask03.getId()); + + // 调用 + int count = notifyService.executeNotify(); + // æ–­è¨€ï¼Œæ•°é‡ + assertEquals(count, 3); + } + + @Test // 由于 HttpUtil ä¸å¥½ mockï¼Œæ‰€ä»¥åªæµ‹è¯•异常的情况 + public void testExecuteNotify0_exception() { + // mock æ•°æ®ï¼ˆtask) + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, o -> o.setType(-1) + .setNotifyTimes(0).setMaxNotifyTimes(9)); + notifyTaskMapper.insert(task); + + // 调用 + notifyService.executeNotify0(task); + // 断言,task + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); + assertNotEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); + assertEquals(dbTask.getNotifyTimes(), 1); + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()); + // 断言,log + PayNotifyLogDO dbLog = notifyLogMapper.selectOne(null); + assertEquals(dbLog.getTaskId(), task.getId()); + assertEquals(dbLog.getNotifyTimes(), 1); + assertTrue(dbLog.getResponse().contains("未知的通知任务类型:")); + assertEquals(dbLog.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()); + } + + @Test + public void testProcessNotifyResult_success() { + // mock æ•°æ®ï¼ˆtask) + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, + o -> o.setNotifyTimes(0).setMaxNotifyTimes(9)); + notifyTaskMapper.insert(task); + // 准备傿•° + CommonResult invokeResult = CommonResult.success(randomString()); + + // 调用 + notifyService.processNotifyResult(task, invokeResult, null); + // 断言 + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); + assertEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); + assertEquals(dbTask.getNotifyTimes(), 1); + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.SUCCESS.getStatus()); + } + + @Test + public void testProcessNotifyResult_failure() { + // mock æ•°æ®ï¼ˆtask) + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, + o -> o.setNotifyTimes(8).setMaxNotifyTimes(9)); + notifyTaskMapper.insert(task); + // 准备傿•° + CommonResult invokeResult = CommonResult.error(BAD_REQUEST); + + // 调用 + notifyService.processNotifyResult(task, invokeResult, null); + // 断言 + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); + assertEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); + assertEquals(dbTask.getNotifyTimes(), 9); + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.FAILURE.getStatus()); + } + + @Test + public void testProcessNotifyResult_requestFailure() { + // mock æ•°æ®ï¼ˆtask) + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, + o -> o.setNotifyTimes(0).setMaxNotifyTimes(9)); + notifyTaskMapper.insert(task); + // 准备傿•° + CommonResult invokeResult = CommonResult.error(BAD_REQUEST); + + // 调用 + notifyService.processNotifyResult(task, invokeResult, null); + // 断言 + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); + assertNotEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); + assertEquals(dbTask.getNotifyTimes(), 1); + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus()); + } + + @Test + public void testProcessNotifyResult_requestSuccess() { + // mock æ•°æ®ï¼ˆtask) + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, + o -> o.setNotifyTimes(0).setMaxNotifyTimes(9)); + notifyTaskMapper.insert(task); + // 准备傿•° + CommonResult invokeResult = CommonResult.error(BAD_REQUEST); + RuntimeException invokeException = new RuntimeException(); + + // 调用 + notifyService.processNotifyResult(task, invokeResult, invokeException); + // 断言 + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); + assertNotEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); + assertEquals(dbTask.getNotifyTimes(), 1); + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()); + } + + @Test + public void testGetNotifyTask() { + // mock æ•°æ®ï¼ˆtask) + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class); + notifyTaskMapper.insert(task); + // 准备傿•° + Long id = task.getId(); + + // 调用 + PayNotifyTaskDO dbTask = notifyService.getNotifyTask(id); + // 断言 + assertPojoEquals(dbTask, task); + } + + @Test + public void testGetNotifyTaskPage() { + // mock æ•°æ® + PayNotifyTaskDO dbTask = randomPojo(PayNotifyTaskDO.class, o -> { // 等会查询到 + o.setAppId(1L); + o.setType(PayNotifyTypeEnum.REFUND.getType()); + o.setDataId(100L); + o.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()); + o.setMerchantOrderId("P110"); + o.setCreateTime(buildTime(2023, 2, 3)); + }); + notifyTaskMapper.insert(dbTask); + // 测试 appId ä¸åŒ¹é… + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setAppId(2L))); + // 测试 type ä¸åŒ¹é… + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setType(PayNotifyTypeEnum.ORDER.getType()))); + // 测试 dataId ä¸åŒ¹é… + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setDataId(200L))); + // 测试 status ä¸åŒ¹é… + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setStatus(PayNotifyStatusEnum.FAILURE.getStatus()))); + // 测试 merchantOrderId ä¸åŒ¹é… + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setMerchantOrderId(randomString()))); + // 测试 createTime ä¸åŒ¹é… + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setCreateTime(buildTime(2023, 1, 1)))); + // 准备傿•° + PayNotifyTaskPageReqVO reqVO = new PayNotifyTaskPageReqVO(); + reqVO.setAppId(1L); + reqVO.setType(PayNotifyTypeEnum.REFUND.getType()); + reqVO.setDataId(100L); + reqVO.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()); + reqVO.setMerchantOrderId("P110"); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = notifyService.getNotifyTaskPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbTask, pageResult.getList().get(0)); + } + + @Test + public void testGetNotifyLogList() { + // mock æ•°æ® + PayNotifyLogDO dbLog = randomPojo(PayNotifyLogDO.class); + notifyLogMapper.insert(dbLog); + PayNotifyLogDO dbLog02 = randomPojo(PayNotifyLogDO.class); + notifyLogMapper.insert(dbLog02); + // 准备傿•° + Long taskId = dbLog.getTaskId(); + + // 调用 + List logList = notifyService.getNotifyLogList(taskId); + // 断言 + assertEquals(logList.size(), 1); + assertPojoEquals(dbLog, logList.get(0)); + } + + private void mockLock(Long id) { + RLock lock = mock(RLock.class); + if (id == null) { + when(redissonClient.getLock(anyString())) + .thenReturn(lock); + } else { + when(redissonClient.getLock(eq("pay_notify:lock:" + id))) + .thenReturn(lock); + } + } + +} diff --git a/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/order/PayOrderServiceTest.java b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/order/PayOrderServiceTest.java new file mode 100644 index 0000000..3ea404f --- /dev/null +++ b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/order/PayOrderServiceTest.java @@ -0,0 +1,1105 @@ +package com.tashow.cloud.pay.service.order; + +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderExportReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderPageReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderSubmitReqVO; +import com.tashow.cloud.pay.controller.admin.order.vo.PayOrderSubmitRespVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderExtensionDO; +import com.tashow.cloud.pay.dal.mysql.order.PayOrderExtensionMapper; +import com.tashow.cloud.pay.dal.mysql.order.PayOrderMapper; +import com.tashow.cloud.pay.dal.redis.no.PayNoRedisDAO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import com.tashow.cloud.pay.framework.pay.config.PayProperties; +import com.tashow.cloud.pay.service.app.PayAppService; +import com.tashow.cloud.pay.service.channel.PayChannelService; +import com.tashow.cloud.pay.service.notify.PayNotifyService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +/** + * {@link PayOrderServiceImpl} çš„å•元测试类 + * + * @author 芋艿 + */ +@Import({PayOrderServiceImpl.class, PayNoRedisDAO.class}) +public class PayOrderServiceTest extends BaseDbAndRedisUnitTest { + + @Resource + private PayOrderServiceImpl orderService; + + @Resource + private PayOrderMapper orderMapper; + @Resource + private PayOrderExtensionMapper orderExtensionMapper; + + @MockBean + private PayProperties properties; + @MockBean + private PayAppService appService; + @MockBean + private PayChannelService channelService; + @MockBean + private PayNotifyService notifyService; + + @BeforeEach + public void setUp() { + when(properties.getOrderNotifyUrl()).thenReturn("http://127.0.0.1"); + } + + @Test + public void testGetOrder_id() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class); + orderMapper.insert(order); + // 准备傿•° + Long id = order.getId(); + + // 调用 + PayOrderDO dbOrder = orderService.getOrder(id); + // 断言 + assertPojoEquals(dbOrder, order); + } + + @Test + public void testGetOrder_appIdAndMerchantOrderId() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class); + orderMapper.insert(order); + // 准备傿•° + Long appId = order.getAppId(); + String merchantOrderId = order.getMerchantOrderId(); + + // 调用 + PayOrderDO dbOrder = orderService.getOrder(appId, merchantOrderId); + // 断言 + assertPojoEquals(dbOrder, order); + } + + @Test + public void testGetOrderCountByAppId() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order01 = randomPojo(PayOrderDO.class); + orderMapper.insert(order01); + PayOrderDO order02 = randomPojo(PayOrderDO.class); + orderMapper.insert(order02); + // 准备傿•° + Long appId = order01.getAppId(); + + // 调用 + Long count = orderService.getOrderCountByAppId(appId); + // 断言 + assertEquals(count, 1L); + } + + @Test + public void testGetOrderPage() { + // mock æ•°æ® + PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> { // 等会查询到 + o.setAppId(1L); + o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); + o.setMerchantOrderId("110"); + o.setChannelOrderNo("220"); + o.setNo("330"); + o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + o.setCreateTime(buildTime(2018, 1, 15)); + }); + orderMapper.insert(dbOrder); + // 测试 appId ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setAppId(2L))); + // 测试 channelCode ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); + // 测试 merchantOrderId ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(randomString()))); + // 测试 channelOrderNo ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelOrderNo(randomString()))); + // 测试 no ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNo(randomString()))); + // 测试 status ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()))); + // 测试 createTime ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(buildTime(2019, 1, 1)))); + // 准备傿•° + PayOrderPageReqVO reqVO = new PayOrderPageReqVO(); + reqVO.setAppId(1L); + reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode()); + reqVO.setMerchantOrderId("11"); + reqVO.setChannelOrderNo("22"); + reqVO.setNo("33"); + reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2018, 1, 10, 2018, 1, 30)); + + // 调用 + PageResult pageResult = orderService.getOrderPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbOrder, pageResult.getList().get(0)); + } + + @Test + public void testGetOrderList() { + // mock æ•°æ® + PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> { // 等会查询到 + o.setAppId(1L); + o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); + o.setMerchantOrderId("110"); + o.setChannelOrderNo("220"); + o.setNo("330"); + o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + o.setCreateTime(buildTime(2018, 1, 15)); + }); + orderMapper.insert(dbOrder); + // 测试 appId ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setAppId(2L))); + // 测试 channelCode ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); + // 测试 merchantOrderId ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(randomString()))); + // 测试 channelOrderNo ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelOrderNo(randomString()))); + // 测试 no ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNo(randomString()))); + // 测试 status ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()))); + // 测试 createTime ä¸åŒ¹é… + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(buildTime(2019, 1, 1)))); + // 准备傿•° + PayOrderExportReqVO reqVO = new PayOrderExportReqVO(); + reqVO.setAppId(1L); + reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode()); + reqVO.setMerchantOrderId("11"); + reqVO.setChannelOrderNo("22"); + reqVO.setNo("33"); + reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2018, 1, 10, 2018, 1, 30)); + + // 调用 + List list = orderService.getOrderList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbOrder, list.get(0)); + } + + @Test + public void testCreateOrder_success() { + // mock 傿•° + PayOrderCreateReqDTO reqDTO = randomPojo(PayOrderCreateReqDTO.class, + o -> o.setAppKey("demo").setMerchantOrderId("10") + .setSubject(randomString()).setBody(randomString())); + // mock 方法 + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L).setOrderNotifyUrl("http://127.0.0.1")); + when(appService.validPayApp(eq(reqDTO.getAppKey()))).thenReturn(app); + + // 调用 + Long orderId = orderService.createOrder(reqDTO); + // 断言 + PayOrderDO order = orderMapper.selectById(orderId); + assertPojoEquals(order, reqDTO); + assertEquals(order.getAppId(), 1L); + assertEquals(order.getNotifyUrl(), "http://127.0.0.1"); + assertEquals(order.getStatus(), PayOrderStatusEnum.WAITING.getStatus()); + assertEquals(order.getRefundPrice(), 0); + } + + @Test + public void testCreateOrder_exists() { + // mock 傿•° + PayOrderCreateReqDTO reqDTO = randomPojo(PayOrderCreateReqDTO.class, + o -> o.setAppKey("demo").setMerchantOrderId("10")); + // mock æ•°æ® + PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> o.setAppId(1L).setMerchantOrderId("10")); + orderMapper.insert(dbOrder); + // mock 方法 + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L).setOrderNotifyUrl("http://127.0.0.1")); + when(appService.validPayApp(eq(reqDTO.getAppKey()))).thenReturn(app); + + // 调用 + Long orderId = orderService.createOrder(reqDTO); + // 断言 + PayOrderDO order = orderMapper.selectById(orderId); + assertPojoEquals(dbOrder, order); + } + + @Test + public void testSubmitOrder_notFound() { + // 准备傿•° + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class); + String userIp = randomString(); + + // 调用, 并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), PAY_ORDER_NOT_FOUND); + } + + @Test + public void testSubmitOrder_notWaiting() { + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.REFUND.getStatus())); + orderMapper.insert(order); + // 准备傿•° + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId())); + String userIp = randomString(); + + // 调用, 并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), PAY_ORDER_STATUS_IS_NOT_WAITING); + } + + @Test + public void testSubmitOrder_isSuccess() { + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())); + orderMapper.insert(order); + // 准备傿•° + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId())); + String userIp = randomString(); + + // 调用, 并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), PAY_ORDER_STATUS_IS_SUCCESS); + } + + @Test + public void testSubmitOrder_expired() { + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setExpireTime(addTime(Duration.ofDays(-1)))); + orderMapper.insert(order); + // 准备傿•° + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId())); + String userIp = randomString(); + + // 调用, 并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), PAY_ORDER_IS_EXPIRED); + } + + @Test + public void testSubmitOrder_channelNotFound() { + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1)))); + orderMapper.insert(order); + // 准备傿•° + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + String userIp = randomString(); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq(1L))).thenReturn(app); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode()))) + .thenReturn(channel); + + // 调用, 并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), CHANNEL_NOT_FOUND); + } + + @Test // 调用 unifiedOrder 接å£ï¼Œè¿”回存在渠é“错误 + public void testSubmitOrder_channelError() { + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) + .thenReturn(payOrderServiceImpl); + + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1)))); + orderMapper.insert(order); + // 准备傿•° + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + String userIp = randomString(); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq(1L))).thenReturn(app); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode()))) + .thenReturn(channel); + // mock 方法(client) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法() + PayOrderRespDTO unifiedOrderResp = randomPojo(PayOrderRespDTO.class, o -> + o.setChannelErrorCode("001").setChannelErrorMsg("模拟异常")); + when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> { + assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo()); + assertThat(payOrderUnifiedReqDTO) + .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime") + .containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10", + reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime()); + return true; + }))).thenReturn(unifiedOrderResp); + + // 调用,并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), + PAY_ORDER_SUBMIT_CHANNEL_ERROR, "001", "模拟异常"); + // 断言,数æ®è®°å½•(PayOrderExtensionDO) + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectOne(null); + assertNotNull(orderExtension); + assertThat(orderExtension).extracting("no", "orderId").isNotNull(); + assertThat(orderExtension) + .extracting("channelId", "channelCode","userIp" ,"status", "channelExtras", + "channelErrorCode", "channelErrorMsg", "channelNotifyData") + .containsExactly(10L, PayChannelEnum.ALIPAY_APP.getCode(), userIp, + PayOrderStatusEnum.WAITING.getStatus(), reqVO.getChannelExtras(), + null, null, null); + } + } + + @Test + public void testSubmitOrder_success() { + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) + .thenReturn(payOrderServiceImpl); + + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1)))); + orderMapper.insert(order); + // 准备傿•° + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + String userIp = randomString(); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq(1L))).thenReturn(app); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode()))) + .thenReturn(channel); + // mock 方法(client) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(支付渠é“的调用) + PayOrderRespDTO unifiedOrderResp = randomPojo(PayOrderRespDTO.class, o -> o.setChannelErrorCode(null).setChannelErrorMsg(null) + .setDisplayMode(PayOrderDisplayModeEnum.URL.getMode()).setDisplayContent("tudou")); + when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> { + assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo()); + assertThat(payOrderUnifiedReqDTO) + .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime") + .containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10", + reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime()); + return true; + }))).thenReturn(unifiedOrderResp); + + // 调用 + PayOrderSubmitRespVO result = orderService.submitOrder(reqVO, userIp); + // 断言,数æ®è®°å½•(PayOrderExtensionDO) + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectOne(null); + assertNotNull(orderExtension); + assertThat(orderExtension).extracting("no", "orderId").isNotNull(); + assertThat(orderExtension) + .extracting("channelId", "channelCode","userIp" ,"status", "channelExtras", + "channelErrorCode", "channelErrorMsg", "channelNotifyData") + .containsExactly(10L, PayChannelEnum.ALIPAY_APP.getCode(), userIp, + PayOrderStatusEnum.WAITING.getStatus(), reqVO.getChannelExtras(), + null, null, null); + // 断言,返回(PayOrderSubmitRespVO) + assertThat(result) + .extracting("status", "displayMode", "displayContent") + .containsExactly(PayOrderStatusEnum.WAITING.getStatus(), PayOrderDisplayModeEnum.URL.getMode(), "tudou"); + // 断言,调用 + verify(payOrderServiceImpl).notifyOrder(same(channel), same(unifiedOrderResp)); + } + } + + @Test + public void testValidateOrderActuallyPaid_dbPaid() { + // 准备傿•° + Long id = randomLongId(); + // mock 方法(OrderExtension 已支付) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setOrderId(id).setStatus(PayOrderStatusEnum.SUCCESS.getStatus())); + orderExtensionMapper.insert(orderExtension); + + // 调用,并断言异常 + assertServiceException(() -> orderService.validateOrderActuallyPaid(id), + PAY_ORDER_EXTENSION_IS_PAID); + } + + @Test + public void testValidateOrderActuallyPaid_remotePaid() { + // 准备傿•° + Long id = randomLongId(); + // mock 方法(OrderExtension 已支付) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setOrderId(id).setStatus(PayOrderStatusEnum.WAITING.getStatus())); + orderExtensionMapper.insert(orderExtension); + // mock 方法(PayClient 已支付) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(orderExtension.getChannelId()))).thenReturn(client); + when(client.getOrder(eq(orderExtension.getNo()))).thenReturn(randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()))); + + // 调用,并断言异常 + assertServiceException(() -> orderService.validateOrderActuallyPaid(id), + PAY_ORDER_EXTENSION_IS_PAID); + } + + @Test + public void testValidateOrderActuallyPaid_success() { + // 准备傿•° + Long id = randomLongId(); + // mock 方法(OrderExtension 已支付) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setOrderId(id).setStatus(PayOrderStatusEnum.WAITING.getStatus())); + orderExtensionMapper.insert(orderExtension); + // mock 方法(PayClient 已支付) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(orderExtension.getChannelId()))).thenReturn(client); + when(client.getOrder(eq(orderExtension.getNo()))).thenReturn(randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()))); + + // 调用,并断言异常 + orderService.validateOrderActuallyPaid(id); + } + + @Test + public void testNotifyOrder_channelId() { + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) + .thenReturn(payOrderServiceImpl); + // 准备傿•° + Long channelId = 10L; + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); + + // 调用 + orderService.notifyOrder(channelId, notify); + // 断言 + verify(payOrderServiceImpl).notifyOrder(same(channel), same(notify)); + } + } + + @Test + public void testNotifyOrderSuccess_orderExtension_notFound() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + PAY_ORDER_EXTENSION_NOT_FOUND); + } + + @Test + public void testNotifyOrderSuccess_orderExtension_closed() { + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) + .setNo("P110")); + orderExtensionMapper.insert(orderExtension); + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + } + + @Test + public void testNotifyOrderSuccess_order_notFound() { + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setNo("P110")); + orderExtensionMapper.insert(orderExtension); + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + PAY_ORDER_NOT_FOUND); + // 断言 PayOrderExtensionDO ï¼šæ•°æ®æ›´æ–°è¢«å›žæ»š + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); + } + + @Test + public void testNotifyOrderSuccess_order_closed() { + testNotifyOrderSuccess_order_closedOrRefund(PayOrderStatusEnum.CLOSED.getStatus()); + } + + @Test + public void testNotifyOrderSuccess_order_refund() { + testNotifyOrderSuccess_order_closedOrRefund(PayOrderStatusEnum.REFUND.getStatus()); + } + + private void testNotifyOrderSuccess_order_closedOrRefund(Integer status) { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(status)); + orderMapper.insert(order); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setNo("P110") + .setOrderId(order.getId())); + orderExtensionMapper.insert(orderExtension); + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + PAY_ORDER_STATUS_IS_NOT_WAITING); + // 断言 PayOrderExtensionDO ï¼šæ•°æ®æœªæ›´æ–°ï¼Œå› ä¸ºå®ƒæ˜¯ SUCCESS + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); + } + + @Test + public void testNotifyOrderSuccess_order_paid() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())); + orderMapper.insert(order); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setNo("P110") + .setOrderId(order.getId())); + orderExtensionMapper.insert(orderExtension); + // é‡è¦ï¼šéœ€è¦å°† order çš„ extensionId 更新下 + order.setExtensionId(orderExtension.getId()); + orderMapper.updateById(order); + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + orderService.notifyOrder(channel, notify); + // 断言 PayOrderExtensionDO ï¼šæ•°æ®æœªæ›´æ–°ï¼Œå› ä¸ºå®ƒæ˜¯ SUCCESS + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); + // 断言 PayOrderDO ï¼šæ•°æ®æœªæ›´æ–°ï¼Œå› ä¸ºå®ƒæ˜¯ SUCCESS + assertPojoEquals(order, orderMapper.selectOne(null)); + // 断言,调用 + verify(notifyService, never()).createPayNotifyTask(anyInt(), anyLong()); + } + + @Test + public void testNotifyOrderSuccess_order_waiting() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setPrice(10)); + orderMapper.insert(order); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setNo("P110") + .setOrderId(order.getId())); + orderExtensionMapper.insert(orderExtension); + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setFeeRate(10D)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + orderService.notifyOrder(channel, notify); + // 断言 PayOrderExtensionDO ï¼šæ•°æ®æœªæ›´æ–°ï¼Œå› ä¸ºå®ƒæ˜¯ SUCCESS + orderExtension.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setChannelNotifyData(toJsonString(notify)); + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null), + "updateTime", "updater"); + // 断言 PayOrderDO ï¼šæ•°æ®æœªæ›´æ–°ï¼Œå› ä¸ºå®ƒæ˜¯ SUCCESS + order.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setChannelId(10L).setChannelCode(channel.getCode()) + .setSuccessTime(notify.getSuccessTime()).setExtensionId(orderExtension.getId()).setNo(orderExtension.getNo()) + .setChannelOrderNo(notify.getChannelOrderNo()).setChannelUserId(notify.getChannelUserId()) + .setChannelFeeRate(10D).setChannelFeePrice(1); + assertPojoEquals(order, orderMapper.selectOne(null), + "updateTime", "updater"); + // 断言,调用 + verify(notifyService).createPayNotifyTask(eq(PayNotifyTypeEnum.ORDER.getType()), + eq(orderExtension.getOrderId())); + } + + @Test + public void testNotifyOrderClosed_orderExtension_notFound() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus())); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + PAY_ORDER_EXTENSION_NOT_FOUND); + } + + @Test + public void testNotifyOrderClosed_orderExtension_closed() { + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) + .setNo("P110")); + orderExtensionMapper.insert(orderExtension); + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言 + orderService.notifyOrder(channel, notify); + // 断言 PayOrderExtensionDO ï¼šæ•°æ®æœªæ›´æ–°ï¼Œå› ä¸ºå®ƒæ˜¯ CLOSED + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); + } + + @Test + public void testNotifyOrderClosed_orderExtension_paid() { + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setNo("P110")); + orderExtensionMapper.insert(orderExtension); + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言 + orderService.notifyOrder(channel, notify); + // 断言 PayOrderExtensionDO ï¼šæ•°æ®æœªæ›´æ–°ï¼Œå› ä¸ºå®ƒæ˜¯ SUCCESS + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); + } + + @Test + public void testNotifyOrderClosed_orderExtension_refund() { + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.REFUND.getStatus()) + .setNo("P110")); + orderExtensionMapper.insert(orderExtension); + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + } + + @Test + public void testNotifyOrderClosed_orderExtension_waiting() { + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setNo("P110")); + orderExtensionMapper.insert(orderExtension); + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus()) + .setOutTradeNo("P110")); + + // 调用 + orderService.notifyOrder(channel, notify); + // 断言 PayOrderExtensionDO + orderExtension.setStatus(PayOrderStatusEnum.CLOSED.getStatus()).setChannelNotifyData(toJsonString(notify)) + .setChannelErrorCode(notify.getChannelErrorCode()).setChannelErrorMsg(notify.getChannelErrorMsg()); + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null), + "updateTime", "updater"); + } + + @Test + public void testUpdateOrderRefundPrice_notFound() { + // 准备傿•° + Long id = randomLongId(); + Integer incrRefundPrice = randomInteger(); + + // 调用,并断言异常 + assertServiceException(() -> orderService.updateOrderRefundPrice(id, incrRefundPrice), + PAY_ORDER_NOT_FOUND); + } + + @Test + public void testUpdateOrderRefundPrice_waiting() { + testUpdateOrderRefundPrice_waitingOrClosed(PayOrderStatusEnum.WAITING.getStatus()); + } + + @Test + public void testUpdateOrderRefundPrice_closed() { + testUpdateOrderRefundPrice_waitingOrClosed(PayOrderStatusEnum.CLOSED.getStatus()); + } + + private void testUpdateOrderRefundPrice_waitingOrClosed(Integer status) { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(status)); + orderMapper.insert(order); + // 准备傿•° + Long id = order.getId(); + Integer incrRefundPrice = randomInteger(); + + // 调用,并断言异常 + assertServiceException(() -> orderService.updateOrderRefundPrice(id, incrRefundPrice), + PAY_ORDER_REFUND_FAIL_STATUS_ERROR); + } + + @Test + public void testUpdateOrderRefundPrice_priceExceed() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setRefundPrice(1).setPrice(10)); + orderMapper.insert(order); + // 准备傿•° + Long id = order.getId(); + Integer incrRefundPrice = 10; + + // 调用,并断言异常 + assertServiceException(() -> orderService.updateOrderRefundPrice(id, incrRefundPrice), + REFUND_PRICE_EXCEED); + } + + @Test + public void testUpdateOrderRefundPrice_refund() { + testUpdateOrderRefundPrice_refundOrSuccess(PayOrderStatusEnum.REFUND.getStatus()); + } + + @Test + public void testUpdateOrderRefundPrice_success() { + testUpdateOrderRefundPrice_refundOrSuccess(PayOrderStatusEnum.SUCCESS.getStatus()); + } + + private void testUpdateOrderRefundPrice_refundOrSuccess(Integer status) { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(status).setRefundPrice(1).setPrice(10)); + orderMapper.insert(order); + // 准备傿•° + Long id = order.getId(); + Integer incrRefundPrice = 8; + + // 调用 + orderService.updateOrderRefundPrice(id, incrRefundPrice); + // 断言 + order.setRefundPrice(9).setStatus(PayOrderStatusEnum.REFUND.getStatus()); + assertPojoEquals(order, orderMapper.selectOne(null), + "updateTime", "updater"); + } + + @Test + public void testGetOrderExtension() { + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class); + orderExtensionMapper.insert(orderExtension); + // 准备傿•° + Long id = orderExtension.getId(); + + // 调用 + PayOrderExtensionDO dbOrderExtension = orderService.getOrderExtension(id); + // 断言 + assertPojoEquals(dbOrderExtension, orderExtension); + } + + @Test + public void testSyncOrder_payClientNotFound() { + // 准备傿•° + LocalDateTime minCreateTime = LocalDateTime.now().minus(Duration.ofMinutes(10)); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setCreateTime(LocalDateTime.now())); + orderExtensionMapper.insert(orderExtension); + + // 调用 + int count = orderService.syncOrder(minCreateTime); + // 断言 + assertEquals(count, 0); + } + + @Test + public void testSyncOrder_exception() { + // 准备傿•° + LocalDateTime minCreateTime = LocalDateTime.now().minus(Duration.ofMinutes(10)); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setChannelId(10L) + .setCreateTime(LocalDateTime.now())); + orderExtensionMapper.insert(orderExtension); + // mock 方法(PayClient) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(PayClient 异常) + when(client.getOrder(any())).thenThrow(new RuntimeException()); + + // 调用 + int count = orderService.syncOrder(minCreateTime); + // 断言 + assertEquals(count, 0); + } + + @Test + public void testSyncOrder_orderSuccess() { + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) + .thenReturn(payOrderServiceImpl); + + // 准备傿•° + LocalDateTime minCreateTime = LocalDateTime.now().minus(Duration.ofMinutes(10)); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setChannelId(10L).setNo("P110") + .setCreateTime(LocalDateTime.now())); + orderExtensionMapper.insert(orderExtension); + // mock 方法(PayClient) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(PayClient æˆåŠŸè¿”å›žï¼‰ + PayOrderRespDTO respDTO = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())); + when(client.getOrder(eq("P110"))).thenReturn(respDTO); + // mock 方法(PayChannelDO) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); + + // 调用 + int count = orderService.syncOrder(minCreateTime); + // 断言 + assertEquals(count, 1); + verify(payOrderServiceImpl).notifyOrder(same(channel), same(respDTO)); + } + } + + @Test + public void testSyncOrder_orderClosed() { + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) + .thenReturn(payOrderServiceImpl); + + // 准备傿•° + LocalDateTime minCreateTime = LocalDateTime.now().minus(Duration.ofMinutes(10)); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setChannelId(10L).setNo("P110") + .setCreateTime(LocalDateTime.now())); + orderExtensionMapper.insert(orderExtension); + // mock 方法(PayClient) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(PayClient æˆåŠŸè¿”å›žï¼‰ + PayOrderRespDTO respDTO = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus())); + when(client.getOrder(eq("P110"))).thenReturn(respDTO); + // mock 方法(PayChannelDO) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); + + // 调用 + int count = orderService.syncOrder(minCreateTime); + // 断言 + assertEquals(count, 0); + verify(payOrderServiceImpl, never()).notifyOrder(same(channel), same(respDTO)); + } + } + + @Test + public void testExpireOrder_orderExtension_isSuccess() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setExpireTime(addTime(Duration.ofMinutes(-1)))); + orderMapper.insert(order); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO 已支付) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setOrderId(order.getId())); + orderExtensionMapper.insert(orderExtension); + // mock 方法(PayClient) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + + // 调用 + int count = orderService.expireOrder(); + // 断言 + assertEquals(count, 0); + // 断言 order 没有å˜åŒ–,因为没更新 + assertPojoEquals(order, orderMapper.selectOne(null)); + } + + @Test + public void testExpireOrder_payClient_notFound() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setExpireTime(addTime(Duration.ofMinutes(-1)))); + orderMapper.insert(order); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO 等待中) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setOrderId(order.getId()) + .setChannelId(10L)); + orderExtensionMapper.insert(orderExtension); + + // 调用 + int count = orderService.expireOrder(); + // 断言 + assertEquals(count, 0); + // 断言 order 没有å˜åŒ–,因为没更新 + assertPojoEquals(order, orderMapper.selectOne(null)); + } + + @Test + public void testExpireOrder_getOrder_isRefund() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setExpireTime(addTime(Duration.ofMinutes(-1)))); + orderMapper.insert(order); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO 等待中) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setOrderId(order.getId()).setNo("P110") + .setChannelId(10L)); + orderExtensionMapper.insert(orderExtension); + // mock 方法(PayClient) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(PayClient 退款返回) + PayOrderRespDTO respDTO = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusEnum.REFUND.getStatus())); + when(client.getOrder(eq("P110"))).thenReturn(respDTO); + + // 调用 + int count = orderService.expireOrder(); + // 断言 + assertEquals(count, 0); + // 断言 order 没有å˜åŒ–,因为没更新 + assertPojoEquals(order, orderMapper.selectOne(null)); + } + + @Test + public void testExpireOrder_getOrder_isSuccess() { + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) + .thenReturn(payOrderServiceImpl); + + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setExpireTime(addTime(Duration.ofMinutes(-1)))); + orderMapper.insert(order); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO 等待中) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setOrderId(order.getId()).setNo("P110") + .setChannelId(10L)); + orderExtensionMapper.insert(orderExtension); + // mock 方法(PayClient) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(PayClient æˆåŠŸè¿”å›žï¼‰ + PayOrderRespDTO respDTO = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())); + when(client.getOrder(eq("P110"))).thenReturn(respDTO); + // mock 方法(PayChannelDO) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); + + // 调用 + int count = orderService.expireOrder(); + // 断言 + assertEquals(count, 0); + // 断言 order 没有å˜åŒ–,因为没更新 + assertPojoEquals(order, orderMapper.selectOne(null)); + verify(payOrderServiceImpl).notifyOrder(same(channel), same(respDTO)); + } + } + + @Test + public void testExpireOrder_success() { + // mock æ•°æ®ï¼ˆPayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setExpireTime(addTime(Duration.ofMinutes(-1)))); + orderMapper.insert(order); + // mock æ•°æ®ï¼ˆPayOrderExtensionDO 等待中) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setOrderId(order.getId()).setNo("P110") + .setChannelId(10L)); + orderExtensionMapper.insert(orderExtension); + // mock 方法(PayClient) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(PayClient 关闭返回) + PayOrderRespDTO respDTO = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus())); + when(client.getOrder(eq("P110"))).thenReturn(respDTO); + + // 调用 + int count = orderService.expireOrder(); + // 断言 + assertEquals(count, 1); + // 断言 extension å˜åŒ– + orderExtension.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) + .setChannelNotifyData(toJsonString(respDTO)); + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null), + "updateTime", "updater"); + // 断言 order å˜åŒ– + order.setStatus(PayOrderStatusEnum.CLOSED.getStatus()); + assertPojoEquals(order, orderMapper.selectOne(null), + "updateTime", "updater"); + } + +} diff --git a/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/refund/PayRefundServiceTest.java b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/refund/PayRefundServiceTest.java new file mode 100644 index 0000000..3451db5 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/test/java/com/tashow/cloud/pay/service/refund/PayRefundServiceTest.java @@ -0,0 +1,700 @@ +package com.tashow.cloud.pay.service.refund; + +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundExportReqVO; +import com.tashow.cloud.pay.controller.admin.refund.vo.PayRefundPageReqVO; +import com.tashow.cloud.pay.dal.dataobject.app.PayAppDO; +import com.tashow.cloud.pay.dal.dataobject.channel.PayChannelDO; +import com.tashow.cloud.pay.dal.dataobject.order.PayOrderDO; +import com.tashow.cloud.pay.dal.dataobject.refund.PayRefundDO; +import com.tashow.cloud.pay.dal.mysql.refund.PayRefundMapper; +import com.tashow.cloud.pay.dal.redis.no.PayNoRedisDAO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; +import com.tashow.cloud.pay.framework.pay.config.PayProperties; +import com.tashow.cloud.pay.service.app.PayAppService; +import com.tashow.cloud.pay.service.channel.PayChannelService; +import com.tashow.cloud.pay.service.notify.PayNotifyService; +import com.tashow.cloud.pay.service.order.PayOrderService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +/** + * {@link PayRefundServiceImpl} çš„å•元测试类 + * + * @author 芋艿 + */ +@Import({PayRefundServiceImpl.class, PayNoRedisDAO.class}) +public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { + + @Resource + private PayRefundServiceImpl refundService; + + @Resource + private PayRefundMapper refundMapper; + + @MockBean + private PayProperties payProperties; + @MockBean + private PayOrderService orderService; + @MockBean + private PayAppService appService; + @MockBean + private PayChannelService channelService; + @MockBean + private PayNotifyService notifyService; + + @BeforeEach + public void setUp() { + when(payProperties.getRefundNotifyUrl()).thenReturn("http://127.0.0.1"); + } + + @Test + public void testGetRefund() { + // mock æ•°æ® + PayRefundDO refund = randomPojo(PayRefundDO.class); + refundMapper.insert(refund); + // 准备傿•° + Long id = refund.getId(); + + // 调用 + PayRefundDO dbRefund = refundService.getRefund(id); + // 断言 + assertPojoEquals(dbRefund, refund); + } + + @Test + public void testGetRefundCountByAppId() { + // mock æ•°æ® + PayRefundDO refund01 = randomPojo(PayRefundDO.class); + refundMapper.insert(refund01); + PayRefundDO refund02 = randomPojo(PayRefundDO.class); + refundMapper.insert(refund02); + // 准备傿•° + Long appId = refund01.getAppId(); + + // 调用 + Long count = refundService.getRefundCountByAppId(appId); + // 断言 + assertEquals(count, 1); + } + + @Test + public void testGetRefundPage() { + // mock æ•°æ® + PayRefundDO dbRefund = randomPojo(PayRefundDO.class, o -> { // 等会查询到 + o.setAppId(1L); + o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); + o.setMerchantOrderId("MOT0000001"); + o.setMerchantRefundId("MRF0000001"); + o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + o.setChannelOrderNo("CH0000001"); + o.setChannelRefundNo("CHR0000001"); + o.setCreateTime(buildTime(2021, 1, 10)); + }); + refundMapper.insert(dbRefund); + // 测试 appId ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setAppId(2L))); + // 测试 channelCode ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); + // 测试 merchantOrderId ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantOrderId(randomString()))); + // 测试 merchantRefundId ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId(randomString()))); + // 测试 channelOrderNo ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelOrderNo(randomString()))); + // 测试 channelRefundNo ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelRefundNo(randomString()))); + // 测试 status ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()))); + // 测试 createTime ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(buildTime(2021, 1, 1)))); + // 准备傿•° + PayRefundPageReqVO reqVO = new PayRefundPageReqVO(); + reqVO.setAppId(1L); + reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode()); + reqVO.setMerchantOrderId("MOT0000001"); + reqVO.setMerchantRefundId("MRF0000001"); + reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + reqVO.setChannelOrderNo("CH0000001"); + reqVO.setChannelRefundNo("CHR0000001"); + reqVO.setCreateTime(buildBetweenTime(2021, 1, 9, 2021, 1, 11)); + + // 调用 + PageResult pageResult = refundService.getRefundPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbRefund, pageResult.getList().get(0)); + } + + @Test + public void testGetRefundList() { + // mock æ•°æ® + PayRefundDO dbRefund = randomPojo(PayRefundDO.class, o -> { // 等会查询到 + o.setAppId(1L); + o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); + o.setMerchantOrderId("MOT0000001"); + o.setMerchantRefundId("MRF0000001"); + o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + o.setChannelOrderNo("CH0000001"); + o.setChannelRefundNo("CHR0000001"); + o.setCreateTime(buildTime(2021, 1, 10)); + }); + refundMapper.insert(dbRefund); + // 测试 appId ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setAppId(2L))); + // 测试 channelCode ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); + // 测试 merchantOrderId ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantOrderId(randomString()))); + // 测试 merchantRefundId ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId(randomString()))); + // 测试 channelOrderNo ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelOrderNo(randomString()))); + // 测试 channelRefundNo ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelRefundNo(randomString()))); + // 测试 status ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()))); + // 测试 createTime ä¸åŒ¹é… + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(buildTime(2021, 1, 1)))); + // 准备傿•° + PayRefundExportReqVO reqVO = new PayRefundExportReqVO(); + reqVO.setAppId(1L); + reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode()); + reqVO.setMerchantOrderId("MOT0000001"); + reqVO.setMerchantRefundId("MRF0000001"); + reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + reqVO.setChannelOrderNo("CH0000001"); + reqVO.setChannelRefundNo("CHR0000001"); + reqVO.setCreateTime(buildBetweenTime(2021, 1, 9, 2021, 1, 11)); + + // 调用 + List list = refundService.getRefundList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbRefund, list.get(0)); + } + + @Test + public void testCreateRefund_orderNotFound() { + PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class, + o -> o.setAppKey("demo")); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq("demo"))).thenReturn(app); + + // 调用,并断言异常 + assertServiceException(() -> refundService.createPayRefund(reqDTO), + PAY_ORDER_NOT_FOUND); + } + + @Test + public void testCreateRefund_orderWaiting() { + testCreateRefund_orderWaitingOrClosed(PayOrderStatusEnum.WAITING.getStatus()); + } + + @Test + public void testCreateRefund_orderClosed() { + testCreateRefund_orderWaitingOrClosed(PayOrderStatusEnum.CLOSED.getStatus()); + } + + private void testCreateRefund_orderWaitingOrClosed(Integer status) { + // 准备傿•° + PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class, + o -> o.setAppKey("demo").setMerchantOrderId("100")); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq("demo"))).thenReturn(app); + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(status)); + when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order); + + // 调用,并断言异常 + assertServiceException(() -> refundService.createPayRefund(reqDTO), + PAY_ORDER_REFUND_FAIL_STATUS_ERROR); + } + + @Test + public void testCreateRefund_refundPriceExceed() { + // 准备傿•° + PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class, + o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(10)); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq("demo"))).thenReturn(app); + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> + o.setStatus(PayOrderStatusEnum.REFUND.getStatus()) + .setPrice(10).setRefundPrice(1)); + when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order); + + // 调用,并断言异常 + assertServiceException(() -> refundService.createPayRefund(reqDTO), + REFUND_PRICE_EXCEED); + } + + @Test + public void testCreateRefund_orderHasRefunding() { + // 准备傿•° + PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class, + o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(10)); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq("demo"))).thenReturn(app); + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> + o.setStatus(PayOrderStatusEnum.REFUND.getStatus()) + .setPrice(10).setRefundPrice(1)); + when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order); + // mock æ•°æ®ï¼ˆrefund 在退款中) + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> + o.setOrderId(order.getId()).setStatus(PayOrderStatusEnum.WAITING.getStatus())); + refundMapper.insert(refund); + + // 调用,并断言异常 + assertServiceException(() -> refundService.createPayRefund(reqDTO), + REFUND_PRICE_EXCEED); + } + + @Test + public void testCreateRefund_channelNotFound() { + // 准备傿•° + PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class, + o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9)); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq("demo"))).thenReturn(app); + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> + o.setStatus(PayOrderStatusEnum.REFUND.getStatus()) + .setPrice(10).setRefundPrice(1) + .setChannelId(1L).setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(1L))).thenReturn(channel); + + // 调用,并断言异常 + assertServiceException(() -> refundService.createPayRefund(reqDTO), + CHANNEL_NOT_FOUND); + } + + @Test + public void testCreateRefund_refundExists() { + // 准备傿•° + PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class, + o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9) + .setMerchantRefundId("200").setReason("测试退款")); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq("demo"))).thenReturn(app); + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> + o.setStatus(PayOrderStatusEnum.REFUND.getStatus()) + .setPrice(10).setRefundPrice(1) + .setChannelId(1L).setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(1L))).thenReturn(channel); + // mock 方法(client) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock æ•°æ®ï¼ˆrefund 已存在) + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> + o.setAppId(1L).setMerchantRefundId("200")); + refundMapper.insert(refund); + + // 调用,并断言异常 + assertServiceException(() -> refundService.createPayRefund(reqDTO), + REFUND_EXISTS); + } + + @Test + public void testCreateRefund_invokeException() { + // 准备傿•° + PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class, + o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9) + .setMerchantRefundId("200").setReason("测试退款")); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq("demo"))).thenReturn(app); + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> + o.setStatus(PayOrderStatusEnum.REFUND.getStatus()) + .setPrice(10).setRefundPrice(1) + .setChannelId(10L).setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); + // mock 方法(client) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(client 调用å‘生异常) + when(client.unifiedRefund(any(PayRefundUnifiedReqDTO.class))).thenThrow(new RuntimeException()); + + // 调用 + Long refundId = refundService.createPayRefund(reqDTO); + // 断言 + PayRefundDO refundDO = refundMapper.selectById(refundId); + assertPojoEquals(reqDTO, refundDO); + assertNotNull(refundDO.getNo()); + assertThat(refundDO) + .extracting("orderId", "orderNo", "channelId", "channelCode", + "notifyUrl", "channelOrderNo", "status", "payPrice", "refundPrice") + .containsExactly(order.getId(), order.getNo(), channel.getId(), channel.getCode(), + app.getRefundNotifyUrl(), order.getChannelOrderNo(), PayRefundStatusEnum.WAITING.getStatus(), + order.getPrice(), reqDTO.getPrice()); + } + + @Test + public void testCreateRefund_invokeSuccess() { + PayRefundServiceImpl payRefundServiceImpl = mock(PayRefundServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayRefundServiceImpl.class))) + .thenReturn(payRefundServiceImpl); + + // 准备傿•° + PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class, + o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9) + .setMerchantRefundId("200").setReason("测试退款")); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq("demo"))).thenReturn(app); + // mock æ•°æ®ï¼ˆorder) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> + o.setStatus(PayOrderStatusEnum.REFUND.getStatus()) + .setPrice(10).setRefundPrice(1) + .setChannelId(10L).setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); + // mock 方法(client) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(client æˆåŠŸï¼‰ + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class); + when(client.unifiedRefund(argThat(unifiedReqDTO -> { + assertNotNull(unifiedReqDTO.getOutRefundNo()); + assertThat(unifiedReqDTO) + .extracting("payPrice", "refundPrice", "outTradeNo", + "notifyUrl", "reason") + .containsExactly(order.getPrice(), reqDTO.getPrice(), order.getNo(), + "http://127.0.0.1/10", reqDTO.getReason()); + return true; + }))).thenReturn(refundRespDTO); + + // 调用 + Long refundId = refundService.createPayRefund(reqDTO); + // 断言 + PayRefundDO refundDO = refundMapper.selectById(refundId); + assertPojoEquals(reqDTO, refundDO); + assertNotNull(refundDO.getNo()); + assertThat(refundDO) + .extracting("orderId", "orderNo", "channelId", "channelCode", + "notifyUrl", "channelOrderNo", "status", "payPrice", "refundPrice") + .containsExactly(order.getId(), order.getNo(), channel.getId(), channel.getCode(), + app.getRefundNotifyUrl(), order.getChannelOrderNo(), PayRefundStatusEnum.WAITING.getStatus(), + order.getPrice(), reqDTO.getPrice()); + // 断言调用 + verify(payRefundServiceImpl).notifyRefund(same(channel), same(refundRespDTO)); + } + } + + @Test + public void testNotifyRefund() { + PayRefundServiceImpl payRefundServiceImpl = mock(PayRefundServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayRefundServiceImpl.class))) + .thenReturn(payRefundServiceImpl); + + // 准备傿•° + Long channelId = 10L; + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); + + // 调用 + refundService.notifyRefund(channelId, refundRespDTO); + // 断言 + verify(payRefundServiceImpl).notifyRefund(same(channel), same(refundRespDTO)); + } + } + + @Test + public void testNotifyRefundSuccess_notFound() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L)); + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class, + o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100")); + + // 调用,并断言异常 + assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO), + REFUND_NOT_FOUND); + } + + @Test + public void testNotifyRefundSuccess_isSuccess() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L)); + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class, + o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100")); + // mock æ•°æ®ï¼ˆrefund + 已支付) + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100") + .setStatus(PayRefundStatusEnum.SUCCESS.getStatus())); + refundMapper.insert(refund); + + // 调用 + refundService.notifyRefund(channel, refundRespDTO); + // 断言,refund 没有更新,因为已ç»é€€æ¬¾æˆåŠŸ + assertPojoEquals(refund, refundMapper.selectById(refund.getId())); + } + + @Test + public void testNotifyRefundSuccess_failure() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L)); + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class, + o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100")); + // mock æ•°æ®ï¼ˆrefund + 已支付) + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100") + .setStatus(PayRefundStatusEnum.FAILURE.getStatus())); + refundMapper.insert(refund); + + // 调用,并断言异常 + assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO), + REFUND_STATUS_IS_NOT_WAITING); + } + + @Test + public void testNotifyRefundSuccess_updateOrderException() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L)); + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class, + o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100")); + // mock æ•°æ®ï¼ˆrefund + 已支付) + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100") + .setStatus(PayRefundStatusEnum.WAITING.getStatus()) + .setOrderId(100L).setRefundPrice(23)); + refundMapper.insert(refund); + // mock 方法(order + 更新异常) + doThrow(new RuntimeException()).when(orderService) + .updateOrderRefundPrice(eq(100L), eq(23)); + + // 调用,并断言异常 + assertThrows(RuntimeException.class, () -> refundService.notifyRefund(channel, refundRespDTO)); + // 断言,refund 没有更新,因为事务回滚了 + assertPojoEquals(refund, refundMapper.selectById(refund.getId())); + } + + @Test + public void testNotifyRefundSuccess_success() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L)); + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class, + o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100")); + // mock æ•°æ®ï¼ˆrefund + 已支付) + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100") + .setStatus(PayRefundStatusEnum.WAITING.getStatus()) + .setOrderId(100L).setRefundPrice(23)); + refundMapper.insert(refund); + + // 调用 + refundService.notifyRefund(channel, refundRespDTO); + // 断言,refund + refund.setSuccessTime(refundRespDTO.getSuccessTime()) + .setChannelRefundNo(refundRespDTO.getChannelRefundNo()) + .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()) + .setChannelNotifyData(toJsonString(refundRespDTO)); + assertPojoEquals(refund, refundMapper.selectById(refund.getId()), + "updateTime", "updater"); + // 断言,调用 + verify(orderService).updateOrderRefundPrice(eq(100L), eq(23)); + verify(notifyService).createPayNotifyTask(eq(PayNotifyTypeEnum.REFUND.getType()), + eq(refund.getId())); + } + + @Test + public void testNotifyRefundFailure_notFound() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L)); + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class, + o -> o.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()).setOutRefundNo("R100")); + + // 调用,并断言异常 + assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO), + REFUND_NOT_FOUND); + } + + @Test + public void testNotifyRefundFailure_isFailure() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L)); + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class, + o -> o.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()).setOutRefundNo("R100")); + // mock æ•°æ®ï¼ˆrefund + 退款失败) + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100") + .setStatus(PayRefundStatusEnum.FAILURE.getStatus())); + refundMapper.insert(refund); + + // 调用 + refundService.notifyRefund(channel, refundRespDTO); + // 断言,refund 没有更新,因为已ç»é€€æ¬¾å¤±è´¥ + assertPojoEquals(refund, refundMapper.selectById(refund.getId())); + } + + @Test + public void testNotifyRefundFailure_isSuccess() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L)); + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class, + o -> o.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()).setOutRefundNo("R100")); + // mock æ•°æ®ï¼ˆrefund + 已支付) + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100") + .setStatus(PayRefundStatusEnum.SUCCESS.getStatus())); + refundMapper.insert(refund); + + // 调用,并断言异常 + assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO), + REFUND_STATUS_IS_NOT_WAITING); + } + + @Test + public void testNotifyRefundFailure_success() { + // 准备傿•° + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L)); + PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class, + o -> o.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()).setOutRefundNo("R100")); + // mock æ•°æ®ï¼ˆrefund + 已支付) + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100") + .setStatus(PayRefundStatusEnum.WAITING.getStatus()) + .setOrderId(100L).setRefundPrice(23)); + refundMapper.insert(refund); + + // 调用 + refundService.notifyRefund(channel, refundRespDTO); + // 断言,refund + refund.setChannelRefundNo(refundRespDTO.getChannelRefundNo()) + .setStatus(PayRefundStatusEnum.FAILURE.getStatus()) + .setChannelNotifyData(toJsonString(refundRespDTO)) + .setChannelErrorCode(refundRespDTO.getChannelErrorCode()) + .setChannelErrorMsg(refundRespDTO.getChannelErrorMsg()); + assertPojoEquals(refund, refundMapper.selectById(refund.getId()), + "updateTime", "updater"); + // 断言,调用 + verify(notifyService).createPayNotifyTask(eq(PayNotifyTypeEnum.REFUND.getType()), + eq(refund.getId())); + } + + @Test + public void testSyncRefund_notFound() { + // 准备傿•° + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L) + .setStatus(PayRefundStatusEnum.WAITING.getStatus())); + refundMapper.insert(refund); + + // 调用 + int count = refundService.syncRefund(); + // 断言 + assertEquals(count, 0); + } + + @Test + public void testSyncRefund_waiting() { + assertEquals(testSyncRefund_waitingOrSuccessOrFailure(PayRefundStatusRespEnum.WAITING.getStatus()), 0); + } + + @Test + public void testSyncRefund_success() { + assertEquals(testSyncRefund_waitingOrSuccessOrFailure(PayRefundStatusRespEnum.SUCCESS.getStatus()), 1); + } + + @Test + public void testSyncRefund_failure() { + assertEquals(testSyncRefund_waitingOrSuccessOrFailure(PayRefundStatusRespEnum.FAILURE.getStatus()), 1); + } + + private int testSyncRefund_waitingOrSuccessOrFailure(Integer status) { + PayRefundServiceImpl payRefundServiceImpl = mock(PayRefundServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayRefundServiceImpl.class))) + .thenReturn(payRefundServiceImpl); + + // 准备傿•° + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setChannelId(10L) + .setStatus(PayRefundStatusEnum.WAITING.getStatus()) + .setOrderNo("P110").setNo("R220")); + refundMapper.insert(refund); + // mock 方法(client) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(client 返回指定状æ€ï¼‰ + PayRefundRespDTO respDTO = randomPojo(PayRefundRespDTO.class, o -> o.setStatus(status)); + when(client.getRefund(eq("P110"), eq("R220"))).thenReturn(respDTO); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); + + // 调用 + return refundService.syncRefund(); + } + } + + @Test + public void testSyncRefund_exception() { + // 准备傿•° + PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setChannelId(10L) + .setStatus(PayRefundStatusEnum.WAITING.getStatus()) + .setOrderNo("P110").setNo("R220")); + refundMapper.insert(refund); + // mock 方法(client) + PayClient client = mock(PayClient.class); + when(channelService.getPayClient(eq(10L))).thenReturn(client); + // mock 方法(client 抛出异常) + when(client.getRefund(eq("P110"), eq("R220"))).thenThrow(new RuntimeException()); + + // 调用 + int count = refundService.syncRefund(); + // 断言 + assertEquals(count, 0); + } + +} diff --git a/tashow-module/tashow-module-pay/src/test/resources/application-unit-test.yaml b/tashow-module/tashow-module-pay/src/test/resources/application-unit-test.yaml new file mode 100644 index 0000000..93023bd --- /dev/null +++ b/tashow-module/tashow-module-pay/src/test/resources/application-unit-test.yaml @@ -0,0 +1,53 @@ +spring: + main: + lazy-initialization: true # 开坿‡’加载,加快速度 + banner-mode: off # å•元测试,ç¦ç”¨ Banner + +--- #################### æ•°æ®åº“相关é…ç½® #################### + +spring: + # æ•°æ®æºé…置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模å¼ï¼›DATABASE_TO_UPPER é…置表和字段使用å°å†™ + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # å•元测试,异步åˆå§‹åŒ– Druid 连接池,æå‡å¯åŠ¨é€Ÿåº¦ + initial-size: 1 # å•元测试,é…置为 1,æå‡å¯åŠ¨é€Ÿåº¦ + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + + # Redis é…置。Redisson 默认的é…置足够使用,一般ä¸éœ€è¦è¿›è¡Œè°ƒä¼˜ + data: + redis: + host: 127.0.0.1 # åœ°å€ + port: 16379 # 端å£ï¼ˆå•元测试,使用 16379 端å£ï¼‰ + database: 0 # æ•°æ®åº“索引 + +mybatis: + lazy-initialization: true # å•元测试,设置 MyBatis Mapper 延迟加载,加速æ¯ä¸ªå•元测试 + +mybatis-plus: + global-config: + db-config: + id-type: AUTO # H2 主键递增 + +--- #################### 定时任务相关é…ç½® #################### + +--- #################### é…置中心相关é…ç½® #################### + +--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® #################### + +# Lock4j é…置项(å•元测试,ç¦ç”¨ Lock4j) + +--- #################### 监控相关é…ç½® #################### + +--- #################### 芋é“相关é…ç½® #################### + +# 芋é“é…置项,设置当å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½® +yudao: + info: + base-package: cn.iocoder.yudao.module diff --git a/tashow-module/tashow-module-pay/src/test/resources/logback.xml b/tashow-module/tashow-module-pay/src/test/resources/logback.xml new file mode 100644 index 0000000..daf756b --- /dev/null +++ b/tashow-module/tashow-module-pay/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tashow-module/tashow-module-pay/src/test/resources/sql/clean.sql b/tashow-module/tashow-module-pay/src/test/resources/sql/clean.sql new file mode 100644 index 0000000..91fff0c --- /dev/null +++ b/tashow-module/tashow-module-pay/src/test/resources/sql/clean.sql @@ -0,0 +1,7 @@ +DELETE FROM pay_app; +DELETE FROM pay_channel; +DELETE FROM pay_order; +DELETE FROM pay_order_extension; +DELETE FROM pay_refund; +DELETE FROM pay_notify_task; +DELETE FROM pay_notify_log; diff --git a/tashow-module/tashow-module-pay/src/test/resources/sql/create_tables.sql b/tashow-module/tashow-module-pay/src/test/resources/sql/create_tables.sql new file mode 100644 index 0000000..3f9f764 --- /dev/null +++ b/tashow-module/tashow-module-pay/src/test/resources/sql/create_tables.sql @@ -0,0 +1,147 @@ +CREATE TABLE IF NOT EXISTS "pay_app" ( + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "app_key" varchar(64) NOT NULL, + "name" varchar(64) NOT NULL, + "status" tinyint NOT NULL, + "remark" varchar(255) DEFAULT NULL, + `order_notify_url` varchar(1024) NOT NULL, + `refund_notify_url` varchar(1024) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit(1) NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT = '支付应用'; + +CREATE TABLE IF NOT EXISTS "pay_channel" ( + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(32) NOT NULL, + "status" tinyint(4) NOT NULL, + "remark" varchar(255) DEFAULT NULL, + "fee_rate" double NOT NULL DEFAULT 0, + "app_id" bigint(20) NOT NULL, + "config" varchar(10240) NOT NULL, + "creator" varchar(64) NULL DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) NULL DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit(1) NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT = '支付渠é“'; + +CREATE TABLE IF NOT EXISTS `pay_order` ( + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, + `app_id` bigint(20) NOT NULL, + `channel_id` bigint(20) DEFAULT NULL, + `channel_code` varchar(32) DEFAULT NULL, + `merchant_order_id` varchar(64) NOT NULL, + `subject` varchar(32) NOT NULL, + `body` varchar(128) NOT NULL, + `notify_url` varchar(1024) NOT NULL, + `price` bigint(20) NOT NULL, + `channel_fee_rate` double DEFAULT 0, + `channel_fee_price` bigint(20) DEFAULT 0, + `status` tinyint(4) NOT NULL, + `user_ip` varchar(50) NOT NULL, + `expire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `success_time` datetime(0) DEFAULT CURRENT_TIMESTAMP, + `notify_time` datetime(0) DEFAULT CURRENT_TIMESTAMP, + `extension_id` bigint(20) DEFAULT NULL, + `no` varchar(64) NULL, + `refund_price` bigint(20) NOT NULL, + `channel_user_id` varchar(255) DEFAULT NULL, + `channel_order_no` varchar(64) DEFAULT NULL, + `creator` varchar(64) DEFAULT '', + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater` varchar(64) DEFAULT '', + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` bit(1) NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT = '支付订å•'; + +CREATE TABLE IF NOT EXISTS `pay_order_extension` ( + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, + `no` varchar(64) NOT NULL, + `order_id` bigint(20) NOT NULL, + `channel_id` bigint(20) NOT NULL, + `channel_code` varchar(32) NOT NULL, + `user_ip` varchar(50) NULL DEFAULT NULL, + `status` tinyint(4) NOT NULL, + `channel_extras` varchar(1024) NULL DEFAULT NULL, + `channel_error_code` varchar(64) NULL, + `channel_error_msg` varchar(64) NULL, + `channel_notify_data` varchar(1024) NULL, + `creator` varchar(64) NULL DEFAULT '', + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater` varchar(64) NULL DEFAULT '', + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` bit(1) NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT = 'æ”¯ä»˜è®¢å•æ‹“展'; + +CREATE TABLE IF NOT EXISTS `pay_refund` ( + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, + `no` varchar(64) NOT NULL, + `app_id` bigint(20) NOT NULL, + `channel_id` bigint(20) NOT NULL, + `channel_code` varchar(32) NOT NULL, + `order_id` bigint(20) NOT NULL, + `order_no` varchar(64) NOT NULL, + `merchant_order_id` varchar(64) NOT NULL, + `merchant_refund_id` varchar(64) NOT NULL, + `notify_url` varchar(1024) NOT NULL, + `status` tinyint(4) NOT NULL, + `pay_price` bigint(20) NOT NULL, + `refund_price` bigint(20) NOT NULL, + `reason` varchar(256) NOT NULL, + `user_ip` varchar(50) NULL DEFAULT NULL, + `channel_order_no` varchar(64) NOT NULL, + `channel_refund_no` varchar(64) NULL DEFAULT NULL, + `success_time` datetime(0) NULL DEFAULT NULL, + `channel_error_code` varchar(128) NULL DEFAULT NULL, + `channel_error_msg` varchar(256) NULL DEFAULT NULL, + `channel_notify_data` varchar(1024) NULL, + `creator` varchar(64) NULL DEFAULT '', + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater` varchar(64) NULL DEFAULT '', + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` bit(1) NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT = '退款订å•'; + +CREATE TABLE IF NOT EXISTS `pay_notify_task` ( + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, + `app_id` bigint(20) NOT NULL, + `type` tinyint(4) NOT NULL, + `data_id` bigint(20) NOT NULL, + `merchant_order_id` varchar(64) NOT NULL, + `status` tinyint(4) NOT NULL, + `next_notify_time` datetime(0) NULL DEFAULT NULL, + `last_execute_time` datetime(0) NULL DEFAULT NULL, + `notify_times` int NOT NULL, + `max_notify_times` int NOT NULL, + `notify_url` varchar(1024) NOT NULL, + `creator` varchar(64) NULL DEFAULT '', + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater` varchar(64) NULL DEFAULT '', + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` bit(1) NOT NULL DEFAULT FALSE, + `tenant_id` bigint(20) NOT NULL DEFAULT 0, + PRIMARY KEY ("id") +) COMMENT = '支付通知任务'; + +CREATE TABLE IF NOT EXISTS `pay_notify_log` ( + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, + `task_id` bigint(20) NOT NULL, + `notify_times` int NOT NULL, + `response` varchar(1024) NOT NULL, + `status` tinyint(4) NOT NULL, + `creator` varchar(64) NULL DEFAULT '', + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater` varchar(64) NULL DEFAULT '', + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` bit(1) NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT = '支付通知日志'; diff --git a/tashow-module/tashow-module-system/src/main/resources/application-local.yaml b/tashow-module/tashow-module-system/src/main/resources/application-local.yaml index 03bada2..04c956f 100644 --- a/tashow-module/tashow-module-system/src/main/resources/application-local.yaml +++ b/tashow-module/tashow-module-system/src/main/resources/application-local.yaml @@ -5,11 +5,11 @@ spring: username: nacos # Nacos è´¦å· password: nacos # Nacos å¯†ç  discovery: # ã€é…置中心】é…置项 - namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + namespace: liwq # 命å空间。这里使用 dev å¼€å‘环境 group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # æœåŠ¡å®žä¾‹çš„ç‰ˆæœ¬å·ï¼Œå¯ç”¨äºŽç°åº¦å‘布 config: # ã€æ³¨å†Œä¸­å¿ƒã€‘é…置项 - namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + namespace: liwq # 命å空间。这里使用 dev å¼€å‘环境 group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP diff --git a/tashow-module/tashow-module-trade/Dockerfile b/tashow-module/tashow-module-trade/Dockerfile new file mode 100644 index 0000000..b6aef79 --- /dev/null +++ b/tashow-module/tashow-module-trade/Dockerfile @@ -0,0 +1,19 @@ +## AdoptOpenJDK åœæ­¢å‘布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,æä¾›æ›´å¥½çš„稳定性 +## æ„Ÿè°¢å¤æ—¦æ ¸åšå£«çš„建议ï¼ç°å­å“¥ï¼Œç‰›çš®ï¼ +FROM eclipse-temurin:21-jre + +## 创建目录,并使用它作为工作目录 +RUN mkdir -p /yudao-module-trade-biz +WORKDIR /yudao-module-trade-biz +## å°†åŽç«¯é¡¹ç›®çš„ Jar 文件,å¤åˆ¶åˆ°é•œåƒä¸­ +COPY ./target/yudao-module-trade-biz.jar app.jar + +## 设置 TZ 时区 +## 设置 JAVA_OPTS 环境å˜é‡ï¼Œå¯é€šè¿‡ docker run -e "JAVA_OPTS=" 进行覆盖 +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m" + +## 暴露åŽç«¯é¡¹ç›®çš„ 48080 ç«¯å£ +EXPOSE 48102 + +## å¯åЍåŽç«¯é¡¹ç›® +CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar diff --git a/tashow-module/tashow-module-trade/pom.xml b/tashow-module/tashow-module-trade/pom.xml new file mode 100644 index 0000000..4a27f22 --- /dev/null +++ b/tashow-module/tashow-module-trade/pom.xml @@ -0,0 +1,100 @@ + + + + com.tashow.cloud + tashow-module + ${revision} + + 4.0.0 + tashow-module-trade + jar + + ${project.artifactId} + + trade 模å—,主è¦å®žçŽ°äº¤æ˜“ç›¸å…³åŠŸèƒ½ + 例如:订å•ã€é€€æ¬¾ã€è´­ç‰©è½¦ç­‰åŠŸèƒ½ã€‚ + + + + + + com.tashow.cloud + tashow-framework-env + + + + + com.tashow.cloud + tashow-trade-api + + + + + com.tashow.cloud + tashow-framework-web + + + + com.tashow.cloud + tashow-framework-security + + + + + com.tashow.cloud + tashow-data-mybatis + + + + com.tashow.cloud + tashow-data-redis + + + + + com.tashow.cloud + tashow-framework-rpc + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.tashow.cloud + tashow-data-excel + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + + diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/TradeServerApplication.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/TradeServerApplication.java new file mode 100644 index 0000000..82289a9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/TradeServerApplication.java @@ -0,0 +1,30 @@ +package com.tashow.cloud.trade; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 项目的å¯åŠ¨ç±» + * + * 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + * 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + * 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + * + * @author èŠ‹é“æºç  + */ +@SpringBootApplication +public class TradeServerApplication { + + public static void main(String[] args) { + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + + SpringApplication.run(TradeServerApplication.class, args); + + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + // 如果你碰到å¯åŠ¨çš„é—®é¢˜ï¼Œè¯·è®¤çœŸé˜…è¯» https://cloud.iocoder.cn/quick-start/ 文章 + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/api/order/TradeOrderApiImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/api/order/TradeOrderApiImpl.java new file mode 100644 index 0000000..7ac927e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/api/order/TradeOrderApiImpl.java @@ -0,0 +1,47 @@ +package com.tashow.cloud.trade.api.order; + +import com.tashow.cloud.common.pojo.CommonResult; +import com.tashow.cloud.trade.convert.order.TradeOrderConvert; +import com.tashow.cloud.trade.service.order.TradeOrderQueryService; +import com.tashow.cloud.trade.service.order.TradeOrderUpdateService; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collection; +import java.util.List; + +import static com.tashow.cloud.common.pojo.CommonResult.success; + + +/** + * è®¢å• API 接å£å®žçŽ°ç±» + * + * @author HUIHUI + */ +@RestController // æä¾› RESTful API 接å£ï¼Œç»™ Feign 调用 +@Validated +public class TradeOrderApiImpl implements TradeOrderApi { + + @Resource + private TradeOrderUpdateService tradeOrderUpdateService; + @Resource + private TradeOrderQueryService tradeOrderQueryService; + + @Override + public CommonResult> getOrderList(Collection ids) { + return success(TradeOrderConvert.INSTANCE.convertList04(tradeOrderQueryService.getOrderList(ids))); + } + + @Override + public CommonResult getOrder(Long id) { + return success(TradeOrderConvert.INSTANCE.convert(tradeOrderQueryService.getOrder(id))); + } + + @Override + public CommonResult cancelPaidOrder(Long userId, Long orderId, Integer cancelType) { + tradeOrderUpdateService.cancelPaidOrder(userId, orderId, cancelType); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/api/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/api/package-info.java new file mode 100644 index 0000000..88efea9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/api/package-info.java @@ -0,0 +1 @@ +package com.tashow.cloud.trade.api; \ No newline at end of file diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/AfterSaleController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/AfterSaleController.java new file mode 100644 index 0000000..5d0fcd6 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/AfterSaleController.java @@ -0,0 +1,145 @@ +package com.tashow.cloud.trade.controller.admin.aftersale; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; +import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.*; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.*; +import com.tashow.cloud.trade.convert.aftersale.AfterSaleConvert; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleDO; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleLogDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.tashow.cloud.trade.service.aftersale.AfterSaleLogService; +import com.tashow.cloud.trade.service.aftersale.AfterSaleService; +import com.tashow.cloud.trade.service.order.TradeOrderQueryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.validation.Valid; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管ç†åŽå° - å”®åŽè®¢å•") +@RestController +@RequestMapping("/trade/after-sale") +@Validated +@Slf4j +public class AfterSaleController { + + @Resource + private AfterSaleService afterSaleService; + @Resource + private TradeOrderQueryService tradeOrderQueryService; + @Resource + private AfterSaleLogService afterSaleLogService; + @Resource + private MemberUserApi memberUserApi; + + @GetMapping("/page") + @Operation(summary = "获得售åŽè®¢å•分页") + @PreAuthorize("@ss.hasPermission('trade:after-sale:query')") + public CommonResult> getAfterSalePage(@Valid AfterSalePageReqVO pageVO) { + // æŸ¥è¯¢å”®åŽ + PageResult pageResult = afterSaleService.getAfterSalePage(pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 查询会员 + Map memberUsers = memberUserApi.getUserMap( + convertSet(pageResult.getList(), AfterSaleDO::getUserId)); + return success(AfterSaleConvert.INSTANCE.convertPage(pageResult, memberUsers)); + } + + @GetMapping("/get-detail") + @Operation(summary = "获得售åŽè®¢å•详情") + @Parameter(name = "id", description = "å”®åŽç¼–å·", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('trade:after-sale:query')") + public CommonResult getOrderDetail(@RequestParam("id") Long id) { + // æŸ¥è¯¢è®¢å• + AfterSaleDO afterSale = afterSaleService.getAfterSale(id); + if (afterSale == null) { + return success(null); + } + + // æŸ¥è¯¢è®¢å• + TradeOrderDO order = tradeOrderQueryService.getOrder(afterSale.getOrderId()); + // 查询订å•项 + TradeOrderItemDO orderItem = tradeOrderQueryService.getOrderItem(afterSale.getOrderItemId()); + // æ‹¼æŽ¥æ•°æ® + MemberUserRespDTO user = memberUserApi.getUser(afterSale.getUserId()).getCheckedData(); + List logs = afterSaleLogService.getAfterSaleLogList(afterSale.getId()); + return success(AfterSaleConvert.INSTANCE.convert(afterSale, order, orderItem, user, logs)); + } + + @PutMapping("/agree") + @Operation(summary = "åŒæ„å”®åŽ") + @Parameter(name = "id", description = "å”®åŽç¼–å·", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('trade:after-sale:agree')") + public CommonResult agreeAfterSale(@RequestParam("id") Long id) { + afterSaleService.agreeAfterSale(getLoginUserId(), id); + return success(true); + } + + @PutMapping("/disagree") + @Operation(summary = "æ‹’ç»å”®åŽ") + @PreAuthorize("@ss.hasPermission('trade:after-sale:disagree')") + public CommonResult disagreeAfterSale(@RequestBody AfterSaleDisagreeReqVO confirmReqVO) { + afterSaleService.disagreeAfterSale(getLoginUserId(), confirmReqVO); + return success(true); + } + + @PutMapping("/receive") + @Operation(summary = "确认收货") + @Parameter(name = "id", description = "å”®åŽç¼–å·", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('trade:after-sale:receive')") + public CommonResult receiveAfterSale(@RequestParam("id") Long id) { + afterSaleService.receiveAfterSale(getLoginUserId(), id); + return success(true); + } + + @PutMapping("/refuse") + @Operation(summary = "æ‹’ç»æ”¶è´§") + @Parameter(name = "id", description = "å”®åŽç¼–å·", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('trade:after-sale:receive')") + public CommonResult refuseAfterSale(AfterSaleRefuseReqVO refuseReqVO) { + afterSaleService.refuseAfterSale(getLoginUserId(), refuseReqVO); + return success(true); + } + + @PutMapping("/refund") + @Operation(summary = "确认退款") + @Parameter(name = "id", description = "å”®åŽç¼–å·", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('trade:after-sale:refund')") + public CommonResult refundAfterSale(@RequestParam("id") Long id) { + afterSaleService.refundAfterSale(getLoginUserId(), getClientIP(), id); + return success(true); + } + + @PostMapping("/update-refunded") + @Operation(summary = "æ›´æ–°å”®åŽè®¢å•为已退款") // ç”± pay-module 支付æœåŠ¡ï¼Œè¿›è¡Œå›žè°ƒï¼Œå¯è§ PayNotifyJob + @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 + public CommonResult updateAfterRefund(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { + // ç›®å‰ä¸šåŠ¡é€»è¾‘ï¼Œä¸éœ€è¦åšä»»ä½•事情 + // å½“ç„¶ï¼Œé€€æ¬¾ä¼šæœ‰å°æ¦‚率会失败的情况,å¯ä»¥ç›‘控失败状æ€ï¼Œè¿›è¡Œå‘Šè­¦ + log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/TradeAfterSaleController.http b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/TradeAfterSaleController.http new file mode 100644 index 0000000..f342e94 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/TradeAfterSaleController.http @@ -0,0 +1,33 @@ +### 获得交易售åŽåˆ†é¡µ => æˆåŠŸ +GET {{baseUrl}}/trade/after-sale/page?pageNo=1&pageSize=10 +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +### åŒæ„å”®åŽ => æˆåŠŸ +PUT {{baseUrl}}/trade/after-sale/agree?id=7 +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} +Content-Type: application/json + +### æ‹’ç»å”®åŽ => æˆåŠŸ +PUT {{baseUrl}}/trade/after-sale/disagree +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} +Content-Type: application/json + +{ + "id": 6, + "auditReason": "阿巴巴" +} + +### 确认退款 => æˆåŠŸ +PUT {{baseUrl}}/trade/after-sale/refund?id=6 +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} +Content-Type: application/json + +### 确认收货 => æˆåŠŸ +PUT {{baseUrl}}/trade/after-sale/receive?id=7 +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} +Content-Type: application/json diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleBaseVO.java new file mode 100644 index 0000000..9e5a37e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleBaseVO.java @@ -0,0 +1,119 @@ +package com.tashow.cloud.trade.controller.admin.aftersale.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +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 AfterSaleBaseVO { + + @Schema(description = "å”®åŽæµæ°´å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "202211190847450020500077") + @NotNull(message = "å”®åŽæµæ°´å·ä¸èƒ½ä¸ºç©º") + private String no; + + @Schema(description = "å”®åŽçжæ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "å”®åŽçжæ€ä¸èƒ½ä¸ºç©º") + private Integer status; + + @Schema(description = "å”®åŽç±»åž‹", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + @NotNull(message = "å”®åŽç±»åž‹ä¸èƒ½ä¸ºç©º") + private Integer type; + + @Schema(description = "å”®åŽæ–¹å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "å”®åŽæ–¹å¼ä¸èƒ½ä¸ºç©º") + private Integer way; + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "30337") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + + @Schema(description = "申请原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "ä¸å–œæ¬¢") + @NotNull(message = "申请原因ä¸èƒ½ä¸ºç©º") + private String applyReason; + + @Schema(description = "补充æè¿°", example = "你说的对") + private String applyDescription; + + @Schema(description = "补充凭è¯å›¾ç‰‡", example = "https://www.iocoder.cn/1.png") + private List applyPicUrls; + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "18078") + @NotNull(message = "订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long orderId; + + @Schema(description = "è®¢å•æµæ°´å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2022111917190001") + @NotNull(message = "è®¢å•æµæ°´å·ä¸èƒ½ä¸ºç©º") + private String orderNo; + + @Schema(description = "订å•项编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "572") + @NotNull(message = "订å•项编å·ä¸èƒ½ä¸ºç©º") + private Long orderItemId; + + @Schema(description = "å•†å“ SPU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2888") + @NotNull(message = "å•†å“ SPU ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long spuId; + + @Schema(description = "å•†å“ SPU åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + @NotNull(message = "å•†å“ SPU åç§°ä¸èƒ½ä¸ºç©º") + private String spuName; + + @Schema(description = "å•†å“ SKU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "15657") + @NotNull(message = "å•†å“ SKU ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long skuId; + + @Schema(description = "商å“图片", example = "https://www.iocoder.cn/2.png") + private String picUrl; + + @Schema(description = "è´­ä¹°æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "20012") + @NotNull(message = "è´­ä¹°æ•°é‡ä¸èƒ½ä¸ºç©º") + private Integer count; + + @Schema(description = "审批时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime auditTime; + + @Schema(description = "审批人", example = "30835") + private Long auditUserId; + + @Schema(description = "审批备注", example = "ä¸é¦™") + private String auditReason; + + @Schema(description = "退款金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "18077") + @NotNull(message = "退款金é¢ï¼Œå•ä½ï¼šåˆ†ä¸èƒ½ä¸ºç©º") + private Integer refundPrice; + + @Schema(description = "支付退款编å·", example = "10271") + private Long payRefundId; + + @Schema(description = "退款时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime refundTime; + + @Schema(description = "退货物æµå…¬å¸ç¼–å·", example = "10") + private Long logisticsId; + + @Schema(description = "退货物æµå•å·", example = "610003952009") + private String logisticsNo; + + @Schema(description = "退货时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime deliveryTime; + + @Schema(description = "æ”¶è´§æ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime receiveTime; + + @Schema(description = "收货备注", example = "ä¸å–œæ¬¢") + private String receiveReason; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleDetailRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleDetailRespVO.java new file mode 100644 index 0000000..b7d37c6 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleDetailRespVO.java @@ -0,0 +1,52 @@ +package com.tashow.cloud.trade.controller.admin.aftersale.vo; + +import com.tashow.cloud.trade.controller.admin.aftersale.vo.log.AfterSaleLogRespVO; +import com.tashow.cloud.trade.controller.admin.base.member.user.MemberUserRespVO; +import com.tashow.cloud.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderBaseVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderItemBaseVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管ç†åŽå° - å”®åŽè®¢å•的详情 Response VO") +@Data +public class AfterSaleDetailRespVO extends AfterSaleBaseVO { + + @Schema(description = "å”®åŽç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + + + /** + * 订å•åŸºæœ¬ä¿¡æ¯ + */ + private TradeOrderBaseVO order; + /** + * 订å•项列表 + */ + private OrderItem orderItem; + + /** + * ç”¨æˆ·ä¿¡æ¯ + */ + private MemberUserRespVO user; + + /** + * å”®åŽæ—¥å¿— + */ + private List logs; + + @Schema(description = "管ç†åŽå° - 交易订å•的详情的订å•项目") + @Data + public static class OrderItem extends TradeOrderItemBaseVO { + + /** + * 属性数组 + */ + private List properties; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleDisagreeReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleDisagreeReqVO.java new file mode 100644 index 0000000..f68b5f3 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleDisagreeReqVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.trade.controller.admin.aftersale.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - äº¤æ˜“å”®åŽæ‹’ç» Request VO") +@Data +public class AfterSaleDisagreeReqVO { + + @Schema(description = "å”®åŽç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "å”®åŽç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "审批备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + @NotEmpty(message = "审批备注ä¸èƒ½ä¸ºç©º") + private String auditReason; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSalePageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSalePageReqVO.java new file mode 100644 index 0000000..d20058f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSalePageReqVO.java @@ -0,0 +1,52 @@ +package com.tashow.cloud.trade.controller.admin.aftersale.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleTypeEnum; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 AfterSalePageReqVO extends PageParam { + + @Schema(description = "用户编å·", example = "1024") + private Long userId; + + @Schema(description = "å”®åŽæµæ°´å·", example = "202211190847450020500077") + private String no; + + @Schema(description = "å”®åŽçжæ€", example = "10") + @InEnum(value = AfterSaleStatusEnum.class, message = "å”®åŽçжæ€å¿…须是 {value}") + private Integer status; + + @Schema(description = "å”®åŽç±»åž‹", example = "20") + @InEnum(value = AfterSaleTypeEnum.class, message = "å”®åŽç±»åž‹å¿…须是 {value}") + private Integer type; + + @Schema(description = "å”®åŽæ–¹å¼", example = "10") + @InEnum(value = AfterSaleWayEnum.class, message = "å”®åŽæ–¹å¼å¿…须是 {value}") + private Integer way; + + @Schema(description = "订å•ç¼–å·", example = "18078") + private String orderNo; + + @Schema(description = "å•†å“ SPU åç§°", example = "æŽå››") + private String spuName; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleRefuseReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleRefuseReqVO.java new file mode 100644 index 0000000..1790b73 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleRefuseReqVO.java @@ -0,0 +1,20 @@ +package com.tashow.cloud.trade.controller.admin.aftersale.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - äº¤æ˜“å”®åŽæ‹’ç»æ”¶è´§ Request VO") +@Data +public class AfterSaleRefuseReqVO { + + @Schema(description = "å”®åŽç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "å”®åŽç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "收货备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + @NotNull(message = "收货备注ä¸èƒ½ä¸ºç©º") + private String refuseMemo; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleRespPageItemVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleRespPageItemVO.java new file mode 100644 index 0000000..93b6c94 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/AfterSaleRespPageItemVO.java @@ -0,0 +1,35 @@ +package com.tashow.cloud.trade.controller.admin.aftersale.vo; + +import com.tashow.cloud.trade.controller.admin.base.member.user.MemberUserRespVO; +import com.tashow.cloud.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 交易售åŽåˆ†é¡µçš„æ¯ä¸€æ¡è®°å½• Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AfterSaleRespPageItemVO extends AfterSaleBaseVO { + + @Schema(description = "å”®åŽç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "27630") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + /** + * 商å“属性数组 + */ + private List properties; + + /** + * ç”¨æˆ·ä¿¡æ¯ + */ + private MemberUserRespVO user; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/log/AfterSaleLogRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/log/AfterSaleLogRespVO.java new file mode 100644 index 0000000..a537f4b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/aftersale/vo/log/AfterSaleLogRespVO.java @@ -0,0 +1,37 @@ +package com.tashow.cloud.trade.controller.admin.aftersale.vo.log; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - äº¤æ˜“å”®åŽæ—¥å¿— Response VO") +@Data +public class AfterSaleLogRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20669") + private Long id; + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "22634") + private Long userId; + + @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer userType; + + @Schema(description = "å”®åŽç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "3023") + private Long afterSaleId; + + @Schema(description = "å”®åŽçжæ€ï¼ˆä¹‹å‰ï¼‰", example = "2") + private Integer beforeStatus; + + @Schema(description = "å”®åŽçжæ€ï¼ˆä¹‹åŽï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer afterStatus; + + @Schema(description = "æ“作明细", requiredMode = Schema.RequiredMode.REQUIRED, example = "ç»´æƒå®Œæˆï¼Œé€€æ¬¾é‡‘é¢ï¼šÂ¥37776.00") + private String content; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/member/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/member/package-info.java new file mode 100644 index 0000000..bd77464 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/member/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ç¬¦ï¼Œå¯å¿½ç•¥ + */ +package com.tashow.cloud.trade.controller.admin.base.member; diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/member/user/MemberUserRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/member/user/MemberUserRespVO.java new file mode 100644 index 0000000..445c67c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/member/user/MemberUserRespVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.trade.controller.admin.base.member.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - 会员用户 Response VO") +@Data +public class MemberUserRespVO { + + @Schema(description = "用户 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "èŠ‹é“æºç ") + private String nickname; + + @Schema(description = "用户头åƒ", example = "https://www.iocoder.cn/xxx.png") + private String avatar; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/package-info.java new file mode 100644 index 0000000..9f93ed2 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/package-info.java @@ -0,0 +1,4 @@ +/** + * 放置该模å—通用的 VO ç±» + */ +package com.tashow.cloud.trade.controller.admin.base; diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/product/property/ProductPropertyValueDetailRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/product/property/ProductPropertyValueDetailRespVO.java new file mode 100644 index 0000000..ba4e20c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/product/property/ProductPropertyValueDetailRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.admin.base.product.property; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - 商å“属性值的明细 Response VO") +@Data +public class ProductPropertyValueDetailRespVO { + + @Schema(description = "属性的编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long propertyId; + + @Schema(description = "属性的åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "颜色") + private String propertyName; + + @Schema(description = "属性值的编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long valueId; + + @Schema(description = "属性值的åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "红色") + private String valueName; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/system/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/system/package-info.java new file mode 100644 index 0000000..694594d --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/system/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ç¬¦ï¼Œå¯å¿½ç•¥ + */ +package com.tashow.cloud.trade.controller.admin.base.system; diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/system/user/UserSimpleBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/system/user/UserSimpleBaseVO.java new file mode 100644 index 0000000..758b358 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/base/system/user/UserSimpleBaseVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.trade.controller.admin.base.system.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "ç”¨æˆ·ç²¾ç®€ä¿¡æ¯ VO") +@Data +public class UserSimpleBaseVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String nickname; + + @Schema(description = "用户头åƒ", example = "https://www.iocoder.cn/1.png") + private String avatar; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageRecordController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageRecordController.java new file mode 100644 index 0000000..c3383f1 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageRecordController.java @@ -0,0 +1,66 @@ +package com.tashow.cloud.trade.controller.admin.brokerage; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.record.BrokerageRecordRespVO; +import com.tashow.cloud.trade.convert.brokerage.BrokerageRecordConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageRecordDO; +import com.tashow.cloud.trade.service.brokerage.BrokerageRecordService; +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.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 jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.Map; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +@Tag(name = "管ç†åŽå° - 佣金记录") +@RestController +@RequestMapping("/trade/brokerage-record") +@Validated +public class BrokerageRecordController { + + @Resource + private BrokerageRecordService brokerageRecordService; + + @Resource + private MemberUserApi memberUserApi; + + @GetMapping("/get") + @Operation(summary = "获得佣金记录") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')") + public CommonResult getBrokerageRecord(@RequestParam("id") Long id) { + BrokerageRecordDO brokerageRecord = brokerageRecordService.getBrokerageRecord(id); + return success(BrokerageRecordConvert.INSTANCE.convert(brokerageRecord)); + } + + @GetMapping("/page") + @Operation(summary = "获得佣金记录分页") + @PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')") + public CommonResult> getBrokerageRecordPage(@Valid BrokerageRecordPageReqVO pageVO) { + PageResult pageResult = brokerageRecordService.getBrokerageRecordPage(pageVO); + + // æŸ¥è¯¢ç”¨æˆ·ä¿¡æ¯ + Set userIds = convertSet(pageResult.getList(), BrokerageRecordDO::getUserId); + userIds.addAll(convertList(pageResult.getList(), BrokerageRecordDO::getSourceUserId)); + Map userMap = memberUserApi.getUserMap(userIds); + // æ‹¼æŽ¥æ•°æ® + return success(BrokerageRecordConvert.INSTANCE.convertPage(pageResult, userMap)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageUserController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageUserController.java new file mode 100644 index 0000000..f5e7118 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageUserController.java @@ -0,0 +1,121 @@ +package com.tashow.cloud.trade.controller.admin.brokerage; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.user.*; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.user.*; +import com.tashow.cloud.trade.convert.brokerage.BrokerageUserConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; +import com.tashow.cloud.trade.service.brokerage.BrokerageRecordService; +import com.tashow.cloud.trade.service.brokerage.BrokerageUserService; +import com.tashow.cloud.trade.service.brokerage.BrokerageWithdrawService; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; +import com.tashow.cloud.trade.service.brokerage.bo.UserBrokerageSummaryRespBO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static java.util.Arrays.asList; + +@Tag(name = "管ç†åŽå° - 分销用户") +@RestController +@RequestMapping("/trade/brokerage-user") +@Validated +public class BrokerageUserController { + + @Resource + private BrokerageUserService brokerageUserService; + @Resource + private BrokerageRecordService brokerageRecordService; + @Resource + private BrokerageWithdrawService brokerageWithdrawService; + + @Resource + private MemberUserApi memberUserApi; + + @PostMapping("/create") + @Operation(summary = "创建分销用户") + @PreAuthorize("@ss.hasPermission('trade:brokerage-user:create')") + public CommonResult createBrokerageUser(@Valid @RequestBody BrokerageUserCreateReqVO createReqVO) { + return success(brokerageUserService.createBrokerageUser(createReqVO)); + } + + @PutMapping("/update-bind-user") + @Operation(summary = "修改推广员") + @PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-bind-user')") + public CommonResult updateBindUser(@Valid @RequestBody BrokerageUserUpdateBrokerageUserReqVO updateReqVO) { + brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), updateReqVO.getBindUserId()); + return success(true); + } + + @PutMapping("/clear-bind-user") + @Operation(summary = "清除推广员") + @PreAuthorize("@ss.hasPermission('trade:brokerage-user:clear-bind-user')") + public CommonResult clearBindUser(@Valid @RequestBody BrokerageUserClearBrokerageUserReqVO updateReqVO) { + brokerageUserService.updateBrokerageUserId(updateReqVO.getId(), null); + return success(true); + } + + @PutMapping("/update-brokerage-enable") + @Operation(summary = "修改推广资格") + @PreAuthorize("@ss.hasPermission('trade:brokerage-user:update-brokerage-enable')") + public CommonResult updateBrokerageEnabled(@Valid @RequestBody BrokerageUserUpdateBrokerageEnabledReqVO updateReqVO) { + brokerageUserService.updateBrokerageUserEnabled(updateReqVO.getId(), updateReqVO.getEnabled()); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得分销用户") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('trade:brokerage-user:query')") + public CommonResult getBrokerageUser(@RequestParam("id") Long id) { + BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(id); + // TODO @ç–¯ç‹‚ï¼šæ˜¯ä¸æ˜¯æžæˆä¸€ä¸ªç»Ÿä¸€çš„ convert? + BrokerageUserRespVO respVO = BrokerageUserConvert.INSTANCE.convert(brokerageUser); + return success(BrokerageUserConvert.INSTANCE.copyTo(memberUserApi.getUser(id).getCheckedData(), respVO)); + } + + @GetMapping("/page") + @Operation(summary = "获得分销用户分页") + @PreAuthorize("@ss.hasPermission('trade:brokerage-user:query')") + public CommonResult> getBrokerageUserPage(@Valid BrokerageUserPageReqVO pageVO) { + // 分页查询 + PageResult pageResult = brokerageUserService.getBrokerageUserPage(pageVO); + + // æŸ¥è¯¢ç”¨æˆ·ä¿¡æ¯ + Set userIds = convertSet(pageResult.getList(), BrokerageUserDO::getId); + Map userMap = memberUserApi.getUserMap(userIds); + // åˆè®¡åˆ†ä½£çš„æŽ¨å¹¿è®¢å• + Map brokerageOrderSummaryMap = brokerageRecordService.getUserBrokerageSummaryMapByUserId( + userIds, BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus()); + // åˆè®¡åˆ†ä½£çš„æŽ¨å¹¿ç”¨æˆ· + // TODO @ç–¯ç‹‚ï¼šè½¬æˆ map 批é‡è¯»å– + Map brokerageUserCountMap = convertMap(userIds, + userId -> userId, + userId -> brokerageUserService.getBrokerageUserCountByBindUserId(userId, null)); + // åˆè®¡åˆ†ä½£çš„æçް + // TODO @ç–¯ç‹‚ï¼šå¦‚æžœæœªæ¥æ”¯æŒäº†æ‰“款这个动作,å¯èƒ½ status 会ä¸å¯¹ï¼› + Map withdrawMap = brokerageWithdrawService.getWithdrawSummaryMapByUserId( + userIds, asList(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS)); + // 拼接返回 + return success(BrokerageUserConvert.INSTANCE.convertPage(pageResult, userMap, brokerageUserCountMap, + brokerageOrderSummaryMap, withdrawMap)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageWithdrawController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageWithdrawController.java new file mode 100644 index 0000000..b70ada5 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/BrokerageWithdrawController.java @@ -0,0 +1,96 @@ +package com.tashow.cloud.trade.controller.admin.brokerage; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayTransferNotifyReqDTO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRejectReqVO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRespVO; +import com.tashow.cloud.trade.convert.brokerage.BrokerageWithdrawConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; +import com.tashow.cloud.trade.service.brokerage.BrokerageWithdrawService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; + +@Tag(name = "管ç†åŽå° - 佣金æçް") +@RestController +@RequestMapping("/trade/brokerage-withdraw") +@Validated +@Slf4j +public class BrokerageWithdrawController { + + @Resource + private BrokerageWithdrawService brokerageWithdrawService; + + @Resource + private MemberUserApi memberUserApi; + + @PutMapping("/approve") + @Operation(summary = "通过申请") + @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") + public CommonResult approveBrokerageWithdraw(@RequestParam("id") Long id) { + brokerageWithdrawService.auditBrokerageWithdraw(id, + BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, "", getClientIP()); + return success(true); + } + + @PutMapping("/reject") + @Operation(summary = "驳回申请") + @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") + public CommonResult rejectBrokerageWithdraw(@Valid @RequestBody BrokerageWithdrawRejectReqVO reqVO) { + brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(), + BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason(), getClientIP()); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得佣金æçް") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:query')") + public CommonResult getBrokerageWithdraw(@RequestParam("id") Long id) { + BrokerageWithdrawDO brokerageWithdraw = brokerageWithdrawService.getBrokerageWithdraw(id); + return success(BrokerageWithdrawConvert.INSTANCE.convert(brokerageWithdraw)); + } + + @GetMapping("/page") + @Operation(summary = "获得佣金æçŽ°åˆ†é¡µ") + @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:query')") + public CommonResult> getBrokerageWithdrawPage(@Valid BrokerageWithdrawPageReqVO pageVO) { + // 分页查询 + PageResult pageResult = brokerageWithdrawService.getBrokerageWithdrawPage(pageVO); + + // æ‹¼æŽ¥ä¿¡æ¯ + Map userMap = memberUserApi.getUserMap( + convertSet(pageResult.getList(), BrokerageWithdrawDO::getUserId)); + return success(BrokerageWithdrawConvert.INSTANCE.convertPage(pageResult, userMap)); + } + + // TODO @luchi:update-transferred,url 改æˆè¿™ä¸ªã€‚å’Œ update-paid ã€update-refunded ä¿æŒä¸€è‡´ + @PostMapping("/update-transfer") + @Operation(summary = "更新转账订å•为转账æˆåŠŸ") // ç”± pay-module 支付æœåŠ¡ï¼Œè¿›è¡Œå›žè°ƒï¼Œå¯è§ PayNotifyJob + @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 + public CommonResult updateBrokerageWithdrawTransferred(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) { + log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO); + brokerageWithdrawService.updateBrokerageWithdrawTransferred( + Long.parseLong(notifyReqDTO.getMerchantTransferId()), notifyReqDTO.getPayTransferId()); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordBaseVO.java new file mode 100644 index 0000000..5f8eed3 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordBaseVO.java @@ -0,0 +1,65 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.record; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +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; + +/** + * 佣金记录 Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class BrokerageRecordBaseVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "25973") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + + @Schema(description = "业务编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "23353") + @NotEmpty(message = "业务编å·ä¸èƒ½ä¸ºç©º") + private String bizId; + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "业务类型ä¸èƒ½ä¸ºç©º") + private Integer bizType; + + @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "标题ä¸èƒ½ä¸ºç©º") + private String title; + + @Schema(description = "金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "28731") + @NotNull(message = "金é¢ä¸èƒ½ä¸ºç©º") + private Integer price; + + @Schema(description = "当剿€»ä½£é‡‘", requiredMode = Schema.RequiredMode.REQUIRED, example = "13226") + @NotNull(message = "当剿€»ä½£é‡‘ä¸èƒ½ä¸ºç©º") + private Integer totalPrice; + + @Schema(description = "说明", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对") + @NotNull(message = "说明ä¸èƒ½ä¸ºç©º") + private String description; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状æ€ä¸èƒ½ä¸ºç©º") + private Integer status; + + @Schema(description = "冻结时间(天)", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "冻结时间(天)ä¸èƒ½ä¸ºç©º") + private Integer frozenDays; + + @Schema(description = "解冻时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime unfreezeTime; + + @Schema(description = "æ¥æºç”¨æˆ·ç­‰çº§") + private Integer sourceUserLevel; + + @Schema(description = "æ¥æºç”¨æˆ·ç¼–å·") + private Long sourceUserId; +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordPageReqVO.java new file mode 100644 index 0000000..c525a2f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordPageReqVO.java @@ -0,0 +1,36 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.record; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 BrokerageRecordPageReqVO extends PageParam { + + @Schema(description = "用户编å·", example = "25973") + private Long userId; + + @Schema(description = "业务类型", example = "1") + private Integer bizType; + + @Schema(description = "状æ€", example = "1") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "用户类型", example = "1") + private Integer sourceUserLevel; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordRespVO.java new file mode 100644 index 0000000..cd8b024 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/record/BrokerageRecordRespVO.java @@ -0,0 +1,37 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.record; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 佣金记录 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BrokerageRecordRespVO extends BrokerageRecordBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "28896") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + + // ========== ç”¨æˆ·ä¿¡æ¯ ========== + + @Schema(description = "用户头åƒ", example = "https://www.iocoder.cn/xxx.png") + private String userAvatar; + @Schema(description = "用户昵称", example = "æŽå››") + private String userNickname; + + + // ========== æ¥æºç”¨æˆ·ä¿¡æ¯ ========== + + @Schema(description = "æ¥æºç”¨æˆ·å¤´åƒ", example = "https://www.iocoder.cn/xxx.png") + private String sourceUserAvatar; + @Schema(description = "æ¥æºç”¨æˆ·æ˜µç§°", example = "æŽå››") + private String sourceUserNickname; +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserBaseVO.java new file mode 100644 index 0000000..b1c93a9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserBaseVO.java @@ -0,0 +1,43 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +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; + +/** + * 分销用户 Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class BrokerageUserBaseVO { + + @Schema(description = "推广员编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "4587") + @NotNull(message = "推广员编å·ä¸èƒ½ä¸ºç©º") + private Long bindUserId; + + @Schema(description = "推广员绑定时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime bindUserTime; + + @Schema(description = "推广资格", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "推广资格ä¸èƒ½ä¸ºç©º") + private Boolean brokerageEnabled; + + @Schema(description = "æˆä¸ºåˆ†é”€å‘˜æ—¶é—´") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime brokerageTime; + + @Schema(description = "å¯ç”¨ä½£é‡‘", requiredMode = Schema.RequiredMode.REQUIRED, example = "11089") + @NotNull(message = "å¯ç”¨ä½£é‡‘ä¸èƒ½ä¸ºç©º") + private Integer price; + + @Schema(description = "冻结佣金", requiredMode = Schema.RequiredMode.REQUIRED, example = "30916") + @NotNull(message = "冻结佣金ä¸èƒ½ä¸ºç©º") + private Integer frozenPrice; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserClearBrokerageUserReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserClearBrokerageUserReqVO.java new file mode 100644 index 0000000..1f8a24c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserClearBrokerageUserReqVO.java @@ -0,0 +1,17 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - 分销用户 - 清除推广员 Request VO") +@Data +@ToString(callSuper = true) +public class BrokerageUserClearBrokerageUserReqVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long id; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserCreateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserCreateReqVO.java new file mode 100644 index 0000000..48ca055 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserCreateReqVO.java @@ -0,0 +1,18 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - 分销用户创建 Request VO") +@Data +public class BrokerageUserCreateReqVO { + + @Schema(description = "分销用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "分销用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + + @Schema(description = "推广员编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "4587") + private Long bindUserId; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserPageReqVO.java new file mode 100644 index 0000000..5e4a5a2 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserPageReqVO.java @@ -0,0 +1,37 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.user; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 BrokerageUserPageReqVO extends PageParam { + + @Schema(description = "推广员编å·", example = "4587") + private Long bindUserId; + + @Schema(description = "推广资格", example = "true") + private Boolean brokerageEnabled; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "用户等级", example = "1") // 注æ„ï¼Œè¿™äº†ä¸æ˜¯ç”¨æˆ·çš„会员等级,而是过滤推广的层级 + private Integer level; + + @Schema(description = "绑定时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] bindUserTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserRespVO.java new file mode 100644 index 0000000..07fd62a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserRespVO.java @@ -0,0 +1,45 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 分销用户 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BrokerageUserRespVO extends BrokerageUserBaseVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + // ========== ç”¨æˆ·ä¿¡æ¯ ========== + + @Schema(description = "用户头åƒ", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.png") + private String avatar; + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + private String nickname; + + // ========== æŽ¨å¹¿ä¿¡æ¯ ========== 注æ„:是包括 1 + 2 çº§çš„æ•°æ® + + @Schema(description = "推广用户数é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019") + private Integer brokerageUserCount; + @Schema(description = "æŽ¨å¹¿è®¢å•æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019") + private Integer brokerageOrderCount; + @Schema(description = "推广订å•金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019") + private Integer brokerageOrderPrice; + + // ========== æçŽ°ä¿¡æ¯ ========== + + @Schema(description = "å·²æçް金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019") + private Integer withdrawPrice; + @Schema(description = "å·²æçŽ°æ¬¡æ•°", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019") + private Integer withdrawCount; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageEnabledReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageEnabledReqVO.java new file mode 100644 index 0000000..70f36a7 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageEnabledReqVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - 分销用户 - 修改推广员 Request VO") +@Data +@ToString(callSuper = true) +public class BrokerageUserUpdateBrokerageEnabledReqVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "推广资格", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "推广资格ä¸èƒ½ä¸ºç©º") + private Boolean enabled; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageUserReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageUserReqVO.java new file mode 100644 index 0000000..57fbc5e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/user/BrokerageUserUpdateBrokerageUserReqVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - 分销用户 - 修改推广员 Request VO") +@Data +@ToString(callSuper = true) +public class BrokerageUserUpdateBrokerageUserReqVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20019") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "推广员编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "4587") + @NotNull(message = "推广员编å·ä¸èƒ½ä¸ºç©º") + private Long bindUserId; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawBaseVO.java new file mode 100644 index 0000000..b27f83c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawBaseVO.java @@ -0,0 +1,68 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +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; + +/** + * 佣金æçް Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class BrokerageWithdrawBaseVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "11436") + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + + @Schema(description = "æçް金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "18781") + @NotNull(message = "æçް金é¢ä¸èƒ½ä¸ºç©º") + private Integer price; + + @Schema(description = "æçŽ°æ‰‹ç»­è´¹", requiredMode = Schema.RequiredMode.REQUIRED, example = "11417") + @NotNull(message = "æçŽ°æ‰‹ç»­è´¹ä¸èƒ½ä¸ºç©º") + private Integer feePrice; + + @Schema(description = "当剿€»ä½£é‡‘", requiredMode = Schema.RequiredMode.REQUIRED, example = "18576") + @NotNull(message = "当剿€»ä½£é‡‘ä¸èƒ½ä¸ºç©º") + private Integer totalPrice; + + @Schema(description = "æçŽ°ç±»åž‹", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "æçŽ°ç±»åž‹ä¸èƒ½ä¸ºç©º") + private Integer type; + + @Schema(description = "真实姓å", example = "赵六") + private String name; + + @Schema(description = "è´¦å·", example = "88677912132") + private String accountNo; + + @Schema(description = "银行åç§°", example = "1") + private String bankName; + + @Schema(description = "开户地å€", example = "海淀支行") + private String bankAddress; + + @Schema(description = "收款ç ", example = "https://www.iocoder.cn") + private String accountQrCodeUrl; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状æ€ä¸èƒ½ä¸ºç©º") + private Integer status; + + @Schema(description = "审核驳回原因", example = "ä¸å¯¹") + private String auditReason; + + @Schema(description = "审核时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime auditTime; + + @Schema(description = "备注", example = "éšä¾¿") + private String remark; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawPageReqVO.java new file mode 100644 index 0000000..4534350 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawPageReqVO.java @@ -0,0 +1,47 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 BrokerageWithdrawPageReqVO extends PageParam { + + @Schema(description = "用户编å·", example = "11436") + private Long userId; + + @Schema(description = "æçŽ°ç±»åž‹", example = "1") + @InEnum(value = BrokerageWithdrawTypeEnum.class, message = "æçŽ°ç±»åž‹å¿…é¡»æ˜¯ {value}") + private Integer type; + + @Schema(description = "真实姓å", example = "赵六") + private String name; + + @Schema(description = "è´¦å·", example = "886779132") + private String accountNo; + + @Schema(description = "银行åç§°", example = "1") + private String bankName; + + @Schema(description = "状æ€", example = "1") + @InEnum(value = BrokerageWithdrawStatusEnum.class, message = "状æ€å¿…须是 {value}") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRejectReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRejectReqVO.java new file mode 100644 index 0000000..63aa0fb --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRejectReqVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - 驳回申请 Request VO") +@Data +@ToString(callSuper = true) +public class BrokerageWithdrawRejectReqVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "7161") + @NotNull(message = "ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "审核驳回原因", example = "ä¸å¯¹") + @NotEmpty(message = "审核驳回原因ä¸èƒ½ä¸ºç©º") + private String auditReason; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRespVO.java new file mode 100644 index 0000000..b752636 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRespVO.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 佣金æçް Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BrokerageWithdrawRespVO extends BrokerageWithdrawBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "7161") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String userNickname; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/TradeConfigController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/TradeConfigController.java new file mode 100644 index 0000000..69d5682 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/TradeConfigController.java @@ -0,0 +1,53 @@ +package com.tashow.cloud.trade.controller.admin.config; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import com.tashow.cloud.trade.controller.admin.config.vo.TradeConfigRespVO; +import com.tashow.cloud.trade.controller.admin.config.vo.TradeConfigSaveReqVO; +import com.tashow.cloud.trade.convert.config.TradeConfigConvert; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; +import com.tashow.cloud.trade.service.config.TradeConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管ç†åŽå° - 交易中心é…ç½®") +@RestController +@RequestMapping("/trade/config") +@Validated +public class TradeConfigController { + + @Resource + private TradeConfigService tradeConfigService; + + @Value("${yudao.tencent-lbs-key}") + private String tencentLbsKey; + + @PutMapping("/save") + @Operation(summary = "更新交易中心é…ç½®") + @PreAuthorize("@ss.hasPermission('trade:config:save')") + public CommonResult updateConfig(@Valid @RequestBody TradeConfigSaveReqVO updateReqVO) { + tradeConfigService.saveTradeConfig(updateReqVO); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得交易中心é…ç½®") + @PreAuthorize("@ss.hasPermission('trade:config:query')") + public CommonResult getConfig() { + TradeConfigDO config = tradeConfigService.getTradeConfig(); + TradeConfigRespVO configVO = TradeConfigConvert.INSTANCE.convert(config); + if (configVO != null) { + configVO.setTencentLbsKey(tencentLbsKey); + } + return success(configVO); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigBaseVO.java new file mode 100644 index 0000000..d39226f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigBaseVO.java @@ -0,0 +1,100 @@ +package com.tashow.cloud.trade.controller.admin.config.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.PositiveOrZero; +import java.util.List; + +/** + * 交易中心é…ç½® Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class TradeConfigBaseVO { + + // ========== å”®åŽç›¸å…³ ========== + + @Schema(description = "å”®åŽçš„退款ç†ç”±", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "å”®åŽçš„退款ç†ç”±ä¸èƒ½ä¸ºç©º") + private List afterSaleRefundReasons; + + @Schema(description = "å”®åŽçš„退货ç†ç”±", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "å”®åŽçš„退货ç†ç”±ä¸èƒ½ä¸ºç©º") + private List afterSaleReturnReasons; + + // ========== é…é€ç›¸å…³ ========== + + /** + * 是å¦å¯ç”¨å…¨åœºåŒ…é‚® + */ + @Schema(description = "是å¦å¯ç”¨å…¨åœºåŒ…é‚®", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是å¦å¯ç”¨å…¨åœºåŒ…é‚®ä¸èƒ½ä¸ºç©º") + private Boolean deliveryExpressFreeEnabled; + + @Schema(description = "全场包邮的最å°é‡‘é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + @NotNull(message = "全场包邮的最å°é‡‘é¢ä¸èƒ½ä¸ºç©º") + @PositiveOrZero(message = "全场包邮的最å°é‡‘é¢ä¸èƒ½æ˜¯è´Ÿæ•°") + private Integer deliveryExpressFreePrice; + + @Schema(description = "是å¦å¼€å¯è‡ªæ", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是å¦å¼€å¯è‡ªæä¸èƒ½ä¸ºç©º") + private Boolean deliveryPickUpEnabled; + + // ========== 分销相关 ========== + + @Schema(description = "是å¦å¯ç”¨åˆ†ä½£", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是å¦å¯ç”¨åˆ†ä½£ä¸èƒ½ä¸ºç©º") + private Boolean brokerageEnabled; + + @Schema(description = "分佣模å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @NotNull(message = "分佣模å¼ä¸èƒ½ä¸ºç©º") + @InEnum(value = BrokerageEnabledConditionEnum.class, message = "分佣模å¼å¿…须是 {value}") + private Integer brokerageEnabledCondition; + + @Schema(description = "分销关系绑定模å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @NotNull(message = "分销关系绑定模å¼ä¸èƒ½ä¸ºç©º") + @InEnum(value = BrokerageBindModeEnum.class, message = "分销关系绑定模å¼å¿…须是 {value}") + private Integer brokerageBindMode; + + @Schema(description = "åˆ†é”€æµ·æŠ¥å›¾åœ°å€æ•°ç»„", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/yudao.jpg]") + private List brokeragePosterUrls; + + @Schema(description = "一级返佣比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + @NotNull(message = "一级返佣比例ä¸èƒ½ä¸ºç©º") + @Range(min = 0, max = 100, message = "一级返佣比例必须在 0 - 100 之间") + private Integer brokerageFirstPercent; + + @Schema(description = "二级返佣比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + @NotNull(message = "二级返佣比例ä¸èƒ½ä¸ºç©º") + @Range(min = 0, max = 100, message = "二级返佣比例必须在 0 - 100 之间") + private Integer brokerageSecondPercent; + + @Schema(description = "用户æçŽ°æœ€ä½Žé‡‘é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + @NotNull(message = "用户æçŽ°æœ€ä½Žé‡‘é¢ä¸èƒ½ä¸ºç©º") + @PositiveOrZero(message = "用户æçŽ°æœ€ä½Žé‡‘é¢ä¸èƒ½æ˜¯è´Ÿæ•°") + private Integer brokerageWithdrawMinPrice; + + @Schema(description = "用户æçŽ°æ‰‹ç»­è´¹ç™¾åˆ†æ¯”", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + @NotNull(message = "用户æçŽ°æ‰‹ç»­è´¹ç™¾åˆ†æ¯”ä¸èƒ½ä¸ºç©º") + @PositiveOrZero(message = "用户æçŽ°æ‰‹ç»­è´¹ç™¾åˆ†æ¯”ä¸èƒ½æ˜¯è´Ÿæ•°") + private Integer brokerageWithdrawFeePercent; + + @Schema(description = "佣金冻结时间(天)", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") + @NotNull(message = "佣金冻结时间(天)ä¸èƒ½ä¸ºç©º") + @PositiveOrZero(message = "佣金冻结时间ä¸èƒ½æ˜¯è´Ÿæ•°") + private Integer brokerageFrozenDays; + + @Schema(description = "æçŽ°æ–¹å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "[0, 1]") + @NotEmpty(message = "æçŽ°æ–¹å¼ä¸èƒ½ä¸ºç©º") + @InEnum(value = BrokerageWithdrawTypeEnum.class, message = "æçŽ°æ–¹å¼å¿…须是 {value}") + private List brokerageWithdrawTypes; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigRespVO.java new file mode 100644 index 0000000..a55e641 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigRespVO.java @@ -0,0 +1,20 @@ +package com.tashow.cloud.trade.controller.admin.config.vo; + +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 TradeConfigRespVO extends TradeConfigBaseVO { + + @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "腾讯地图 KEY", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + private String tencentLbsKey; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigSaveReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigSaveReqVO.java new file mode 100644 index 0000000..d0b7b02 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/config/vo/TradeConfigSaveReqVO.java @@ -0,0 +1,14 @@ +package com.tashow.cloud.trade.controller.admin.config.vo; + +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 TradeConfigSaveReqVO extends TradeConfigBaseVO { + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryExpressController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryExpressController.java new file mode 100644 index 0000000..91e59a5 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryExpressController.java @@ -0,0 +1,97 @@ +package com.tashow.cloud.trade.controller.admin.delivery; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.*; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.*; +import com.tashow.cloud.trade.convert.delivery.DeliveryExpressConvert; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.tashow.cloud.trade.service.delivery.DeliveryExpressService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管ç†åŽå° - 快递公å¸") +@RestController +@RequestMapping("/trade/delivery/express") +@Validated +public class DeliveryExpressController { + + @Resource + private DeliveryExpressService deliveryExpressService; + + @PostMapping("/create") + @Operation(summary = "创建快递公å¸") + @PreAuthorize("@ss.hasPermission('trade:delivery:express:create')") + public CommonResult createDeliveryExpress(@Valid @RequestBody DeliveryExpressCreateReqVO createReqVO) { + return success(deliveryExpressService.createDeliveryExpress(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新快递公å¸") + @PreAuthorize("@ss.hasPermission('trade:delivery:express:update')") + public CommonResult updateDeliveryExpress(@Valid @RequestBody DeliveryExpressUpdateReqVO updateReqVO) { + deliveryExpressService.updateDeliveryExpress(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除快递公å¸") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('trade:delivery:express:delete')") + public CommonResult deleteDeliveryExpress(@RequestParam("id") Long id) { + deliveryExpressService.deleteDeliveryExpress(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得快递公å¸") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('trade:delivery:express:query')") + public CommonResult getDeliveryExpress(@RequestParam("id") Long id) { + DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(id); + return success(DeliveryExpressConvert.INSTANCE.convert(deliveryExpress)); + } + + @GetMapping("/list-all-simple") + @Operation(summary = "获å–快递公å¸ç²¾ç®€ä¿¡æ¯åˆ—表", description = "主è¦ç”¨äºŽå‰ç«¯çš„下拉选项") + public CommonResult> getSimpleDeliveryExpressList() { + List list = deliveryExpressService.getDeliveryExpressListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(DeliveryExpressConvert.INSTANCE.convertList1(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得快递公å¸åˆ†é¡µ") + @PreAuthorize("@ss.hasPermission('trade:delivery:express:query')") + public CommonResult> getDeliveryExpressPage(@Valid DeliveryExpressPageReqVO pageVO) { + PageResult pageResult = deliveryExpressService.getDeliveryExpressPage(pageVO); + return success(DeliveryExpressConvert.INSTANCE.convertPage(pageResult)); + } + + @GetMapping("/export-excel") + @Operation(summary = "å¯¼å‡ºå¿«é€’å…¬å¸ Excel") + @PreAuthorize("@ss.hasPermission('trade:delivery:express:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeliveryExpressExcel(@Valid DeliveryExpressExportReqVO exportReqVO, + HttpServletResponse response) throws IOException { + List list = deliveryExpressService.getDeliveryExpressList(exportReqVO); + // 导出 Excel + List dataList = DeliveryExpressConvert.INSTANCE.convertList02(list); + ExcelUtils.write(response, "快递公å¸.xls", "æ•°æ®", DeliveryExpressExcelVO.class, dataList); + } +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryExpressTemplateController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryExpressTemplateController.java new file mode 100644 index 0000000..c106810 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryExpressTemplateController.java @@ -0,0 +1,91 @@ +package com.tashow.cloud.trade.controller.admin.delivery; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.*; +import com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate.*; +import com.tashow.cloud.trade.convert.delivery.DeliveryExpressTemplateConvert; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; +import com.tashow.cloud.trade.service.delivery.DeliveryExpressTemplateService; +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.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管ç†åŽå° - 快递è¿è´¹æ¨¡æ¿") +@RestController +@RequestMapping("/trade/delivery/express-template") +@Validated +public class DeliveryExpressTemplateController { + + @Resource + private DeliveryExpressTemplateService deliveryExpressTemplateService; + + @PostMapping("/create") + @Operation(summary = "创建快递è¿è´¹æ¨¡æ¿") + @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:create')") + public CommonResult createDeliveryExpressTemplate(@Valid @RequestBody DeliveryExpressTemplateCreateReqVO createReqVO) { + return success(deliveryExpressTemplateService.createDeliveryExpressTemplate(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新快递è¿è´¹æ¨¡æ¿") + @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:update')") + public CommonResult updateDeliveryExpressTemplate(@Valid @RequestBody DeliveryExpressTemplateUpdateReqVO updateReqVO) { + deliveryExpressTemplateService.updateDeliveryExpressTemplate(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除快递è¿è´¹æ¨¡æ¿") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:delete')") + public CommonResult deleteDeliveryExpressTemplate(@RequestParam("id") Long id) { + deliveryExpressTemplateService.deleteDeliveryExpressTemplate(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得快递è¿è´¹æ¨¡æ¿") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')") + public CommonResult getDeliveryExpressTemplate(@RequestParam("id") Long id) { + return success(deliveryExpressTemplateService.getDeliveryExpressTemplate(id)); + } + + @GetMapping("/list") + @Operation(summary = "获得快递è¿è´¹æ¨¡æ¿åˆ—表") + @Parameter(name = "ids", description = "ç¼–å·åˆ—表", required = true, example = "1024,2048") + @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')") + public CommonResult> getDeliveryExpressTemplateList(@RequestParam("ids") Collection ids) { + List list = deliveryExpressTemplateService.getDeliveryExpressTemplateList(ids); + return success(DeliveryExpressTemplateConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/list-all-simple") + @Operation(summary = "获å–快递模版精简信æ¯åˆ—表", description = "主è¦ç”¨äºŽå‰ç«¯çš„下拉选项") + public CommonResult> getSimpleTemplateList() { + // 获å–è¿è´¹æ¨¡ç‰ˆåˆ—表,åªè¦å¼€å¯çжæ€çš„ + List list = deliveryExpressTemplateService.getDeliveryExpressTemplateList(); + // 排åºåŽï¼Œè¿”回给å‰ç«¯ + return success(DeliveryExpressTemplateConvert.INSTANCE.convertList1(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得快递è¿è´¹æ¨¡æ¿åˆ†é¡µ") + @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')") + public CommonResult> getDeliveryExpressTemplatePage(@Valid DeliveryExpressTemplatePageReqVO pageVO) { + PageResult pageResult = deliveryExpressTemplateService.getDeliveryExpressTemplatePage(pageVO); + return success(DeliveryExpressTemplateConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryPickUpStoreController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryPickUpStoreController.java new file mode 100644 index 0000000..b6981b4 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/DeliveryPickUpStoreController.java @@ -0,0 +1,114 @@ +package com.tashow.cloud.trade.controller.admin.delivery; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import com.tashow.cloud.trade.controller.admin.base.system.user.UserSimpleBaseVO; +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.*; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.*; +import com.tashow.cloud.trade.convert.delivery.DeliveryPickUpStoreConvert; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; +import com.tashow.cloud.trade.service.delivery.DeliveryPickUpStoreService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管ç†åŽå° - 自æé—¨åº—") +@RestController +@RequestMapping("/trade/delivery/pick-up-store") +@Validated +public class DeliveryPickUpStoreController { + + @Resource + private DeliveryPickUpStoreService deliveryPickUpStoreService; + + @Resource + private AdminUserApi adminUserApi; + + @PostMapping("/create") + @Operation(summary = "创建自æé—¨åº—") + @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:create')") + public CommonResult createDeliveryPickUpStore(@Valid @RequestBody DeliveryPickUpStoreCreateReqVO createReqVO) { + return success(deliveryPickUpStoreService.createDeliveryPickUpStore(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新自æé—¨åº—") + @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:update')") + public CommonResult updateDeliveryPickUpStore(@Valid @RequestBody DeliveryPickUpStoreUpdateReqVO updateReqVO) { + deliveryPickUpStoreService.updateDeliveryPickUpStore(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除自æé—¨åº—") + @Parameter(name = "id", description = "ç¼–å·", required = true) + @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:delete')") + public CommonResult deleteDeliveryPickUpStore(@RequestParam("id") Long id) { + deliveryPickUpStoreService.deleteDeliveryPickUpStore(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得自æé—¨åº—") + @Parameter(name = "id", description = "ç¼–å·", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:query')") + public CommonResult getDeliveryPickUpStore(@RequestParam("id") Long id) { + DeliveryPickUpStoreDO deliveryPickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(id); + if (deliveryPickUpStore == null) { + return success(null); + } + List verifyUsers = CollUtil.isNotEmpty(deliveryPickUpStore.getVerifyUserIds()) ? + adminUserApi.getUserList(deliveryPickUpStore.getVerifyUserIds()).getCheckedData() : null; + return success(BeanUtils.toBean(deliveryPickUpStore, DeliveryPickUpStoreRespVO.class) + .setVerifyUsers(BeanUtils.toBean(verifyUsers, UserSimpleBaseVO.class))); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得自æé—¨åº—精简信æ¯åˆ—表") + public CommonResult> getSimpleDeliveryPickUpStoreList() { + List list = deliveryPickUpStoreService.getDeliveryPickUpStoreListByStatus( + CommonStatusEnum.ENABLE.getStatus()); + return success(DeliveryPickUpStoreConvert.INSTANCE.convertList1(list)); + } + + @GetMapping("/list") + @Operation(summary = "获得自æé—¨åº—列表") + @Parameter(name = "ids", description = "ç¼–å·åˆ—表", required = true, example = "1024,2048") + @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:query')") + public CommonResult> getDeliveryPickUpStoreList(@RequestParam("ids") Collection ids) { + List list = deliveryPickUpStoreService.getDeliveryPickUpStoreList(ids); + return success(DeliveryPickUpStoreConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得自æé—¨åº—分页") + @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:query')") + public CommonResult> getDeliveryPickUpStorePage(@Valid DeliveryPickUpStorePageReqVO pageVO) { + PageResult pageResult = deliveryPickUpStoreService.getDeliveryPickUpStorePage(pageVO); + return success(DeliveryPickUpStoreConvert.INSTANCE.convertPage(pageResult)); + } + + @PostMapping("/bind") + @Operation(summary = "绑定自æåº—员") + @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:create')") + public CommonResult bindDeliveryPickUpStore(@Valid @RequestBody DeliveryPickUpBindReqVO bindReqVO) { + deliveryPickUpStoreService.bindDeliveryPickUpStore(bindReqVO); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressBaseVO.java new file mode 100644 index 0000000..a1cc0b8 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressBaseVO.java @@ -0,0 +1,34 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.express; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +/** +* å¿«é€’å…¬å¸ Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 +* å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ +*/ +@Data +public class DeliveryExpressBaseVO { + + @Schema(description = "快递公å¸ç¼–ç ", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "快递公å¸ç¼–ç ä¸èƒ½ä¸ºç©º") + private String code; + + @Schema(description = "快递公å¸åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + @NotNull(message = "快递公å¸åç§°ä¸èƒ½ä¸ºç©º") + private String name; + + @Schema(description = "快递公å¸logo") + private String logo; + + @Schema(description = "排åº", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "排åºä¸èƒ½ä¸ºç©º") + private Integer sort; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状æ€ä¸èƒ½ä¸ºç©º") + private Integer status; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressCreateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressCreateReqVO.java new file mode 100644 index 0000000..0af5173 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressCreateReqVO.java @@ -0,0 +1,12 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.express; + +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "管ç†åŽå° - 快递公å¸åˆ›å»º Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeliveryExpressCreateReqVO extends DeliveryExpressBaseVO { + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressExcelVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressExcelVO.java new file mode 100644 index 0000000..3b020c1 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressExcelVO.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.express; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * å¿«é€’å…¬å¸ Excel VO + */ +@Data +public class DeliveryExpressExcelVO { + + @ExcelProperty("ç¼–å·") + private Long id; + + @ExcelProperty("快递公å¸ç¼–ç ") + private String code; + + @ExcelProperty("快递公å¸åç§°") + private String name; + + @ExcelProperty("å¿«é€’å…¬å¸ logo") + private String logo; + + @ExcelProperty("排åº") + private Integer sort; + + @ExcelProperty(value = "状æ€", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressExportReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressExportReqVO.java new file mode 100644 index 0000000..4e14e5e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressExportReqVO.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.express; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +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 = "管ç†åŽå° - å¿«é€’å…¬å¸ Excel 导出 Request VO") +@Data +public class DeliveryExpressExportReqVO { + + @Schema(description = "快递公å¸ç¼–ç ") + private String code; + + @Schema(description = "快递公å¸åç§°", example = "æŽå››") + private String name; + + @Schema(description = "状æ€ï¼ˆ0正常 1åœç”¨ï¼‰", example = "1") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressPageReqVO.java new file mode 100644 index 0000000..262f495 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressPageReqVO.java @@ -0,0 +1,31 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.express; + +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 DeliveryExpressPageReqVO extends PageParam { + + @Schema(description = "快递公å¸ç¼–ç ") + private String code; + + @Schema(description = "快递公å¸åç§°", example = "æŽå››") + private String name; + + @Schema(description = "状æ€ï¼ˆ0正常 1åœç”¨ï¼‰", example = "1") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressRespVO.java new file mode 100644 index 0000000..6acb9c0 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.express; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - å¿«é€’å…¬å¸ Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeliveryExpressRespVO extends DeliveryExpressBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "6592") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressSimpleRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressSimpleRespVO.java new file mode 100644 index 0000000..d23df33 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressSimpleRespVO.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.express; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - 快递公å¸ç²¾ç®€ä¿¡æ¯ Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DeliveryExpressSimpleRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "6592") + @NotNull(message = "ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "快递公å¸åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "顺丰速è¿") + @NotNull(message = "快递公å¸åç§°ä¸èƒ½ä¸ºç©º") + private String name; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressUpdateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressUpdateReqVO.java new file mode 100644 index 0000000..d3f4a01 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/express/DeliveryExpressUpdateReqVO.java @@ -0,0 +1,20 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.express; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - å¿«é€’å…¬å¸æ›´æ–° Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeliveryExpressUpdateReqVO extends DeliveryExpressBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "6592") + @NotNull(message = "ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateBaseVO.java new file mode 100644 index 0000000..83cd958 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateBaseVO.java @@ -0,0 +1,27 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +/** +* 快递è¿è´¹æ¨¡æ¿ Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 +* å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ +*/ +@Data +public class DeliveryExpressTemplateBaseVO { + + @Schema(description = "模æ¿åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @NotNull(message = "模æ¿åç§°ä¸èƒ½ä¸ºç©º") + private String name; + + @Schema(description = "é…é€è®¡è´¹æ–¹å¼ 1:按件 2:按é‡é‡ 3:按体积", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "é…é€è®¡è´¹æ–¹å¼ 1:按件 2:按é‡é‡ 3:按体积ä¸èƒ½ä¸ºç©º") + private Integer chargeMode; + + @Schema(description = "排åº", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "排åºä¸èƒ½ä¸ºç©º") + private Integer sort; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateChargeBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateChargeBaseVO.java new file mode 100644 index 0000000..819ee12 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateChargeBaseVO.java @@ -0,0 +1,38 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +/** + * 快递è¿è´¹æ¨¡æ¿è¿è´¹è®¾ç½® Base VO,æä¾›ç»™æ·»åŠ è¿è´¹æ¨¡æ¿ä½¿ç”¨ + */ +@Data +public class DeliveryExpressTemplateChargeBaseVO { + + @Schema(description = "ç¼–å·", example = "6592", hidden = true) // 由于想简å•一点,å¤ç”¨è¿™ä¸ª VO 在更新æ“作,所以 hidden 为 false + private Long id; + + @Schema(description = "区域编å·åˆ—表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,120000]") + @NotEmpty(message = "区域编å·åˆ—表ä¸èƒ½ä¸ºç©º") + private List areaIds; + + @Schema(description = "首件数é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + @NotNull(message = "首件数é‡ä¸èƒ½ä¸ºç©º") + private Double startCount; + + @Schema(description = "起步价", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + @NotNull(message = "起步价ä¸èƒ½ä¸ºç©º") + private Integer startPrice; + + @Schema(description = "ç»­ä»¶æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "ç»­ä»¶æ•°é‡ä¸èƒ½ä¸ºç©º") + private Double extraCount; + + @Schema(description = "é¢å¤–ä»·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000") + @NotNull(message = "é¢å¤–ä»·ä¸èƒ½ä¸ºç©º") + private Integer extraPrice; +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateCreateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateCreateReqVO.java new file mode 100644 index 0000000..17b0984 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateCreateReqVO.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import jakarta.validation.Valid; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 快递è¿è´¹æ¨¡æ¿åˆ›å»º Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeliveryExpressTemplateCreateReqVO extends DeliveryExpressTemplateBaseVO { + + @Schema(description = "区域è¿è´¹åˆ—表") + @Valid + private List charges; + + @Schema(description = "包邮区域列表") + @Valid + private List frees; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java new file mode 100644 index 0000000..dd2e05c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +@Schema(description = "管ç†åŽå° - 快递è¿è´¹æ¨¡æ¿çš„详细 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeliveryExpressTemplateDetailRespVO extends DeliveryExpressTemplateBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "371") + private Long id; + + @Schema(description = "è¿è´¹æ¨¡æ¿è¿è´¹è®¾ç½®", requiredMode = Schema.RequiredMode.REQUIRED) + private List charges; + + @Schema(description = "è¿è´¹æ¨¡æ¿åŒ…邮区域", requiredMode = Schema.RequiredMode.REQUIRED) + private List frees; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateFreeBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateFreeBaseVO.java new file mode 100644 index 0000000..b9b39fb --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateFreeBaseVO.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +/** + * 快递è¿è´¹æ¨¡æ¿åŒ…é‚® Base VO,æä¾›ç»™æ·»åŠ è¿è´¹æ¨¡æ¿ä½¿ç”¨ + */ +@Data +public class DeliveryExpressTemplateFreeBaseVO { + + @Schema(description = "区域编å·åˆ—表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,120000]") + @NotEmpty(message = "区域编å·åˆ—表ä¸èƒ½ä¸ºç©º") + private List areaIds; + + @Schema(description = "包邮金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "5000") + @NotNull(message = "包邮金é¢ä¸èƒ½ä¸ºç©º") + private Integer freePrice; + + @Schema(description = "包邮件数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + @NotNull(message = "包邮件数ä¸èƒ½ä¸ºç©º") + private Integer freeCount; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplatePageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplatePageReqVO.java new file mode 100644 index 0000000..76bb307 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplatePageReqVO.java @@ -0,0 +1,30 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 DeliveryExpressTemplatePageReqVO extends PageParam { + + @Schema(description = "模æ¿åç§°", example = "王五") + private String name; + + @Schema(description = "é…é€è®¡è´¹æ–¹å¼ 1:按件 2:按é‡é‡ 3:按体积") + private Integer chargeMode; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java new file mode 100644 index 0000000..2908291 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - 快递è¿è´¹æ¨¡æ¿ Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeliveryExpressTemplateRespVO extends DeliveryExpressTemplateBaseVO { + + @Schema(description = "ç¼–å·ï¼Œè‡ªå¢ž", requiredMode = Schema.RequiredMode.REQUIRED, example = "371") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java new file mode 100644 index 0000000..4080140 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@Schema(description = "管ç†åŽå° - æ¨¡ç‰ˆç²¾ç®€ä¿¡æ¯ Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DeliveryExpressTemplateSimpleRespVO { + + @Schema(description = "模版编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "模æ¿åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试模版") + private String name; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateUpdateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateUpdateReqVO.java new file mode 100644 index 0000000..66d0686 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateUpdateReqVO.java @@ -0,0 +1,30 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 快递è¿è´¹æ¨¡æ¿æ›´æ–° Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeliveryExpressTemplateUpdateReqVO extends DeliveryExpressTemplateBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "371") + @NotNull(message = "ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "区域è¿è´¹åˆ—表") + @Valid + private List charges; + + @Schema(description = "包邮区域列表") + @Valid + private List frees; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpBindReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpBindReqVO.java new file mode 100644 index 0000000..ed62424 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpBindReqVO.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.pickup; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.ToString; + +import java.util.List; + +@Schema(description = "管ç†åŽå° - 自æé—¨åº—绑定核销人 Request VO") +@Data +@ToString(callSuper = true) +public class DeliveryPickUpBindReqVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128") + @NotNull(message = "ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "绑定用户编å·ç»„æ•°", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128") + @NotEmpty(message = "绑定用户编å·ç»„æ•°ä¸èƒ½æœªç©º") + private List verifyUserIds; + +} \ No newline at end of file diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreBaseVO.java new file mode 100644 index 0000000..e268757 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreBaseVO.java @@ -0,0 +1,68 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.pickup; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalTime; + +/** +* 自æé—¨åº— Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 +* å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ +*/ +@Data +public class DeliveryPickUpStoreBaseVO { + + @Schema(description = "门店åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + @NotBlank(message = "门店åç§°ä¸èƒ½ä¸ºç©º") + private String name; + + @Schema(description = "门店简介", example = "我是门店简介") + private String introduction; + + @Schema(description = "门店手机", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601892312") + @NotBlank(message = "门店手机ä¸èƒ½ä¸ºç©º") + @Mobile + private String phone; + + @Schema(description = "区域编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "18733") + @NotNull(message = "区域编å·ä¸èƒ½ä¸ºç©º") + private Integer areaId; + + @Schema(description = "门店详细地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "夿—¦å¤§å­¦è·¯ 188 å·") + @NotBlank(message = "门店详细地å€ä¸èƒ½ä¸ºç©º") + private String detailAddress; + + @Schema(description = "门店 logo", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @NotBlank(message = "门店 logo ä¸èƒ½ä¸ºç©º") + private String logo; + + @Schema(description = "è¥ä¸šå¼€å§‹æ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "è¥ä¸šå¼€å§‹æ—¶é—´ä¸èƒ½ä¸ºç©º") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm") + private LocalTime openingTime; + + @Schema(description = "è¥ä¸šç»“æŸæ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "è¥ä¸šç»“æŸæ—¶é—´ä¸èƒ½ä¸ºç©º") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm") + private LocalTime closingTime; + + @Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED, example = "5.88") + @NotNull(message = "纬度ä¸èƒ½ä¸ºç©º") + private Double latitude; + + @Schema(description = "ç»åº¦", requiredMode = Schema.RequiredMode.REQUIRED, example = "6.99") + @NotNull(message = "ç»åº¦ä¸èƒ½ä¸ºç©º") + private Double longitude; + + @Schema(description = "门店状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "门店状æ€ä¸èƒ½ä¸ºç©º") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreCreateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreCreateReqVO.java new file mode 100644 index 0000000..86a47f9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreCreateReqVO.java @@ -0,0 +1,14 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.pickup; + +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 DeliveryPickUpStoreCreateReqVO extends DeliveryPickUpStoreBaseVO { + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStorePageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStorePageReqVO.java new file mode 100644 index 0000000..03050bf --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStorePageReqVO.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.pickup; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +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 DeliveryPickUpStorePageReqVO extends PageParam { + + @Schema(description = "门店åç§°", example = "æŽå››") + private String name; + + @Schema(description = "门店手机") + private String phone; + + @Schema(description = "区域编å·", example = "18733") + private Integer areaId; + + @Schema(description = "门店状æ€", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreRespVO.java new file mode 100644 index 0000000..d02f7ff --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreRespVO.java @@ -0,0 +1,27 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.pickup; + +import com.tashow.cloud.trade.controller.admin.base.system.user.UserSimpleBaseVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 自æé—¨åº— Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeliveryPickUpStoreRespVO extends DeliveryPickUpStoreBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "核销用户数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List verifyUsers; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreSimpleRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreSimpleRespVO.java new file mode 100644 index 0000000..d1630d6 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreSimpleRespVO.java @@ -0,0 +1,37 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.pickup; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Schema(description = "管ç†åŽå° - 自æé—¨åº—ç²¾ç®€ä¿¡æ¯ Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DeliveryPickUpStoreSimpleRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128") + private Long id; + + @Schema(description = "门店åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + private String name; + + @Schema(description = "门店手机", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601892312") + private String phone; + + @Schema(description = "区域编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "18733") + private Integer areaId; + + @Schema(description = "区域åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "xx市") + private String areaName; + + @Schema(description = "门店详细地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "夿—¦å¤§å­¦è·¯ 188 å·") + private String detailAddress; + + @Schema(description = "绑定用户编å·ç»„æ•°", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128") + private List verifyUserIds; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreUpdateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreUpdateReqVO.java new file mode 100644 index 0000000..b0acbe3 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreUpdateReqVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.trade.controller.admin.delivery.vo.pickup; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管ç†åŽå° - 自æé—¨åº—æ›´æ–° Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DeliveryPickUpStoreUpdateReqVO extends DeliveryPickUpStoreBaseVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128") + @NotNull(message = "ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/TradeOrderController.http b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/TradeOrderController.http new file mode 100644 index 0000000..5255d1b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/TradeOrderController.http @@ -0,0 +1,14 @@ +### 获得交易订å•分页 => æˆåŠŸ +GET {{baseUrl}}/trade/order/page?pageNo=1&pageSize=10 +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +### 获得交易订å•分页 => æˆåŠŸ +GET {{baseUrl}}/trade/order/get-detail?id=21 +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +### 获得交易订å•的物æµè½¨è¿¹ => æˆåŠŸ +GET {{baseUrl}}/trade/order/get-express-track-list?id=21 +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} \ No newline at end of file diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/TradeOrderController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/TradeOrderController.java new file mode 100644 index 0000000..8f3d04f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/TradeOrderController.java @@ -0,0 +1,170 @@ +package com.tashow.cloud.trade.controller.admin.order; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.trade.controller.admin.order.vo.*; +import com.tashow.cloud.trade.controller.admin.order.vo.*; +import com.tashow.cloud.trade.convert.order.TradeOrderConvert; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderLogDO; +import com.tashow.cloud.trade.service.order.TradeOrderLogService; +import com.tashow.cloud.trade.service.order.TradeOrderQueryService; +import com.tashow.cloud.trade.service.order.TradeOrderUpdateService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; + +@Tag(name = "管ç†åŽå° - 交易订å•") +@RestController +@RequestMapping("/trade/order") +@Validated +@Slf4j +public class TradeOrderController { + + @Resource + private TradeOrderUpdateService tradeOrderUpdateService; + @Resource + private TradeOrderQueryService tradeOrderQueryService; + @Resource + private TradeOrderLogService tradeOrderLogService; + + @Resource + private MemberUserApi memberUserApi; + + @GetMapping("/page") + @Operation(summary = "获得交易订å•分页") + @PreAuthorize("@ss.hasPermission('trade:order:query')") + public CommonResult> getOrderPage(TradeOrderPageReqVO reqVO) { + // æŸ¥è¯¢è®¢å• + PageResult pageResult = tradeOrderQueryService.getOrderPage(reqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // æŸ¥è¯¢ç”¨æˆ·ä¿¡æ¯ + Set userIds = CollUtil.unionDistinct(convertList(pageResult.getList(), TradeOrderDO::getUserId), + convertList(pageResult.getList(), TradeOrderDO::getBrokerageUserId, Objects::nonNull)); + Map userMap = memberUserApi.getUserMap(userIds); + // 查询订å•项 + List orderItems = tradeOrderQueryService.getOrderItemListByOrderId( + convertSet(pageResult.getList(), TradeOrderDO::getId)); + // æœ€ç»ˆç»„åˆ + return success(TradeOrderConvert.INSTANCE.convertPage(pageResult, orderItems, userMap)); + } + + @GetMapping("/summary") + @Operation(summary = "获得交易订å•统计") + @PreAuthorize("@ss.hasPermission('trade:order:query')") + public CommonResult getOrderSummary(TradeOrderPageReqVO reqVO) { + return success(tradeOrderQueryService.getOrderSummary(reqVO)); + } + + @GetMapping("/get-detail") + @Operation(summary = "获得交易订å•详情") + @Parameter(name = "id", description = "订å•ç¼–å·", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('trade:order:query')") + public CommonResult getOrderDetail(@RequestParam("id") Long id) { + // æŸ¥è¯¢è®¢å• + TradeOrderDO order = tradeOrderQueryService.getOrder(id); + if (order == null) { + return success(null); + } + // 查询订å•项 + List orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id); + + // æ‹¼æŽ¥æ•°æ® + MemberUserRespDTO user = memberUserApi.getUser(order.getUserId()).getCheckedData(); + MemberUserRespDTO brokerageUser = order.getBrokerageUserId() != null ? + memberUserApi.getUser(order.getBrokerageUserId()).getCheckedData() : null; + List orderLogs = tradeOrderLogService.getOrderLogListByOrderId(id); + return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, orderLogs, user, brokerageUser)); + } + + @GetMapping("/get-express-track-list") + @Operation(summary = "获得交易订å•的物æµè½¨è¿¹") + @Parameter(name = "id", description = "交易订å•ç¼–å·") + @PreAuthorize("@ss.hasPermission('trade:order:query')") + public CommonResult> getOrderExpressTrackList(@RequestParam("id") Long id) { + return success(TradeOrderConvert.INSTANCE.convertList02( + tradeOrderQueryService.getExpressTrackList(id))); + } + + @PutMapping("/delivery") + @Operation(summary = "订å•å‘è´§") + @PreAuthorize("@ss.hasPermission('trade:order:update')") + public CommonResult deliveryOrder(@RequestBody TradeOrderDeliveryReqVO deliveryReqVO) { + tradeOrderUpdateService.deliveryOrder(deliveryReqVO); + return success(true); + } + + @PutMapping("/update-remark") + @Operation(summary = "订å•备注") + @PreAuthorize("@ss.hasPermission('trade:order:update')") + public CommonResult updateOrderRemark(@RequestBody TradeOrderRemarkReqVO reqVO) { + tradeOrderUpdateService.updateOrderRemark(reqVO); + return success(true); + } + + @PutMapping("/update-price") + @Operation(summary = "订å•调价") + @PreAuthorize("@ss.hasPermission('trade:order:update')") + public CommonResult updateOrderPrice(@RequestBody TradeOrderUpdatePriceReqVO reqVO) { + tradeOrderUpdateService.updateOrderPrice(reqVO); + return success(true); + } + + @PutMapping("/update-address") + @Operation(summary = "ä¿®æ”¹è®¢å•æ”¶è´§åœ°å€") + @PreAuthorize("@ss.hasPermission('trade:order:update')") + public CommonResult updateOrderAddress(@RequestBody TradeOrderUpdateAddressReqVO reqVO) { + tradeOrderUpdateService.updateOrderAddress(reqVO); + return success(true); + } + + @PutMapping("/pick-up-by-id") + @Operation(summary = "è®¢å•æ ¸é”€") + @Parameter(name = "id", description = "交易订å•ç¼–å·") + @PreAuthorize("@ss.hasPermission('trade:order:pick-up')") + public CommonResult pickUpOrderById(@RequestParam("id") Long id) { + tradeOrderUpdateService.pickUpOrderByAdmin(getLoginUserId(), id); + return success(true); + } + + @PutMapping("/pick-up-by-verify-code") + @Operation(summary = "è®¢å•æ ¸é”€") + @Parameter(name = "pickUpVerifyCode", description = "è‡ªææ ¸é”€ç ") + @PreAuthorize("@ss.hasPermission('trade:order:pick-up')") + public CommonResult pickUpOrderByVerifyCode(@RequestParam("pickUpVerifyCode") String pickUpVerifyCode) { + tradeOrderUpdateService.pickUpOrderByAdmin(getLoginUserId(), pickUpVerifyCode); + return success(true); + } + + @GetMapping("/get-by-pick-up-verify-code") + @Operation(summary = "查询核销ç å¯¹åº”的订å•") + @Parameter(name = "pickUpVerifyCode", description = "è‡ªææ ¸é”€ç ") + @PreAuthorize("@ss.hasPermission('trade:order:query')") + public CommonResult getByPickUpVerifyCode(@RequestParam("pickUpVerifyCode") String pickUpVerifyCode) { + TradeOrderDO tradeOrder = tradeOrderUpdateService.getByPickUpVerifyCode(pickUpVerifyCode); + return success(TradeOrderConvert.INSTANCE.convert2(tradeOrder, null)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderBaseVO.java new file mode 100644 index 0000000..258aba9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderBaseVO.java @@ -0,0 +1,151 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * äº¤æ˜“è®¢å• Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class TradeOrderBaseVO { + + // ========== 订å•åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "è®¢å•æµæ°´å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1146347329394184195") + private String no; + + @Schema(description = "䏋啿—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "订å•类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "è®¢å•æ¥æº", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer terminal; + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long userId; + + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") + private String userIp; + + @Schema(description = "用户备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + private String userRemark; + + @Schema(description = "订å•状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "è´­ä¹°çš„å•†å“æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer productCount; + + @Schema(description = "订å•å®Œæˆæ—¶é—´") + private LocalDateTime finishTime; + + @Schema(description = "订å•å–æ¶ˆæ—¶é—´") + private LocalDateTime cancelTime; + + @Schema(description = "å–æ¶ˆç±»åž‹", example = "10") + private Integer cancelType; + + @Schema(description = "商家备注", example = "你猜一下") + private String remark; + + // ========== ä»·æ ¼ + æ”¯ä»˜åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long payOrderId; + + @Schema(description = "是å¦å·²æ”¯ä»˜", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean payStatus; + + @Schema(description = "付款时间") + private LocalDateTime payTime; + + @Schema(description = "支付渠é“", requiredMode = Schema.RequiredMode.REQUIRED, example = "wx_lite") + private String payChannelCode; + + @Schema(description = "商å“原价(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Integer totalPrice; + + @Schema(description = "订å•优惠(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer discountPrice; + + @Schema(description = "è¿è´¹é‡‘é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer deliveryPrice; + + @Schema(description = "订å•调价(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer adjustPrice; + + @Schema(description = "应付金é¢ï¼ˆæ€»ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Integer payPrice; + + // ========== æ”¶ä»¶ + 物æµåŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "é…逿–¹å¼", example = "10") + private Integer deliveryType; + + @Schema(description = "自æé—¨åº—", example = "10") + private Long pickUpStoreId; + + @Schema(description = "è‡ªææ ¸é”€ç ", example = "10") + private Long pickUpVerifyCode; + + @Schema(description = "é…逿¨¡æ¿ç¼–å·", example = "1024") + private Long deliveryTemplateId; + + @Schema(description = "å‘货物æµå…¬å¸ç¼–å·", example = "1024") + private Long logisticsId; + + @Schema(description = "å‘货物æµå•å·", example = "1024") + private String logisticsNo; + + @Schema(description = "å‘è´§æ—¶é—´") + private LocalDateTime deliveryTime; + + @Schema(description = "æ”¶è´§æ—¶é—´") + private LocalDateTime receiveTime; + + @Schema(description = "收件人åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + private String receiverName; + + @Schema(description = "收件人手机", requiredMode = Schema.RequiredMode.REQUIRED, example = "13800138000") + private String receiverMobile; + + @Schema(description = "收件人地区编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "110000") + private Integer receiverAreaId; + + @Schema(description = "收件人详细地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "中关æ‘大街 1 å·") + private String receiverDetailAddress; + + // ========== å”®åŽåŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "å”®åŽçжæ€", example = "1") + private Integer afterSaleStatus; + + @Schema(description = "退款金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer refundPrice; + + // ========== è¥é”€åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "优惠劵编å·", example = "1024") + private Long couponId; + + @Schema(description = "优惠劵å‡å…金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer couponPrice; + + @Schema(description = "积分抵扣的金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer pointPrice; + + @Schema(description = "VIP å‡å…金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") + private Integer vipPrice; + + @Schema(description = "推广人编å·", example = "1") + private Long brokerageUserId; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java new file mode 100644 index 0000000..e43b192 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java @@ -0,0 +1,23 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - 订å•å‘è´§ Request VO") +@Data +public class TradeOrderDeliveryReqVO { + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "å‘货物æµå…¬å¸ç¼–å·", example = "1") + @NotNull(message = "å‘货物æµå…¬å¸ä¸èƒ½ä¸ºç©º") + private Long logisticsId; + + @Schema(description = "å‘货物æµå•å·", example = "SF123456789") + private String logisticsNo; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java new file mode 100644 index 0000000..b913a73 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java @@ -0,0 +1,63 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import com.tashow.cloud.trade.controller.admin.base.member.user.MemberUserRespVO; +import com.tashow.cloud.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管ç†åŽå° - 交易订å•的详情 Response VO") +@Data +public class TradeOrderDetailRespVO extends TradeOrderBaseVO { + + /** + * 订å•项列表 + */ + private List items; + + /** + * 下å•ç”¨æˆ·ä¿¡æ¯ + */ + private MemberUserRespVO user; + /** + * æŽ¨å¹¿ç”¨æˆ·ä¿¡æ¯ + */ + private MemberUserRespVO brokerageUser; + + /** + * æ“作日志列表 + */ + private List logs; + + @Schema(description = "收件人地区åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海 上海市 普陀区") + private String receiverAreaName; + + @Schema(description = "管ç†åŽå° - 交易订å•çš„æ“作日志") + @Data + public static class OrderLog { + + @Schema(description = "æ“作详情", requiredMode = Schema.RequiredMode.REQUIRED, example = "订å•å‘è´§") + private String content; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2023-06-01 10:50:20") + private LocalDateTime createTime; + + @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer userType; + + } + + @Schema(description = "管ç†åŽå° - 交易订å•的详情的订å•项目") + @Data + public static class Item extends TradeOrderItemBaseVO { + + /** + * 属性数组 + */ + private List properties; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderItemBaseVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderItemBaseVO.java new file mode 100644 index 0000000..6742d75 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderItemBaseVO.java @@ -0,0 +1,67 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 交易订å•项 Base VO,æä¾›ç»™æ·»åŠ ã€ä¿®æ”¹ã€è¯¦ç»†çš„å­ VO 使用 + * å¦‚æžœå­ VO 存在差异的字段,请ä¸è¦æ·»åŠ åˆ°è¿™é‡Œï¼Œå½±å“ Swagger æ–‡æ¡£ç”Ÿæˆ + */ +@Data +public class TradeOrderItemBaseVO { + + // ========== 订å•é¡¹åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long userId; + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long orderId; + + // ========== 商å“åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "å•†å“ SPU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long spuId; + + @Schema(description = "å•†å“ SPU åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "èŠ‹é“æºç ") + private String spuName; + + @Schema(description = "å•†å“ SKU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long skuId; + + @Schema(description = "商å“图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + private String picUrl; + + @Schema(description = "è´­ä¹°æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer count; + + // ========== ä»·æ ¼ + æ”¯ä»˜åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "商å“原价(å•)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer price; + + @Schema(description = "商å“优惠(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer discountPrice; + + @Schema(description = "商å“实付金é¢ï¼ˆæ€»ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer payPrice; + + @Schema(description = "å­è®¢å•分摊金é¢ï¼ˆæ€»ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer orderPartPrice; + + @Schema(description = "分摊åŽå­è®¢å•实付金é¢ï¼ˆæ€»ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer orderDividePrice; + + // ========== è¥é”€åŸºæœ¬ä¿¡æ¯ ========== + + // TODO èŠ‹è‰¿ï¼šåœ¨æ‰æ‘¸ä¸€ä¸‹ + + // ========== å”®åŽåŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "å”®åŽçжæ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer afterSaleStatus; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderPageItemRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderPageItemRespVO.java new file mode 100644 index 0000000..7036a8e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderPageItemRespVO.java @@ -0,0 +1,35 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import com.tashow.cloud.trade.controller.admin.base.member.user.MemberUserRespVO; +import com.tashow.cloud.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管ç†åŽå° - 交易订å•的分页项 Response VO") +@Data +public class TradeOrderPageItemRespVO extends TradeOrderBaseVO { + + @Schema(description = "收件人地区åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海 上海市 普陀区") + private String receiverAreaName; + + @Schema(description = "订å•项列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "用户信æ¯", requiredMode = Schema.RequiredMode.REQUIRED) + private MemberUserRespVO user; + + @Schema(description = "推广人信æ¯") + private MemberUserRespVO brokerageUser; + + @Schema(description = "管ç†åŽå° - 交易订å•的分页项的订å•项目") + @Data + public static class Item extends TradeOrderItemBaseVO { + + @Schema(description = "属性列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List properties; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderPageReqVO.java new file mode 100644 index 0000000..006b756 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderPageReqVO.java @@ -0,0 +1,64 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管ç†åŽå° - 交易订å•的分页 Request VO") +@Data +public class TradeOrderPageReqVO extends PageParam { + + @Schema(description = "订å•å·", example = "88888888") + private String no; + + @Schema(description = "用户编å·", example = "1024") + private Long userId; + + @Schema(description = "用户昵称", example = "å°çŽ‹") + private String userNickname; + + @Schema(description = "用户手机å·", example = "å°çŽ‹") + @Mobile + private String userMobile; + + @Schema(description = "é…逿–¹å¼", example = "1") + private Integer deliveryType; + + @Schema(description = "å‘货物æµå…¬å¸ç¼–å·", example = "1") + private Long logisticsId; + + @Schema(description = "自æé—¨åº—ç¼–å·", example = "[1,2]") + private List pickUpStoreIds; + + @Schema(description = "è‡ªææ ¸é”€ç ", example = "12345678") + private String pickUpVerifyCode; + + @Schema(description = "订å•类型", example = "1") + private Integer type; + + @Schema(description = "订å•状æ€", example = "1") + @InEnum(value = TradeOrderStatusEnum.class, message = "订å•状æ€å¿…须是 {value}") + private Integer status; + + @Schema(description = "支付渠é“", example = "wx_lite") + private String payChannelCode; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "è®¢å•æ¥æº", example = "10") + @InEnum(value = TerminalEnum.class, message = "è®¢å•æ¥æº {value}") + private Integer terminal; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderRemarkReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderRemarkReqVO.java new file mode 100644 index 0000000..afdbe74 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderRemarkReqVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - 订å•备注 Request VO") +@Data +public class TradeOrderRemarkReqVO { + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "商家备注", example = "你猜一下") + @NotEmpty(message = "订å•备注ä¸èƒ½ä¸ºç©º") + private String remark; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderSummaryRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderSummaryRespVO.java new file mode 100644 index 0000000..c1b197e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderSummaryRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管ç†åŽå° - 交易订å•统计 Response VO") +@Data +public class TradeOrderSummaryRespVO { + + @Schema(description = "è®¢å•æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderCount; + + @Schema(description = "订å•金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderPayPrice; + + @Schema(description = "é€€æ¬¾å•æ•°", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long afterSaleCount; + + @Schema(description = "退款金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long afterSalePrice; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderUpdateAddressReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderUpdateAddressReqVO.java new file mode 100644 index 0000000..529f3a6 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderUpdateAddressReqVO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - 订å•ä¿®æ”¹åœ°å€ Request VO") +@Data +public class TradeOrderUpdateAddressReqVO { + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "收件人åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "z张三") + @NotEmpty(message = "收件人åç§°ä¸èƒ½ä¸ºç©º") + private String receiverName; + + @Schema(description = "收件人手机", requiredMode = Schema.RequiredMode.REQUIRED, example = "19988188888") + @NotEmpty(message = "收件人手机ä¸èƒ½ä¸ºç©º") + private String receiverMobile; + + @Schema(description = "收件人地区编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "7310") + @NotNull(message = "收件人地区编å·ä¸èƒ½ä¸ºç©º") + private Integer receiverAreaId; + + @Schema(description = "收件人详细地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "昆明市五åŽåŒºxxxå°åŒºxxx") + @NotEmpty(message = "收件人详细地å€ä¸èƒ½ä¸ºç©º") + private String receiverDetailAddress; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderUpdatePriceReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderUpdatePriceReqVO.java new file mode 100644 index 0000000..810ec78 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/admin/order/vo/TradeOrderUpdatePriceReqVO.java @@ -0,0 +1,20 @@ +package com.tashow.cloud.trade.controller.admin.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管ç†åŽå° - è®¢å•æ”¹ä»· Request VO") +@Data +public class TradeOrderUpdatePriceReqVO { + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "订å•调价,å•ä½ï¼šåˆ†ã€‚正数,加价;负数,å‡ä»·", requiredMode = Schema.RequiredMode.REQUIRED, example = "-100") + @NotNull(message = "订å•调价价格ä¸èƒ½ä¸ºç©º") + private Integer adjustPrice; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/AppAfterSaleController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/AppAfterSaleController.java new file mode 100644 index 0000000..7247828 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/AppAfterSaleController.java @@ -0,0 +1,67 @@ +package com.tashow.cloud.trade.controller.app.aftersale; + +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 com.tashow.cloud.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; +import com.tashow.cloud.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO; +import com.tashow.cloud.trade.controller.app.aftersale.vo.AppAfterSaleRespVO; +import com.tashow.cloud.trade.convert.aftersale.AfterSaleConvert; +import com.tashow.cloud.trade.service.aftersale.AfterSaleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 App - 交易售åŽ") +@RestController +@RequestMapping("/trade/after-sale") +@Validated +@Slf4j +public class AppAfterSaleController { + + @Resource + private AfterSaleService afterSaleService; + + @GetMapping(value = "/page") + @Operation(summary = "获得售åŽåˆ†é¡µ") + public CommonResult> getAfterSalePage(PageParam pageParam) { + return success(AfterSaleConvert.INSTANCE.convertPage02( + afterSaleService.getAfterSalePage(getLoginUserId(), pageParam))); + } + + @GetMapping(value = "/get") + @Operation(summary = "获得售åŽè®¢å•") + @Parameter(name = "id", description = "å”®åŽç¼–å·", required = true, example = "1") + public CommonResult getAfterSale(@RequestParam("id") Long id) { + return success(AfterSaleConvert.INSTANCE.convert(afterSaleService.getAfterSale(getLoginUserId(), id))); + } + + @PostMapping(value = "/create") + @Operation(summary = "申请售åŽ") + public CommonResult createAfterSale(@RequestBody AppAfterSaleCreateReqVO createReqVO) { + return success(afterSaleService.createAfterSale(getLoginUserId(), createReqVO)); + } + + @PutMapping(value = "/delivery") + @Operation(summary = "退回货物") + public CommonResult deliveryAfterSale(@RequestBody AppAfterSaleDeliveryReqVO deliveryReqVO) { + afterSaleService.deliveryAfterSale(getLoginUserId(), deliveryReqVO); + return success(true); + } + + @DeleteMapping(value = "/cancel") + @Operation(summary = "å–æ¶ˆå”®åŽ") + @Parameter(name = "id", description = "å”®åŽç¼–å·", required = true, example = "1") + public CommonResult cancelAfterSale(@RequestParam("id") Long id) { + afterSaleService.cancelAfterSale(getLoginUserId(), id); + return success(true); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/AppAfterSaleLogController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/AppAfterSaleLogController.java new file mode 100644 index 0000000..0bdb933 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/AppAfterSaleLogController.java @@ -0,0 +1,42 @@ +package com.tashow.cloud.trade.controller.app.aftersale; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.tashow.cloud.trade.controller.app.aftersale.vo.log.AppAfterSaleLogRespVO; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleLogDO; +import com.tashow.cloud.trade.service.aftersale.AfterSaleLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +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 java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "用户 App - å”®åŽæ—¥å¿—") +@RestController +@RequestMapping("/trade/after-sale-log") +@Validated +@Slf4j +public class AppAfterSaleLogController { + + @Resource + private AfterSaleLogService afterSaleLogService; + + @GetMapping("/list") + @Operation(summary = "èŽ·å¾—å”®åŽæ—¥å¿—列表") + @Parameter(name = "afterSaleId", description = "å”®åŽç¼–å·", required = true, example = "1") + public CommonResult> getAfterSaleLogList( + @RequestParam("afterSaleId") Long afterSaleId) { + List logs = afterSaleLogService.getAfterSaleLogList(afterSaleId); + return success(BeanUtils.toBean(logs, AppAfterSaleLogRespVO.class)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleCreateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleCreateReqVO.java new file mode 100644 index 0000000..b96b24f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleCreateReqVO.java @@ -0,0 +1,40 @@ +package com.tashow.cloud.trade.controller.app.aftersale.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@Schema(description = "用户 App - 交易售åŽåˆ›å»º Request VO") +@Data +public class AppAfterSaleCreateReqVO { + + @Schema(description = "订å•项编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "订å•项编å·ä¸èƒ½ä¸ºç©º") + private Long orderItemId; + + @Schema(description = "å”®åŽæ–¹å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "å”®åŽæ–¹å¼ä¸èƒ½ä¸ºç©º") + @InEnum(value = AfterSaleWayEnum.class, message = "å”®åŽæ–¹å¼å¿…须是 {value}") + private Integer way; + + @Schema(description = "退款金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "退款金é¢ä¸èƒ½ä¸ºç©º") + @Min(value = 1, message = "退款金é¢å¿…须大于 0") + private Integer refundPrice; + + @Schema(description = "申请原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "申请原因ä¸èƒ½ä¸ºç©º") + private String applyReason; + + @Schema(description = "补充æè¿°", example = "商å“è´¨é‡ä¸å¥½") + private String applyDescription; + + @Schema(description = "补充凭è¯å›¾ç‰‡", example = "https://www.iocoder.cn/1.png, https://www.iocoder.cn/2.png") + private List applyPicUrls; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleDeliveryReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleDeliveryReqVO.java new file mode 100644 index 0000000..9c4bef8 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleDeliveryReqVO.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.trade.controller.app.aftersale.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "用户 App - 交易售åŽé€€å›žè´§ç‰© Request VO") +@Data +public class AppAfterSaleDeliveryReqVO { + + @Schema(description = "å”®åŽç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "å”®åŽç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "退货物æµå…¬å¸ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "退货物æµå…¬å¸ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long logisticsId; + + @Schema(description = "退货物æµå•å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "SF123456789") + @NotNull(message = "退货物æµå•å·ä¸èƒ½ä¸ºç©º") + private String logisticsNo; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleRespVO.java new file mode 100644 index 0000000..e67d033 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/AppAfterSaleRespVO.java @@ -0,0 +1,109 @@ +package com.tashow.cloud.trade.controller.app.aftersale.vo; + +import com.tashow.cloud.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "用户 App - äº¤æ˜“å”®åŽ Response VO") +@Data +public class AppAfterSaleRespVO { + + @Schema(description = "å”®åŽç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "å”®åŽæµæ°´å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1146347329394184195") + private String no; + + @Schema(description = "å”®åŽçжæ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "å”®åŽæ–¹å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer way; + + @Schema(description = "å”®åŽç±»åž‹", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "申请原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String applyReason; + + @Schema(description = "补充æè¿°", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String applyDescription; + + @Schema(description = "补充凭è¯å›¾ç‰‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private List applyPicUrls; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "æ›´æ–°æ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + // ========== 交易订å•相关 ========== + + @Schema(description = "交易订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long orderId; + + @Schema(description = "äº¤æ˜“è®¢å•æµæ°´å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String orderNo; + + @Schema(description = "交易订å•项编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long orderItemId; + + @Schema(description = "å•†å“ SPU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long spuId; + + @Schema(description = "å•†å“ SPU åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String spuName; + + @Schema(description = "å•†å“ SKU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long skuId; + + /** + * 属性数组 + */ + private List properties; + + @Schema(description = "商å“图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/01.jpg") + private String picUrl; + + @Schema(description = "é€€è´§å•†å“æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer count; + + // ========== 审批相关 ========== + + /** + * 审批备注 + * + * 注æ„ï¼Œåªæœ‰å®¡æ‰¹ä¸é€šè¿‡æ‰ä¼šå¡«å†™ + */ + private String auditReason; + + // ========== 退款相关 ========== + + @Schema(description = "退款金é¢ï¼Œå•ä½ï¼šåˆ†", example = "100") + private Integer refundPrice; + + @Schema(description = "退款时间") + private LocalDateTime refundTime; + + // ========== 退货相关 ========== + + @Schema(description = "退货物æµå…¬å¸ç¼–å·", example = "1") + private Long logisticsId; + + @Schema(description = "退货物æµå•å·", example = "SF123456789") + private String logisticsNo; + + @Schema(description = "退货时间") + private LocalDateTime deliveryTime; + + @Schema(description = "æ”¶è´§æ—¶é—´") + private LocalDateTime receiveTime; + + @Schema(description = "收货备注") + private String receiveReason; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/log/AppAfterSaleLogRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/log/AppAfterSaleLogRespVO.java new file mode 100644 index 0000000..349bd23 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/aftersale/vo/log/AppAfterSaleLogRespVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.trade.controller.app.aftersale.vo.log; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管ç†åŽå° - App äº¤æ˜“å”®åŽæ—¥å¿— Response VO") +@Data +public class AppAfterSaleLogRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "20669") + private Long id; + + @Schema(description = "æ“作明细", requiredMode = Schema.RequiredMode.REQUIRED, example = "ç»´æƒå®Œæˆï¼Œé€€æ¬¾é‡‘é¢ï¼šÂ¥37776.00") + private String content; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/package-info.java new file mode 100644 index 0000000..efe3170 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/package-info.java @@ -0,0 +1,4 @@ +/** + * 基础包,放一些通用的 VO ç±» + */ +package com.tashow.cloud.trade.controller.app.base; diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/property/AppProductPropertyValueDetailRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/property/AppProductPropertyValueDetailRespVO.java new file mode 100644 index 0000000..65e5ab8 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/property/AppProductPropertyValueDetailRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.app.base.property; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 商å“属性值的明细 Response VO") +@Data +public class AppProductPropertyValueDetailRespVO { + + @Schema(description = "属性的编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long propertyId; + + @Schema(description = "属性的åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "颜色") + private String propertyName; + + @Schema(description = "属性值的编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long valueId; + + @Schema(description = "属性值的åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "红色") + private String valueName; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java new file mode 100644 index 0000000..2085a00 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java @@ -0,0 +1,34 @@ +package com.tashow.cloud.trade.controller.app.base.sku; + +import com.tashow.cloud.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * å•†å“ SKU 基础 Response VO + * + * @author èŠ‹é“æºç  + */ +@Data +public class AppProductSkuBaseRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "图片地å€", example = "https://www.iocoder.cn/xx.png") + private String picUrl; + + @Schema(description = "销售价格,å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer price; + + @Schema(description = "库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer stock; + + /** + * 属性数组 + */ + private List properties; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java new file mode 100644 index 0000000..b9c98c5 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.trade.controller.app.base.spu; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * å•†å“ SPU 基础 Response VO + * + * @author èŠ‹é“æºç  + */ +@Data +public class AppProductSpuBaseRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "å•†å“ SPU åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋é“") + private String name; + + @Schema(description = "商å“主图地å€", example = "https://www.iocoder.cn/xx.png") + private String picUrl; + + @Schema(description = "商å“分类编å·", example = "1") + private Long categoryId; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageRecordController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageRecordController.java new file mode 100644 index 0000000..2ca90c7 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageRecordController.java @@ -0,0 +1,51 @@ +package com.tashow.cloud.trade.controller.app.brokerage; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import com.tashow.cloud.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO; +import com.tashow.cloud.trade.convert.brokerage.BrokerageRecordConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageRecordDO; +import com.tashow.cloud.trade.service.brokerage.BrokerageRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +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 jakarta.annotation.Resource; +import jakarta.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 APP - 分销用户") +@RestController +@RequestMapping("/trade/brokerage-record") +@Validated +@Slf4j +public class AppBrokerageRecordController { + + @Resource + private BrokerageRecordService brokerageRecordService; + + @GetMapping("/page") + @Operation(summary = "获得分销记录分页") + public CommonResult> getBrokerageRecordPage(@Valid AppBrokerageRecordPageReqVO pageReqVO) { + PageResult pageResult = brokerageRecordService.getBrokerageRecordPage( + BrokerageRecordConvert.INSTANCE.convert(pageReqVO, getLoginUserId())); + return success(BeanUtils.toBean(pageResult, AppBrokerageRecordRespVO.class)); + } + + @GetMapping("/get-product-brokerage-price") + @Operation(summary = "获得商å“的分销金é¢") + public CommonResult getProductBrokeragePrice(@RequestParam("spuId") Long spuId) { + return success(brokerageRecordService.calculateProductBrokeragePrice(getLoginUserId(), spuId)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageUserController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageUserController.java new file mode 100644 index 0000000..d4de603 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageUserController.java @@ -0,0 +1,136 @@ +package com.tashow.cloud.trade.controller.app.brokerage; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.*; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.*; +import com.tashow.cloud.trade.convert.brokerage.BrokerageRecordConvert; +import com.tashow.cloud.trade.convert.brokerage.BrokerageUserConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; +import com.tashow.cloud.trade.service.brokerage.BrokerageRecordService; +import com.tashow.cloud.trade.service.brokerage.BrokerageUserService; +import com.tashow.cloud.trade.service.brokerage.BrokerageWithdrawService; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static java.util.Arrays.asList; + +@Tag(name = "用户 APP - 分销用户") +@RestController +@RequestMapping("/trade/brokerage-user") +@Validated +@Slf4j +public class AppBrokerageUserController { + + @Resource + private BrokerageUserService brokerageUserService; + @Resource + private BrokerageRecordService brokerageRecordService; + @Resource + private BrokerageWithdrawService brokerageWithdrawService; + @Resource + private MemberUserApi memberUserApi; + + @GetMapping("/get") + @Operation(summary = "获得个人分销信æ¯") + public CommonResult getBrokerageUser() { + Optional user = Optional.ofNullable(brokerageUserService.getOrCreateBrokerageUser(getLoginUserId())); + // è¿”å›žæ•°æ® + AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO() + .setBrokerageEnabled(user.map(BrokerageUserDO::getBrokerageEnabled).orElse(false)) + .setBrokeragePrice(user.map(BrokerageUserDO::getBrokeragePrice).orElse(0)) + .setFrozenPrice(user.map(BrokerageUserDO::getFrozenPrice).orElse(0)); + return success(respVO); + } + + @PutMapping("/bind") + @Operation(summary = "绑定推广员") + public CommonResult bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) { + return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId())); + } + + @GetMapping("/get-summary") + @Operation(summary = "获得个人分销统计") + public CommonResult getBrokerageUserSummary() { + // 查询当å‰ç™»å½•ç”¨æˆ·ä¿¡æ¯ + Long userId = getLoginUserId(); + BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(userId); + // 统计用户昨日的佣金 + LocalDateTime yesterday = LocalDateTime.now().minusDays(1); + LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday); + LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday); + Integer yesterdayPrice = brokerageRecordService.getSummaryPriceByUserId(userId, + BrokerageRecordBizTypeEnum.ORDER, BrokerageRecordStatusEnum.SETTLEMENT, beginTime, endTime); + // 统计用户æçŽ°çš„ä½£é‡‘ + Integer withdrawPrice = brokerageWithdrawService.getWithdrawSummaryListByUserId(Collections.singleton(userId), + asList(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS)).stream() + .findFirst().map(BrokerageWithdrawSummaryRespBO::getPrice).orElse(0); + // 统计分销用户数é‡ï¼ˆä¸€çº§ï¼‰ + Long firstBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(userId, 1); + // 统计分销用户数é‡ï¼ˆäºŒçº§ï¼‰ + Long secondBrokerageUserCount = brokerageUserService.getBrokerageUserCountByBindUserId(userId, 2); + + // 拼接返回 + return success(BrokerageUserConvert.INSTANCE.convert(yesterdayPrice, withdrawPrice, firstBrokerageUserCount, secondBrokerageUserCount, brokerageUser)); + } + + @GetMapping("/rank-page-by-user-count") + @Operation(summary = "获得分销用户排行分页(基于用户é‡ï¼‰") + public CommonResult> getBrokerageUserRankPageByUserCount(AppBrokerageUserRankPageReqVO pageReqVO) { + // 分页查询 + PageResult pageResult = brokerageUserService.getBrokerageUserRankPageByUserCount(pageReqVO); + // æ‹¼æŽ¥æ•°æ® + Map userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), AppBrokerageUserRankByUserCountRespVO::getId)); + return success(BrokerageUserConvert.INSTANCE.convertPage03(pageResult, userMap)); + } + + @GetMapping("/rank-page-by-price") + @Operation(summary = "获得分销用户排行分页(基于佣金)") + public CommonResult> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) { + // 分页查询 + PageResult pageResult = brokerageRecordService.getBrokerageUserChildSummaryPageByPrice(pageReqVO); + // æ‹¼æŽ¥æ•°æ® + Map userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), AppBrokerageUserRankByPriceRespVO::getId)); + return success(BrokerageRecordConvert.INSTANCE.convertPage03(pageResult, userMap)); + } + + @GetMapping("/child-summary-page") + @Operation(summary = "获得下级分销统计分页") + public CommonResult> getBrokerageUserChildSummaryPage( + AppBrokerageUserChildSummaryPageReqVO pageReqVO) { + PageResult pageResult = brokerageUserService.getBrokerageUserChildSummaryPage(pageReqVO, getLoginUserId()); + return success(pageResult); + } + + @GetMapping("/get-rank-by-price") + @Operation(summary = "获得分销用户排行(基于佣金)") + @Parameter(name = "times", description = "时间段", required = true) + public CommonResult getRankByPrice( + @RequestParam("times") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] times) { + return success(brokerageRecordService.getUserRankByPrice(getLoginUserId(), times)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageWithdrawController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageWithdrawController.java new file mode 100644 index 0000000..ef75590 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/AppBrokerageWithdrawController.java @@ -0,0 +1,47 @@ +package com.tashow.cloud.trade.controller.app.brokerage; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawRespVO; +import com.tashow.cloud.trade.convert.brokerage.BrokerageWithdrawConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; +import com.tashow.cloud.trade.service.brokerage.BrokerageWithdrawService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 APP - 分销æçް") +@RestController +@RequestMapping("/trade/brokerage-withdraw") +@Validated +@Slf4j +public class AppBrokerageWithdrawController { + + @Resource + private BrokerageWithdrawService brokerageWithdrawService; + + @GetMapping("/page") + @Operation(summary = "获得分销æçŽ°åˆ†é¡µ") + public CommonResult> getBrokerageWithdrawPage(AppBrokerageWithdrawPageReqVO pageReqVO) { + PageResult pageResult = brokerageWithdrawService.getBrokerageWithdrawPage( + BrokerageWithdrawConvert.INSTANCE.convert(pageReqVO, getLoginUserId())); + return success(BrokerageWithdrawConvert.INSTANCE.convertPage03(pageResult)); + } + + @PostMapping("/create") + @Operation(summary = "创建分销æçް") + public CommonResult createBrokerageWithdraw(@RequestBody @Valid AppBrokerageWithdrawCreateReqVO createReqVO) { + return success(brokerageWithdrawService.createBrokerageWithdraw(getLoginUserId(), createReqVO)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java new file mode 100644 index 0000000..8a85851 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.record; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 商å“çš„åˆ†é”€é‡‘é¢ Response VO") +@Data +public class AppBrokerageProductPriceRespVO { + + @Schema(description = "是å¦å¼€å¯", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Boolean enabled; + + @Schema(description = "分销最å°é‡‘é¢ï¼Œå•ä½ï¼šåˆ†", example = "100") + private Integer brokerageMinPrice; + + @Schema(description = "分销最大金é¢ï¼Œå•ä½ï¼šåˆ†", example = "100") + private Integer brokerageMaxPrice; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageRecordPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageRecordPageReqVO.java new file mode 100644 index 0000000..9846bcb --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageRecordPageReqVO.java @@ -0,0 +1,31 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.record; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +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 = "应用 App - 分销记录分页 Request VO") +@Data +public class AppBrokerageRecordPageReqVO extends PageParam { + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(value = BrokerageRecordBizTypeEnum.class, message = "业务类型必须是 {value}") + private Integer bizType; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(value = BrokerageRecordStatusEnum.class, message = "状æ€å¿…须是 {value}") + private Integer status; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageRecordRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageRecordRespVO.java new file mode 100644 index 0000000..33c71a9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/record/AppBrokerageRecordRespVO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.record; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 App - 分销记录 Response VO") +@Data +public class AppBrokerageRecordRespVO { + + @Schema(description = "记录编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Long id; + + @Schema(description = "业务编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private String bizId; + + @Schema(description = "分销标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "用户下å•") + private String title; + + @Schema(description = "分销金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Integer price; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "å®Œæˆæ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime finishTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserBindReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserBindReqVO.java new file mode 100644 index 0000000..732d15c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserBindReqVO.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "应用 App - 绑定推广员 Request VO") +@Data +public class AppBrokerageUserBindReqVO { + + @Schema(description = "推广员编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "推广员编å·ä¸èƒ½ä¸ºç©º") + private Long bindUserId; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryPageReqVO.java new file mode 100644 index 0000000..7e24edb --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryPageReqVO.java @@ -0,0 +1,30 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.user; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.SortingField; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "用户 App - 下级分销统计分页 Request VO") +@Data +public class AppBrokerageUserChildSummaryPageReqVO extends PageParam { + + public static final String SORT_FIELD_USER_COUNT = "userCount"; + public static final String SORT_FIELD_ORDER_COUNT = "orderCount"; + public static final String SORT_FIELD_PRICE = "price"; + + @Schema(description = "用户昵称", example = "æŽ") // æ¨¡ç³ŠåŒ¹é… + private String nickname; + + @Schema(description = "排åºå­—段", example = "userCount") + private SortingField sortingField; + + @Schema(description = "下级的级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 1 - 直接下级;2 - 间接下级 + @NotNull(message = "下级的级别ä¸èƒ½ä¸ºç©º") + @Range(min = 1, max = 2, message = "下级的级别åªèƒ½æ˜¯ {min} 或者 {max}") + private Integer level; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java new file mode 100644 index 0000000..ede169c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 App - 下级分销统计 Response VO") +@Data +public class AppBrokerageUserChildSummaryRespVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Long id; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "å°çŽ‹") + private String nickname; + + @Schema(description = "用户头åƒ", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg") + private String avatar; + + @Schema(description = "佣金金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer brokeragePrice; + + @Schema(description = "åˆ†é”€è®¢å•æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + private Integer brokerageOrderCount; + + @Schema(description = "分销用户数é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "30") + private Integer brokerageUserCount; + + @Schema(description = "绑定推广员的时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime brokerageTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java new file mode 100644 index 0000000..1798d4f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 个人分销统计 Response VO") +@Data +public class AppBrokerageUserMySummaryRespVO { + + @Schema(description = "昨天的佣金,å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer yesterdayPrice; + + @Schema(description = "æçŽ°çš„ä½£é‡‘ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Integer withdrawPrice; + + @Schema(description = "å¯ç”¨çš„佣金,å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408") + private Integer brokeragePrice; + + @Schema(description = "冻结的佣金,å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "234") + private Integer frozenPrice; + + @Schema(description = "分销用户数é‡ï¼ˆä¸€çº§ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Long firstBrokerageUserCount; + + @Schema(description = "分销用户数é‡ï¼ˆäºŒçº§ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Long secondBrokerageUserCount; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java new file mode 100644 index 0000000..67239c9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 分销排行用户(基于用户é‡ï¼‰ Response VO") +@Data +public class AppBrokerageUserRankByPriceRespVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Long id; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "å°çŽ‹") + private String nickname; + + @Schema(description = "用户头åƒ", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg") + private String avatar; + + @Schema(description = "佣金金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer brokeragePrice; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByUserCountRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByUserCountRespVO.java new file mode 100644 index 0000000..a7a1972 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByUserCountRespVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 分销排行用户(基于用户é‡ï¼‰ Response VO") +@Data +public class AppBrokerageUserRankByUserCountRespVO { + + @Schema(description = "用户编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Long id; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "å°çŽ‹") + private String nickname; + + @Schema(description = "用户头åƒ", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg") + private String avatar; + + @Schema(description = "邀请用户数é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer brokerageUserCount; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankPageReqVO.java new file mode 100644 index 0000000..ba7c384 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankPageReqVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.user; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import jakarta.validation.constraints.NotEmpty; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "应用 App - 分销用户排行 Request VO") +@Data +public class AppBrokerageUserRankPageReqVO extends PageParam { + + @Schema(description = "开始 + ç»“æŸæ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @NotEmpty(message = "æ—¶é—´ä¸èƒ½ä¸ºç©º") + private LocalDateTime[] times; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java new file mode 100644 index 0000000..b8e869e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - åˆ†é”€ç”¨æˆ·ä¿¡æ¯ Response VO") +@Data +public class AppBrokerageUserRespVO { + + @Schema(description = "æ˜¯å¦æœ‰åˆ†é”€èµ„æ ¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean brokerageEnabled; + + @Schema(description = "å¯ç”¨çš„佣金,å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408") + private Integer brokeragePrice; + + @Schema(description = "冻结的佣金,å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "234") + private Integer frozenPrice; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java new file mode 100644 index 0000000..b471e0b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java @@ -0,0 +1,75 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw; + +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.Validator; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.PositiveOrZero; + +@Schema(description = "用户 App - 分销æçŽ°åˆ›å»º Request VO") +@Data +public class AppBrokerageWithdrawCreateReqVO { + + @Schema(description = "æçŽ°æ–¹å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(value = BrokerageWithdrawTypeEnum.class, message = "æçŽ°æ–¹å¼å¿…须是 {value}") + private Integer type; + + @Schema(description = "æçް金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + @PositiveOrZero(message = "æçް金é¢ä¸èƒ½å°äºŽ 0") + @NotNull(message = "æçް金é¢ä¸èƒ½ä¸ºç©º") + private Integer price; + + // ========== 银行å¡ã€å¾®ä¿¡ã€æ”¯ä»˜å® æçŽ°ç›¸å…³å­—æ®µ ========== + + @Schema(description = "æçŽ°è´¦å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456789") + @NotBlank(message = "æçŽ°è´¦å·ä¸èƒ½ä¸ºç©º", groups = {Bank.class, Wechat.class, Alipay.class}) + private String accountNo; + + // ========== å¾®ä¿¡ã€æ”¯ä»˜å® æçŽ°ç›¸å…³å­—æ®µ ========== + + @Schema(description = "收款ç çš„图片", example = "https://www.iocoder.cn/1.png") + @URL(message = "收款ç çš„图片,必须是一个 URL") + private String accountQrCodeUrl; + + // ========== é“¶è¡Œå¡ æçŽ°ç›¸å…³å­—æ®µ ========== + + @Schema(description = "æŒå¡äººå§“å", example = "张三") + @NotBlank(message = "æŒå¡äººå§“åä¸èƒ½ä¸ºç©º", groups = {Bank.class}) + private String name; + @Schema(description = "æçŽ°é“¶è¡Œ", example = "1") + @NotNull(message = "æçŽ°é“¶è¡Œä¸èƒ½ä¸ºç©º", groups = {Bank.class}) + private String bankName; + @Schema(description = "开户地å€", example = "海淀支行") + private String bankAddress; + + public interface Wallet { + } + + public interface Bank { + } + + public interface Wechat { + } + + public interface Alipay { + } + + public void validate(Validator validator) { + if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(type)) { + ValidationUtils.validate(validator, this, Wallet.class); + } else if (BrokerageWithdrawTypeEnum.BANK.getType().equals(type)) { + ValidationUtils.validate(validator, this, Bank.class); + } else if (BrokerageWithdrawTypeEnum.WECHAT.getType().equals(type)) { + ValidationUtils.validate(validator, this, Wechat.class); + } else if (BrokerageWithdrawTypeEnum.ALIPAY.getType().equals(type)) { + ValidationUtils.validate(validator, this, Alipay.class); + } + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawPageReqVO.java new file mode 100644 index 0000000..117aa83 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawPageReqVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "应用 App - 分销æçŽ°åˆ†é¡µ Request VO") +@Data +public class AppBrokerageWithdrawPageReqVO extends PageParam { + + @Schema(description = "类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(value = BrokerageWithdrawTypeEnum.class, message = "类型必须是 {value}") + private Integer type; + + @Schema(description = "状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(value = BrokerageWithdrawStatusEnum.class, message = "状æ€å¿…须是 {value}") + private Integer status; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawRespVO.java new file mode 100644 index 0000000..3705ee9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawRespVO.java @@ -0,0 +1,27 @@ +package com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "用户 App - 分销æçް Response VO") +@Data +public class AppBrokerageWithdrawRespVO { + + @Schema(description = "æçŽ°ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Long id; + + @Schema(description = "æçŽ°çŠ¶æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer status; + + @Schema(description = "æçŽ°çŠ¶æ€å", requiredMode = Schema.RequiredMode.REQUIRED, example = "审核中") + private String statusName; + + @Schema(description = "æçް金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Integer price; + + @Schema(description = "æçŽ°æ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/AppCartController.http b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/AppCartController.http new file mode 100644 index 0000000..2eda3c2 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/AppCartController.http @@ -0,0 +1,42 @@ +### 请求 /trade/cart/add æŽ¥å£ => æˆåŠŸ +POST {{appApi}}/trade/cart/add +tenant-id: {{appTenantId}} +Authorization: Bearer {{appToken}} +Content-Type: application/json + +{ + "skuId": 1, + "count": 10, + "addStatus": true +} + +### 请求 /trade/cart/update æŽ¥å£ => æˆåŠŸ +PUT {{appApi}}/trade/cart/update +tenant-id: {{appTenantId}} +Authorization: Bearer {{appToken}} +Content-Type: application/json + +{ + "id": 35, + "count": 5 +} + +### 请求 /trade/cart/delete æŽ¥å£ => æˆåŠŸ +DELETE {{appApi}}/trade/cart/delete?ids=1 +tenant-id: {{appTenantId}} +Authorization: Bearer {{appToken}} + +### 请求 /trade/cart/get-count æŽ¥å£ => æˆåŠŸ +GET {{appApi}}/trade/cart/get-count +tenant-id: {{appTenantId}} +Authorization: Bearer {{appToken}} + +### 请求 /trade/cart/get-count-map æŽ¥å£ => æˆåŠŸ +GET {{appApi}}/trade/cart/get-count-map +tenant-id: {{appTenantId}} +Authorization: Bearer {{appToken}} + +### 请求 /trade/cart/list æŽ¥å£ => æˆåŠŸ +GET {{appApi}}/trade/cart/list +tenant-id: {{appTenantId}} +Authorization: Bearer {{appToken}} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/AppCartController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/AppCartController.java new file mode 100644 index 0000000..471f88b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/AppCartController.java @@ -0,0 +1,80 @@ +package com.tashow.cloud.trade.controller.app.cart; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.*; +import com.tashow.cloud.trade.controller.app.cart.vo.*; +import com.tashow.cloud.trade.service.cart.CartService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 App - 购物车") +@RestController +@RequestMapping("/trade/cart") +@RequiredArgsConstructor +@Validated +@Slf4j +public class AppCartController { + + @Resource + private CartService cartService; + + @PostMapping("/add") + @Operation(summary = "添加购物车商å“") + public CommonResult addCart(@Valid @RequestBody AppCartAddReqVO addCountReqVO) { + return success(cartService.addCart(getLoginUserId(), addCountReqVO)); + } + + @PutMapping("/update-count") + @Operation(summary = "æ›´æ–°è´­ç‰©è½¦å•†å“æ•°é‡") + public CommonResult updateCartCount(@Valid @RequestBody AppCartUpdateCountReqVO updateReqVO) { + cartService.updateCartCount(getLoginUserId(), updateReqVO); + return success(true); + } + + @PutMapping("/update-selected") + @Operation(summary = "更新购物车商å“选中") + public CommonResult updateCartSelected(@Valid @RequestBody AppCartUpdateSelectedReqVO updateReqVO) { + cartService.updateCartSelected(getLoginUserId(), updateReqVO); + return success(true); + } + + @PutMapping("/reset") + @Operation(summary = "é‡ç½®è´­ç‰©è½¦å•†å“") + public CommonResult resetCart(@Valid @RequestBody AppCartResetReqVO updateReqVO) { + cartService.resetCart(getLoginUserId(), updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除购物车商å“") + @Parameter(name = "ids", description = "购物车商å“ç¼–å·", required = true, example = "1024,2048") + public CommonResult deleteCart(@RequestParam("ids") List ids) { + cartService.deleteCart(getLoginUserId(), ids); + return success(true); + } + + @GetMapping("get-count") + @Operation(summary = "æŸ¥è¯¢ç”¨æˆ·åœ¨è´­ç‰©è½¦ä¸­çš„å•†å“æ•°é‡") + public CommonResult getCartCount() { + return success(cartService.getCartCount(getLoginUserId())); + } + + @GetMapping("/list") + @Operation(summary = "查询用户的购物车列表") + public CommonResult getCartList() { + return success(cartService.getCartList(getLoginUserId())); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartAddReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartAddReqVO.java new file mode 100644 index 0000000..ecf9716 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartAddReqVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.trade.controller.app.cart.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "用户 App - 购物车添加购物项 Request VO") +@Data +public class AppCartAddReqVO { + + @Schema(description = "å•†å“ SKU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED,example = "1024") + @NotNull(message = "å•†å“ SKU ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long skuId; + + @Schema(description = "æ–°å¢žå•†å“æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "æ•°é‡ä¸èƒ½ä¸ºç©º") + @Min(value = 1, message = "商哿•°é‡å¿…须大于等于 1") + private Integer count; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartDetailRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartDetailRespVO.java new file mode 100644 index 0000000..eb8a0e4 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartDetailRespVO.java @@ -0,0 +1,117 @@ +package com.tashow.cloud.trade.controller.app.cart.vo; + +import com.tashow.cloud.trade.controller.app.base.sku.AppProductSkuBaseRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "用户 App - 用户的购物车明细 Response VO") +@Data +public class AppCartDetailRespVO { + + /** + * 商å“分组数组 + */ + private List itemGroups; + + /** + * 费用 + */ + private Order order; + + @Schema(description = "商å“分组") // 多个商å“,å‚加åŒä¸€ä¸ªæ´»åŠ¨ï¼Œä»Žè€Œå½¢æˆåˆ†ç»„ + @Data + public static class ItemGroup { + + /** + * 商哿•°ç»„ + */ + private List items; + /** + * è¥é”€æ´»åŠ¨ï¼Œè®¢å•级别 + */ + private Promotion promotion; + + } + + @Schema(description = "å•†å“ SKU") + @Data + public static class Sku extends AppProductSkuBaseRespVO { + + /** + * SPU ä¿¡æ¯ + */ + private AppProductSkuBaseRespVO spu; + + // ========== 购物车相关的字段 ========== + + @Schema(description = "商哿•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer count; + @Schema(description = "是å¦é€‰ä¸­", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean selected; + + // ========== 价格相关的字段,对应 PriceCalculateRespDTO.OrderItem 的属性 ========== + + // TODO 芋艿:åŽç»­å¯ä»¥åŽ»é™¤ä¸€äº›æ— ç”¨çš„å­—æ®µ + + @Schema(description = "商å“原价(å•)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer originalPrice; + @Schema(description = "商å“原价(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") + private Integer totalOriginalPrice; + @Schema(description = "商å“级优惠(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "300") + private Integer totalPromotionPrice; + @Schema(description = "最终购买金é¢ï¼ˆæ€»ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "400") + private Integer totalPresentPrice; + @Schema(description = "最终购买金é¢ï¼ˆå•)", requiredMode = Schema.RequiredMode.REQUIRED, example = "500") + private Integer presentPrice; + @Schema(description = "应付金é¢ï¼ˆæ€»ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "600") + private Integer totalPayPrice; + + // ========== è¥é”€ç›¸å…³çš„字段 ========== + /** + * è¥é”€æ´»åŠ¨ï¼Œå•†å“级别 + */ + private Promotion promotion; + + } + + @Schema(description = "订å•") // 对应 PriceCalculateRespDTO.Order 类,用于费用(åˆè®¡ï¼‰ + @Data + public static class Order { + + // TODO 芋艿:åŽç»­å¯ä»¥åŽ»é™¤ä¸€äº›æ— ç”¨çš„å­—æ®µ + + @Schema(description = "商å“原价(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer skuOriginalPrice; + @Schema(description = "商å“优惠(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") + private Integer skuPromotionPrice; + @Schema(description = "订å•优惠(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "300") + private Integer orderPromotionPrice; + @Schema(description = "è¿è´¹é‡‘é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "400") + private Integer deliveryPrice; + @Schema(description = "应付金é¢ï¼ˆæ€»ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "500") + private Integer payPrice; + + } + + @Schema(description = "è¥é”€æ´»åЍ") // 对应 PriceCalculateRespDTO.Promotion 类的属性 + @Data + public static class Promotion { + + @Schema(description = "è¥é”€ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") // è¥é”€æ´»åŠ¨çš„ç¼–å·ã€ä¼˜æƒ åŠµçš„ç¼–å· + private Long id; + @Schema(description = "è¥é”€åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "xx 活动") + private String name; + @Schema(description = "è¥é”€ç±»åž‹", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + // ========== åŒ¹é…æƒ…况 ========== + @Schema(description = "æ˜¯å¦æ»¡è¶³ä¼˜æƒ æ¡ä»¶", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean meet; + @Schema(description = "满足æ¡ä»¶çš„æç¤º", requiredMode = Schema.RequiredMode.REQUIRED, example = "圣诞价:çœ 150.00 å…ƒ") + private String meetTip; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartListRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartListRespVO.java new file mode 100644 index 0000000..b76321c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartListRespVO.java @@ -0,0 +1,48 @@ +package com.tashow.cloud.trade.controller.app.cart.vo; + +import com.tashow.cloud.trade.controller.app.base.sku.AppProductSkuBaseRespVO; +import com.tashow.cloud.trade.controller.app.base.spu.AppProductSpuBaseRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "用户 App - 用户的购物列表 Response VO") +@Data +public class AppCartListRespVO { + + /** + * 有效的购物项数组 + */ + private List validList; + + /** + * 无效的购物项数组 + */ + private List invalidList; + + @Schema(description = "购物项") + @Data + public static class Cart { + + @Schema(description = "购物项的编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "商哿•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer count; + + @Schema(description = "是å¦é€‰ä¸­", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean selected; + + /** + * å•†å“ SPU + */ + private AppProductSpuBaseRespVO spu; + /** + * å•†å“ SKU + */ + private AppProductSkuBaseRespVO sku; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartResetReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartResetReqVO.java new file mode 100644 index 0000000..785aded --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartResetReqVO.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.trade.controller.app.cart.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "用户 App - 购物车é‡ç½® Request VO") +@Data +public class AppCartResetReqVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "å•†å“ SKU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED,example = "1024") + @NotNull(message = "å•†å“ SKU ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long skuId; + + @Schema(description = "商哿•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "æ•°é‡ä¸èƒ½ä¸ºç©º") + @Min(message = "æ•°é‡å¿…须大于 0", value = 1L) + private Integer count; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartUpdateCountReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartUpdateCountReqVO.java new file mode 100644 index 0000000..243d59e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartUpdateCountReqVO.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.controller.app.cart.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "用户 App - è´­ç‰©è½¦æ›´æ–°æ•°é‡ Request VO") +@Data +public class AppCartUpdateCountReqVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long id; + + @Schema(description = "商哿•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "æ•°é‡ä¸èƒ½ä¸ºç©º") + @Min(message = "æ•°é‡å¿…须大于 0", value = 1L) + private Integer count; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartUpdateSelectedReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartUpdateSelectedReqVO.java new file mode 100644 index 0000000..185fae1 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/cart/vo/AppCartUpdateSelectedReqVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.trade.controller.app.cart.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import java.util.Collection; + +@Schema(description = "用户 App - 购物车更新是å¦é€‰ä¸­ Request VO") +@Data +public class AppCartUpdateSelectedReqVO { + + @Schema(description = "ç¼–å·åˆ—表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024,2048") + @NotNull(message = "ç¼–å·åˆ—表ä¸èƒ½ä¸ºç©º") + private Collection ids; + + @Schema(description = "是å¦é€‰ä¸­", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是å¦é€‰ä¸­ä¸èƒ½ä¸ºç©º") + private Boolean selected; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/config/AppTradeConfigController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/config/AppTradeConfigController.java new file mode 100644 index 0000000..dbc9213 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/config/AppTradeConfigController.java @@ -0,0 +1,46 @@ +package com.tashow.cloud.trade.controller.app.config; + +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import com.tashow.cloud.trade.controller.app.config.vo.AppTradeConfigRespVO; +import com.tashow.cloud.trade.convert.config.TradeConfigConvert; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; +import com.tashow.cloud.trade.service.config.TradeConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +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.RestController; + +import jakarta.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "用户 App - 交易é…ç½®") +@RestController +@RequestMapping("/trade/config") +@RequiredArgsConstructor +@Validated +@Slf4j +public class AppTradeConfigController { + + @Resource + private TradeConfigService tradeConfigService; + + @Value("${yudao.tencent-lbs-key}") + private String tencentLbsKey; + + @GetMapping("/get") + @Operation(summary = "获得交易é…ç½®") + @PermitAll + public CommonResult getTradeConfig() { + TradeConfigDO config = ObjUtil.defaultIfNull(tradeConfigService.getTradeConfig(), new TradeConfigDO()); + return success(TradeConfigConvert.INSTANCE.convert02(config).setTencentLbsKey(tencentLbsKey)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/config/vo/AppTradeConfigRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/config/vo/AppTradeConfigRespVO.java new file mode 100644 index 0000000..e87c6d0 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/config/vo/AppTradeConfigRespVO.java @@ -0,0 +1,44 @@ +package com.tashow.cloud.trade.controller.app.config.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@Schema(description = "用户 App - 交易é…ç½® Response VO") +@Data +public class AppTradeConfigRespVO { + + @Schema(description = "腾讯地图 KEY", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + private String tencentLbsKey; + + // ========== é…é€ç›¸å…³ ========== + + @Schema(description = "是å¦å¼€å¯è‡ªæ", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是å¦å¼€å¯è‡ªæä¸èƒ½ä¸ºç©º") + private Boolean deliveryPickUpEnabled; + + // ========== å”®åŽç›¸å…³ ========== + + @Schema(description = "å”®åŽçš„退款ç†ç”±", requiredMode = Schema.RequiredMode.REQUIRED) + private List afterSaleRefundReasons; + + @Schema(description = "å”®åŽçš„退货ç†ç”±", requiredMode = Schema.RequiredMode.REQUIRED) + private List afterSaleReturnReasons; + + // ========== 分销相关 ========== + + @Schema(description = "åˆ†é”€æµ·æŠ¥åœ°å€æ•°ç»„", requiredMode = Schema.RequiredMode.REQUIRED) + private List brokeragePosterUrls; + + @Schema(description = "佣金冻结时间(天)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer brokerageFrozenDays; + + @Schema(description = "佣金æçŽ°æœ€å°é‡‘é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer brokerageWithdrawMinPrice; + + @Schema(description = "æçŽ°æ–¹å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]") + private List brokerageWithdrawTypes; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/AppDeliverExpressController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/AppDeliverExpressController.java new file mode 100644 index 0000000..717d04b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/AppDeliverExpressController.java @@ -0,0 +1,41 @@ +package com.tashow.cloud.trade.controller.app.delivery; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import com.tashow.cloud.trade.controller.app.delivery.vo.express.AppDeliveryExpressRespVO; +import com.tashow.cloud.trade.convert.delivery.DeliveryExpressConvert; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.tashow.cloud.trade.service.delivery.DeliveryExpressService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; +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.RestController; + +import jakarta.annotation.Resource; +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "用户 App - 快递公å¸") +@RestController +@RequestMapping("/trade/delivery/express") +@Validated +public class AppDeliverExpressController { + + @Resource + private DeliveryExpressService deliveryExpressService; + + @GetMapping("/list") + @Operation(summary = "获得快递公å¸åˆ—表") + @PermitAll + public CommonResult> getDeliveryExpressList() { + List list = deliveryExpressService.getDeliveryExpressListByStatus(CommonStatusEnum.ENABLE.getStatus()); + list.sort(Comparator.comparing(DeliveryExpressDO::getSort)); + return success(DeliveryExpressConvert.INSTANCE.convertList03(list)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/AppDeliverPickUpStoreController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/AppDeliverPickUpStoreController.java new file mode 100644 index 0000000..fe259f2 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/AppDeliverPickUpStoreController.java @@ -0,0 +1,58 @@ +package com.tashow.cloud.trade.controller.app.delivery; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import com.tashow.cloud.trade.controller.app.delivery.vo.pickup.AppDeliveryPickUpStoreRespVO; +import com.tashow.cloud.trade.convert.delivery.DeliveryPickUpStoreConvert; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; +import com.tashow.cloud.trade.service.delivery.DeliveryPickUpStoreService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; +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 jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "用户 App - 自æé—¨åº—") +@RestController +@RequestMapping("/trade/delivery/pick-up-store") +@Validated +public class AppDeliverPickUpStoreController { + + @Resource + private DeliveryPickUpStoreService deliveryPickUpStoreService; + + @GetMapping("/list") + @Operation(summary = "获得自æé—¨åº—列表") + @Parameters({ + @Parameter(name = "latitude", description = "精度", example = "110"), + @Parameter(name = "longitude", description = "纬度", example = "120") + }) + @PermitAll + public CommonResult> getDeliveryPickUpStoreList( + @RequestParam(value = "latitude", required = false) Double latitude, + @RequestParam(value = "longitude", required = false) Double longitude) { + List list = deliveryPickUpStoreService.getDeliveryPickUpStoreListByStatus( + CommonStatusEnum.ENABLE.getStatus()); + return success(DeliveryPickUpStoreConvert.INSTANCE.convertList(list, latitude, longitude)); + } + + @GetMapping("/get") + @Operation(summary = "获得自æé—¨åº—") + @Parameter(name = "id", description = "门店编å·") + @PermitAll + public CommonResult getOrder(@RequestParam("id") Long id) { + DeliveryPickUpStoreDO store = deliveryPickUpStoreService.getDeliveryPickUpStore(id); + return success(DeliveryPickUpStoreConvert.INSTANCE.convert03(store)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/config/AppDeliveryConfigRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/config/AppDeliveryConfigRespVO.java new file mode 100644 index 0000000..843b0b8 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/config/AppDeliveryConfigRespVO.java @@ -0,0 +1,17 @@ +package com.tashow.cloud.trade.controller.app.delivery.vo.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +// TODO 芋艿:åŽç»­è¦å®žçŽ°ä¸‹ï¼Œé…é€é…置;åŽç»­èžåˆåˆ° AppTradeConfigRespVO 中 +@Schema(description = "用户 App - é…é€é…ç½® Response VO") +@Data +public class AppDeliveryConfigRespVO { + + @Schema(description = "腾讯地图 KEY", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + private String tencentLbsKey; + + @Schema(description = "是å¦å¼€å¯è‡ªæ", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean pickUpEnable; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/express/AppDeliveryExpressRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/express/AppDeliveryExpressRespVO.java new file mode 100644 index 0000000..ca9f525 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/express/AppDeliveryExpressRespVO.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.trade.controller.app.delivery.vo.express; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - å¿«é€’å…¬å¸ Response VO") +@Data +public class AppDeliveryExpressRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "门店åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "顺丰") + private String name; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java new file mode 100644 index 0000000..324a98d --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/delivery/vo/pickup/AppDeliveryPickUpStoreRespVO.java @@ -0,0 +1,40 @@ +package com.tashow.cloud.trade.controller.app.delivery.vo.pickup; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 自æé—¨åº— Response VO") +@Data +public class AppDeliveryPickUpStoreRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128") + private Long id; + + @Schema(description = "门店åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "æŽå››") + private String name; + + @Schema(description = "门店 logo", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + private String logo; + + @Schema(description = "门店手机", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601892312") + private String phone; + + @Schema(description = "区域编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "18733") + private Integer areaId; + + @Schema(description = "地区åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海上海市普陀区") + private String areaName; + + @Schema(description = "门店详细地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "夿—¦å¤§å­¦è·¯ 188 å·") + private String detailAddress; + + @Schema(description = "纬度", requiredMode = Schema.RequiredMode.REQUIRED, example = "5.88") + private Double latitude; + + @Schema(description = "ç»åº¦", requiredMode = Schema.RequiredMode.REQUIRED, example = "6.99") + private Double longitude; + + @Schema(description = "è·ç¦»ï¼Œå•ä½ï¼šåƒç±³", example = "100") // åªæœ‰åœ¨ç”¨æˆ·ä¼ é€’了ç»çº¬åº¦æ—¶ï¼Œæ‰è¿›è¡Œè®¡ç®— + private Double distance; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/AppTradeOrderController.http b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/AppTradeOrderController.http new file mode 100644 index 0000000..f51ddff --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/AppTradeOrderController.http @@ -0,0 +1,69 @@ +### /trade-order/settlement 获得订å•结算信æ¯ï¼ˆåŸºäºŽå•†å“) +GET {{appApi}}/trade/order/settlement?type=0&items[0].skuId=1&items[0].count=2&items[1].skuId=2&items[1].count=3&couponId=1 +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +### /trade-order/settlement 获得订å•结算信æ¯ï¼ˆåŸºäºŽè´­ç‰©è½¦ï¼‰ +GET {{appApi}}/trade/order/settlement?type=0&items[0].cartId=50&couponId=1 +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +### /trade-order/create 创建订å•(基于商å“)ã€å¿«é€’】 +POST {{appApi}}/trade/order/create +Content-Type: application/json +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +{ + "pointStatus": true, + "deliveryType": 1, + "addressId": 21, + "items": [ + { + "skuId": 1, + "count": 2 + } + ], + "remark": "我是备注" +} + +### /trade-order/create 创建订å•(基于商å“)ã€è‡ªæã€‘ +POST {{appApi}}/trade/order/create +Content-Type: application/json +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +{ + "pointStatus": true, + "deliveryType": 2, + "pickUpStoreId": 1, + "items": [ + { + "skuId": 1, + "count": 2 + } + ], + "remark": "我是备注", + "receiverName": "土豆", + "receiverMobile": "15601691300" +} + +### 获得订å•交易的分页 +GET {{appApi}}/trade/order/page?pageNo=1&pageSize=10 +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +### 获得订å•交易的详细 +GET {{appApi}}/trade/order/get-detail?id=21 +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +### 获得交易订å•的物æµè½¨è¿¹ +GET {{appApi}}/trade/order/get-express-track-list?id=70 +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} + +### /trade-order/settlement-product 获得商å“ç»“ç®—ä¿¡æ¯ +GET {{appApi}}/trade/order/settlement-product?spuIds=633 +Authorization: Bearer {{appToken}} +tenant-id: {{appTenantId}} \ No newline at end of file diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/AppTradeOrderController.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/AppTradeOrderController.java new file mode 100644 index 0000000..71c11fa --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/AppTradeOrderController.java @@ -0,0 +1,204 @@ +package com.tashow.cloud.trade.controller.app.order; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.*; +import com.tashow.cloud.trade.controller.app.order.vo.*; +import com.tashow.cloud.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; +import com.tashow.cloud.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; +import com.tashow.cloud.trade.convert.order.TradeOrderConvert; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import com.tashow.cloud.trade.framework.order.config.TradeOrderProperties; +import com.tashow.cloud.trade.service.aftersale.AfterSaleService; +import com.tashow.cloud.trade.service.delivery.DeliveryExpressService; +import com.tashow.cloud.trade.service.order.TradeOrderQueryService; +import com.tashow.cloud.trade.service.order.TradeOrderUpdateService; +import com.tashow.cloud.trade.service.price.TradePriceService; +import com.google.common.collect.Maps; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "用户 App - 交易订å•") +@RestController +@RequestMapping("/trade/order") +@Validated +@Slf4j +public class AppTradeOrderController { + + @Resource + private TradeOrderUpdateService tradeOrderUpdateService; + @Resource + private TradeOrderQueryService tradeOrderQueryService; + @Resource + private DeliveryExpressService deliveryExpressService; + @Resource + private AfterSaleService afterSaleService; + @Resource + private TradePriceService priceService; + + @Resource + private TradeOrderProperties tradeOrderProperties; + + @GetMapping("/settlement") + @Operation(summary = "获得订å•结算信æ¯") + public CommonResult settlementOrder(@Valid AppTradeOrderSettlementReqVO settlementReqVO) { + return success(tradeOrderUpdateService.settlementOrder(getLoginUserId(), settlementReqVO)); + } + + @GetMapping("/settlement-product") + @Operation(summary = "获得商å“结算信æ¯", description = "用于商å“列表ã€å•†å“详情,获得å‚与活动åŽçš„价格信æ¯") + @Parameter(name = "spuIds", description = "å•†å“ SPU ç¼–å·æ•°ç»„") + @PermitAll + public CommonResult> settlementProduct(@RequestParam("spuIds") List spuIds) { + return success(priceService.calculateProductPrice(getLoginUserId(), spuIds)); + } + + @PostMapping("/create") + @Operation(summary = "创建订å•") + public CommonResult createOrder(@Valid @RequestBody AppTradeOrderCreateReqVO createReqVO) { + TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), createReqVO); + return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId())); + } + + @PostMapping("/update-paid") + @Operation(summary = "更新订å•为已支付") // ç”± pay-module 支付æœåŠ¡ï¼Œè¿›è¡Œå›žè°ƒï¼Œå¯è§ PayNotifyJob + @PermitAll + public CommonResult updateOrderPaid(@RequestBody PayOrderNotifyReqDTO notifyReqDTO) { + tradeOrderUpdateService.updateOrderPaid(Long.valueOf(notifyReqDTO.getMerchantOrderId()), + notifyReqDTO.getPayOrderId()); + return success(true); + } + + @GetMapping("/get-detail") + @Operation(summary = "获得交易订å•") + @Parameters({ + @Parameter(name = "id", description = "交易订å•ç¼–å·"), + @Parameter(name = "sync", description = "是å¦åŒæ­¥æ”¯ä»˜çжæ€", example = "true") + }) + public CommonResult getOrderDetail(@RequestParam("id") Long id, + @RequestParam(value = "sync", required = false) Boolean sync) { + // 1.1 æŸ¥è¯¢è®¢å• + TradeOrderDO order = tradeOrderQueryService.getOrder(getLoginUserId(), id); + if (order == null) { + return success(null); + } + // 1.2 sync 仅在等待支付 + if (Boolean.TRUE.equals(sync) + && TradeOrderStatusEnum.isUnpaid(order.getStatus()) && !order.getPayStatus()) { + tradeOrderUpdateService.syncOrderPayStatusQuietly(order.getId(), order.getPayOrderId()); + // 釿–°æŸ¥è¯¢ï¼Œå› ä¸ºåŒæ­¥åŽï¼Œå¯èƒ½ä¼šæœ‰å˜åŒ– + order = tradeOrderQueryService.getOrder(id); + } + + // 2.1 查询订å•项 + List orderItems = tradeOrderQueryService.getOrderItemListByOrderId(order.getId()); + // 2.2 查询物æµå…¬å¸ + DeliveryExpressDO express = order.getLogisticsId() != null && order.getLogisticsId() > 0 ? + deliveryExpressService.getDeliveryExpress(order.getLogisticsId()) : null; + // 2.3 æœ€ç»ˆç»„åˆ + return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express)); + } + + @GetMapping("/get-express-track-list") + @Operation(summary = "获得交易订å•的物æµè½¨è¿¹") + @Parameter(name = "id", description = "交易订å•ç¼–å·") + public CommonResult> getOrderExpressTrackList(@RequestParam("id") Long id) { + return success(TradeOrderConvert.INSTANCE.convertList02( + tradeOrderQueryService.getExpressTrackList(id, getLoginUserId()))); + } + + @GetMapping("/page") + @Operation(summary = "获得交易订å•分页") + public CommonResult> getOrderPage(AppTradeOrderPageReqVO reqVO) { + // æŸ¥è¯¢è®¢å• + PageResult pageResult = tradeOrderQueryService.getOrderPage(getLoginUserId(), reqVO); + // 查询订å•项 + List orderItems = tradeOrderQueryService.getOrderItemListByOrderId( + convertSet(pageResult.getList(), TradeOrderDO::getId)); + // æœ€ç»ˆç»„åˆ + return success(TradeOrderConvert.INSTANCE.convertPage02(pageResult, orderItems)); + } + + @GetMapping("/get-count") + @Operation(summary = "èŽ·å¾—äº¤æ˜“è®¢å•æ•°é‡") + public CommonResult> getOrderCount() { + Map orderCount = Maps.newLinkedHashMapWithExpectedSize(5); + // 全部 + orderCount.put("allCount", tradeOrderQueryService.getOrderCount(getLoginUserId(), null, null)); + // 待付款(未支付) + orderCount.put("unpaidCount", tradeOrderQueryService.getOrderCount(getLoginUserId(), + TradeOrderStatusEnum.UNPAID.getStatus(), null)); + // å¾…å‘è´§ + orderCount.put("undeliveredCount", tradeOrderQueryService.getOrderCount(getLoginUserId(), + TradeOrderStatusEnum.UNDELIVERED.getStatus(), null)); + // å¾…æ”¶è´§ + orderCount.put("deliveredCount", tradeOrderQueryService.getOrderCount(getLoginUserId(), + TradeOrderStatusEnum.DELIVERED.getStatus(), null)); + // 待评价 + orderCount.put("uncommentedCount", tradeOrderQueryService.getOrderCount(getLoginUserId(), + TradeOrderStatusEnum.COMPLETED.getStatus(), false)); + // å”®åŽæ•°é‡ + orderCount.put("afterSaleCount", afterSaleService.getApplyingAfterSaleCount(getLoginUserId())); + return success(orderCount); + } + + @PutMapping("/receive") + @Operation(summary = "ç¡®è®¤äº¤æ˜“è®¢å•æ”¶è´§") + @Parameter(name = "id", description = "交易订å•ç¼–å·") + public CommonResult receiveOrder(@RequestParam("id") Long id) { + tradeOrderUpdateService.receiveOrderByMember(getLoginUserId(), id); + return success(true); + } + + @DeleteMapping("/cancel") + @Operation(summary = "å–æ¶ˆäº¤æ˜“订å•") + @Parameter(name = "id", description = "交易订å•ç¼–å·") + public CommonResult cancelOrder(@RequestParam("id") Long id) { + tradeOrderUpdateService.cancelOrderByMember(getLoginUserId(), id); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除交易订å•") + @Parameter(name = "id", description = "交易订å•ç¼–å·") + public CommonResult deleteOrder(@RequestParam("id") Long id) { + tradeOrderUpdateService.deleteOrder(getLoginUserId(), id); + return success(true); + } + + // ========== 订å•项 ========== + + @GetMapping("/item/get") + @Operation(summary = "获得交易订å•项") + @Parameter(name = "id", description = "交易订å•项编å·") + public CommonResult getOrderItem(@RequestParam("id") Long id) { + TradeOrderItemDO item = tradeOrderQueryService.getOrderItem(getLoginUserId(), id); + return success(TradeOrderConvert.INSTANCE.convert03(item)); + } + + @PostMapping("/item/create-comment") + @Operation(summary = "创建交易订å•项的评价") + public CommonResult createOrderItemComment(@RequestBody AppTradeOrderItemCommentCreateReqVO createReqVO) { + return success(tradeOrderUpdateService.createOrderItemCommentByMember(getLoginUserId(), createReqVO)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppOrderExpressTrackRespDTO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppOrderExpressTrackRespDTO.java new file mode 100644 index 0000000..e5c591e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppOrderExpressTrackRespDTO.java @@ -0,0 +1,23 @@ +package com.tashow.cloud.trade.controller.app.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 快递查询的轨迹 Resp DTO + * + * @author jason + */ +@Schema(description = "用户 App - 快递查询的轨迹 Response VO") +@Data +public class AppOrderExpressTrackRespDTO { + + @Schema(description = "å‘生时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime time; + + @Schema(description = "快递状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "已签收") + private String content; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java new file mode 100644 index 0000000..8f22f80 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java @@ -0,0 +1,21 @@ +package com.tashow.cloud.trade.controller.app.order.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import lombok.Data; + +@Schema(description = "用户 App - 交易订å•创建 Request VO") +@Data +public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO { + + @Schema(description = "备注", example = "这个是我的订å•哟") + private String remark; + + @AssertTrue(message = "é…逿–¹å¼ä¸èƒ½ä¸ºç©º") + @JsonIgnore + public boolean isDeliveryTypeNotNull() { + return getDeliveryType() != null; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderCreateRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderCreateRespVO.java new file mode 100644 index 0000000..13c9eb8 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderCreateRespVO.java @@ -0,0 +1,16 @@ +package com.tashow.cloud.trade.controller.app.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户 App - 交易订å•创建 Response VO") +@Data +public class AppTradeOrderCreateRespVO { + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long payOrderId; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderDetailRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderDetailRespVO.java new file mode 100644 index 0000000..1d56c11 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderDetailRespVO.java @@ -0,0 +1,151 @@ +package com.tashow.cloud.trade.controller.app.order.vo; + +import com.tashow.cloud.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "用户 App - 订å•交易的明细 Response VO") +@Data +public class AppTradeOrderDetailRespVO { + + // ========== 订å•åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "è®¢å•æµæ°´å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1146347329394184195") + private String no; + + @Schema(description = "订å•类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer type; + + @Schema(description = "䏋啿—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "用户备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + private String userRemark; + + @Schema(description = "订å•状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "è´­ä¹°çš„å•†å“æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer productCount; + + @Schema(description = "订å•å®Œæˆæ—¶é—´") + private LocalDateTime finishTime; + + @Schema(description = "订å•å–æ¶ˆæ—¶é—´") + private LocalDateTime cancelTime; + + @Schema(description = "是å¦è¯„ä»·", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean commentStatus; + + // ========== ä»·æ ¼ + æ”¯ä»˜åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "是å¦å·²æ”¯ä»˜", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean payStatus; + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long payOrderId; + + @Schema(description = "付款时间") + private LocalDateTime payTime; + + @Schema(description = "付款超时时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime payExpireTime; + + @Schema(description = "支付渠é“", example = "wx_lite_pay") + private String payChannelCode; + @Schema(description = "支付渠é“å", example = "微信å°ç¨‹åºæ”¯ä»˜") + private String payChannelName; + + @Schema(description = "商å“原价(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Integer totalPrice; + + @Schema(description = "订å•优惠(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer discountPrice; + + @Schema(description = "è¿è´¹é‡‘é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer deliveryPrice; + + @Schema(description = "订å•调价(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer adjustPrice; + + @Schema(description = "应付金é¢ï¼ˆæ€»ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Integer payPrice; + + // ========== æ”¶ä»¶ + 物æµåŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "é…逿–¹å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer deliveryType; + + @Schema(description = "å‘货物æµå…¬å¸ç¼–å·", example = "10") + private Long logisticsId; + + @Schema(description = "å‘货物æµåç§°", example = "顺丰快递") + private String logisticsName; + + @Schema(description = "å‘货物æµå•å·", example = "1024") + private String logisticsNo; + + @Schema(description = "å‘è´§æ—¶é—´") + private LocalDateTime deliveryTime; + + @Schema(description = "æ”¶è´§æ—¶é—´") + private LocalDateTime receiveTime; + + @Schema(description = "收件人åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + private String receiverName; + + @Schema(description = "收件人手机", requiredMode = Schema.RequiredMode.REQUIRED, example = "13800138000") + private String receiverMobile; + + @Schema(description = "收件人地区编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "110000") + private Integer receiverAreaId; + + @Schema(description = "收件人地区åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海 上海市 普陀区") + private String receiverAreaName; + + @Schema(description = "收件人详细地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "中关æ‘大街 1 å·") + private String receiverDetailAddress; + + @Schema(description = "自æé—¨åº—ç¼–å·", example = "1088") + private Long pickUpStoreId; + + @Schema(description = "è‡ªææ ¸é”€ç ", example = "40964096") + private String pickUpVerifyCode; + + // ========== å”®åŽåŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "å”®åŽçжæ€", example = "0") + private Integer refundStatus; + + @Schema(description = "退款金é¢ï¼Œå•ä½ï¼šåˆ†", example = "100") + private Integer refundPrice; + + // ========== è¥é”€åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "优惠劵编å·", example = "1024") + private Long couponId; + + @Schema(description = "优惠劵å‡å…金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer couponPrice; + + @Schema(description = "积分抵扣的金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer pointPrice; + + @Schema(description = "VIP å‡å…金é¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") + private Integer vipPrice; + + @Schema(description = "拼团记录编å·", example = "100") + private Long combinationRecordId; + + /** + * 订å•项数组 + */ + private List items; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderPageItemRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderPageItemRespVO.java new file mode 100644 index 0000000..55943ad --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderPageItemRespVO.java @@ -0,0 +1,58 @@ +package com.tashow.cloud.trade.controller.app.order.vo; + +import com.tashow.cloud.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "用户 App - 订å•交易的分页项 Response VO") +@Data +public class AppTradeOrderPageItemRespVO { + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "è®¢å•æµæ°´å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1146347329394184195") + private String no; + + @Schema(description = "订å•类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer type; + + @Schema(description = "订å•状æ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "è´­ä¹°çš„å•†å“æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer productCount; + + @Schema(description = "是å¦è¯„ä»·", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean commentStatus; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + // ========== ä»·æ ¼ + æ”¯ä»˜åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "支付订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long payOrderId; + + @Schema(description = "应付金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private Integer payPrice; + + // ========== æ”¶ä»¶ + 物æµåŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "é…逿–¹å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer deliveryType; + + /** + * 订å•项数组 + */ + private List items; + + // ========== è¥é”€åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "拼团记录编å·", example = "100") + private Long combinationRecordId; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderPageReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderPageReqVO.java new file mode 100644 index 0000000..1d79faa --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderPageReqVO.java @@ -0,0 +1,20 @@ +package com.tashow.cloud.trade.controller.app.order.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "交易订å•分页 Request VO") +@Data +public class AppTradeOrderPageReqVO extends PageParam { + + @Schema(description = "订å•状æ€", example = "1") + @InEnum(value = TradeOrderStatusEnum.class, message = "订å•状æ€å¿…须是 {value}") + private Integer status; + + @Schema(description = "是å¦è¯„ä»·", example = "true") + private Boolean commentStatus; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java new file mode 100644 index 0000000..ba06d1b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java @@ -0,0 +1,109 @@ +package com.tashow.cloud.trade.controller.app.order.vo; + +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "用户 App - 交易订å•结算 Request VO") +@Data +@Valid +public class AppTradeOrderSettlementReqVO { + + @Schema(description = "商å“项数组", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "商å“ä¸èƒ½ä¸ºç©º") + private List items; + + @Schema(description = "优惠劵编å·", example = "1024") + private Long couponId; + + @Schema(description = "是å¦ä½¿ç”¨ç§¯åˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是å¦ä½¿ç”¨ç§¯åˆ†ä¸èƒ½ä¸ºç©º") + private Boolean pointStatus; + + // ========== é…é€ç›¸å…³ç›¸å…³å­—段 ========== + @Schema(description = "é…逿–¹å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(value = DeliveryTypeEnum.class, message = "é…逿–¹å¼ä¸æ­£ç¡®") + private Integer deliveryType; + + @Schema(description = "收件地å€ç¼–å·", example = "1") + private Long addressId; + + @Schema(description = "自æé—¨åº—ç¼–å·", example = "1088") + private Long pickUpStoreId; + @Schema(description = "收件人åç§°", example = "芋艿") // é€‰æ‹©é—¨åº—è‡ªææ—¶ï¼Œè¯¥å­—段为è”系人å + private String receiverName; + @Schema(description = "收件人手机", example = "15601691300") // é€‰æ‹©é—¨åº—è‡ªææ—¶ï¼Œè¯¥å­—段为è”系人手机 + @Mobile(message = "收件人手机格å¼ä¸æ­£ç¡®") + private String receiverMobile; + + // ========== ç§’æ€æ´»åŠ¨ç›¸å…³å­—æ®µ ========== + @Schema(description = "ç§’æ€æ´»åŠ¨ç¼–å·", example = "1024") + private Long seckillActivityId; + + // ========== 拼团活动相关字段 ========== + @Schema(description = "拼团活动编å·", example = "1024") + private Long combinationActivityId; + + @Schema(description = "拼团团长编å·", example = "2048") + private Long combinationHeadId; + + // ========== ç ä»·æ´»åŠ¨ç›¸å…³å­—æ®µ ========== + @Schema(description = "ç ä»·è®°å½•ç¼–å·", example = "123") + private Long bargainRecordId; + + // ========== 积分商城活动相关字段 ========== + @Schema(description = "积分商城活动编å·", example = "123") + private Long pointActivityId; + + @AssertTrue(message = "æ´»åŠ¨å•†å“æ¯æ¬¡åªèƒ½è´­ä¹°ä¸€ç§è§„æ ¼") + @JsonIgnore + public boolean isValidActivityItems() { + // æ ¡éªŒæ˜¯å¦æ˜¯æ´»åŠ¨è®¢å• + if (ObjUtil.isAllEmpty(seckillActivityId, combinationActivityId, combinationHeadId, bargainRecordId)) { + return true; + } + // 校验订å•项是å¦è¶…出 + return items.size() == 1; + } + + @Data + @Schema(description = "用户 App - 商å“项") + @Valid + public static class Item { + + @Schema(description = "å•†å“ SKU ç¼–å·", example = "2048") + @NotNull(message = "å•†å“ SKU ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long skuId; + + @Schema(description = "è´­ä¹°æ•°é‡", example = "1") + @Min(value = 1, message = "è´­ä¹°æ•°é‡æœ€å°å€¼ä¸º {value}") + private Integer count; + + @Schema(description = "购物车项的编å·", example = "1024") + private Long cartId; + + @AssertTrue(message = "商å“䏿­£ç¡®") + @JsonIgnore + public boolean isValid() { + // 组åˆä¸€ï¼šskuId + count ä½¿ç”¨å•†å“ SKU + if (skuId != null && count != null) { + return true; + } + // 组åˆäºŒï¼šcartId 使用购物车项 + return cartId != null; + } + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java new file mode 100644 index 0000000..d39b48b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java @@ -0,0 +1,174 @@ +package com.tashow.cloud.trade.controller.app.order.vo; + +import com.tashow.cloud.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "用户 App - 交易订å•ç»“ç®—ä¿¡æ¯ Response VO") +@Data +public class AppTradeOrderSettlementRespVO { + + @Schema(description = "交易类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 对应 TradeOrderTypeEnum 枚举 + private Integer type; + + @Schema(description = "购物项数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List items; + + @Schema(description = "优惠劵数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List coupons; // å¯ç”¨ + ä¸å¯ç”¨ + + @Schema(description = "费用", requiredMode = Schema.RequiredMode.REQUIRED) + private Price price; + + @Schema(description = "收件地å€", requiredMode = Schema.RequiredMode.REQUIRED) + private Address address; + + @Schema(description = "已使用的积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer usePoint; + + @Schema(description = "总积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer totalPoint; + + /** + * è¥é”€æ´»åŠ¨æ•°ç»„ + * + * åªå¯¹åº” {@link TradePriceCalculateRespBO.Price#items} 商å“匹é…的活动 + */ + private List promotions; + + @Schema(description = "购物项") + @Data + public static class Item { + + // ========== SPU ä¿¡æ¯ ========== + + @Schema(description = "å“类编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long categoryId; + @Schema(description = "SPU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long spuId; + @Schema(description = "SPU åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "Apple iPhone 12") + private String spuName; + + // ========== SKU ä¿¡æ¯ ========== + + @Schema(description = "SKU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Integer skuId; + @Schema(description = "价格,å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer price; + @Schema(description = "图片地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + private String picUrl; + + @Schema(description = "属性数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private List properties; + + // ========== è´­ç‰©è½¦ä¿¡æ¯ ========== + + @Schema(description = "购物车编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Long cartId; + + @Schema(description = "è´­ä¹°æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer count; + + } + + @Schema(description = "费用(åˆè®¡ï¼‰") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class Price { + + @Schema(description = "商å“原价(总),å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "500") + private Integer totalPrice; + + @Schema(description = "订å•优惠(总),å•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "66") + private Integer discountPrice; + + @Schema(description = "è¿è´¹é‡‘é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") + private Integer deliveryPrice; + + @Schema(description = "优惠劵å‡å…金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer couponPrice; + + @Schema(description = "积分抵扣的金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") + private Integer pointPrice; + + @Schema(description = "VIP å‡å…金é¢ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "30") + private Integer vipPrice; + + @Schema(description = "实际支付金é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "450") + private Integer payPrice; + + } + + @Schema(description = "地å€ä¿¡æ¯") + @Data + public static class Address { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "收件人åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "å°çŽ‹") + private String name; + + @Schema(description = "手机å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") + private String mobile; + + @Schema(description = "地区编å·", requiredMode = Schema.RequiredMode.REQUIRED) + private Long areaId; + @Schema(description = "地区åå­—", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海上海市普陀区") + private String areaName; + + @Schema(description = "详细地å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "æœ›äº¬æ‚ ä¹æ±‡ A 座") + private String detailAddress; + + @Schema(description = "是å¦é»˜è®¤æ”¶ä»¶åœ°å€", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean defaultStatus; + + } + + @Schema(description = "优惠劵信æ¯") + @Data + public static class Coupon { + + @Schema(description = "优惠劵编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "优惠劵å", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节é€é€é€") + private String name; + + @Schema(description = "是å¦è®¾ç½®æ»¡å¤šå°‘金é¢å¯ç”¨", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // å•ä½ï¼šåˆ†ï¼›0 - ä¸é™åˆ¶ + private Integer usePrice; + + @Schema(description = "固定日期 - 生效开始时间") + private LocalDateTime validStartTime; + + @Schema(description = "固定日期 - ç”Ÿæ•ˆç»“æŸæ—¶é—´") + private LocalDateTime validEndTime; + + @Schema(description = "优惠类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer discountType; + + @Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80 + private Integer discountPercent; + + @Schema(description = "优惠金é¢", example = "10") + private Integer discountPrice; + + @Schema(description = "折扣上é™", example = "100") // å•ä½ï¼šåˆ†ï¼Œä»…在 discountType 为 PERCENT 使用 + private Integer discountLimitPrice; + + @Schema(description = "是å¦å¯ç”¨", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean match; + + @Schema(description = "ä¸å¯ç”¨åŽŸå› ", example = "优惠劵已过期") + private String mismatchReason; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java new file mode 100644 index 0000000..f6e31c3 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java @@ -0,0 +1,81 @@ +package com.tashow.cloud.trade.controller.app.order.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "用户 App - 商å“ç»“ç®—ä¿¡æ¯ Response VO") +@Data +public class AppTradeProductSettlementRespVO { + + @Schema(description = "SPU 商å“ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long spuId; + + @Schema(description = "SKU ä»·æ ¼ä¿¡æ¯æ•°ç»„", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private List skus; + + @Schema(description = "满å‡é€æ´»åŠ¨ä¿¡æ¯", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private RewardActivity rewardActivity; + + @Schema(description = "SKU 价格信æ¯") + @Data + public static class Sku implements Serializable { + + @Schema(description = "å•†å“ SKU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "优惠åŽä»·æ ¼ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer promotionPrice; + + @Schema(description = "è¥é”€ç±»åž‹", requiredMode = Schema.RequiredMode.REQUIRED, example = "4") + private Integer promotionType; // 对应 PromotionTypeEnum 枚举,目å‰åªæœ‰ 4 å’Œ 6 ä¸¤ç§ + + @Schema(description = "è¥é”€ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED) + private Long promotionId; // ç›®å‰åªæœ‰é™æ—¶æŠ˜æ‰£æ´»åŠ¨çš„ç¼–å· + + @Schema(description = "æ´»åŠ¨ç»“æŸæ—¶é—´", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime promotionEndTime; + + } + + @Schema(description = "满å‡é€æ´»åŠ¨ä¿¡æ¯") + @Data + public static class RewardActivity { + + @Schema(description = "æ»¡å‡æ´»åŠ¨ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "æ¡ä»¶ç±»åž‹", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer conditionType; + + @Schema(description = "优惠规则的数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List rules; + + } + + @Schema(description = "优惠规则") + @Data + public static class RewardActivityRule { + + @Schema(description = "优惠门槛", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 1. 满 N 元,å•ä½ï¼šåˆ†; 2. 满 N ä»¶ + private Integer limit; + + @Schema(description = "优惠价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer discountPrice; + + @Schema(description = "是å¦åŒ…é‚®", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean freeDelivery; + + @Schema(description = "èµ é€çš„积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer point; + + @Schema(description = "èµ é€çš„优惠劵编å·çš„æ•°ç»„") + private Map giveCouponTemplateCounts; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/item/AppTradeOrderItemCommentCreateReqVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/item/AppTradeOrderItemCommentCreateReqVO.java new file mode 100644 index 0000000..f4f8ace --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/item/AppTradeOrderItemCommentCreateReqVO.java @@ -0,0 +1,38 @@ +package com.tashow.cloud.trade.controller.app.order.vo.item; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.util.List; + +@Schema(description = "用户 App - 商å“评价创建 Request VO") +@Data +public class AppTradeOrderItemCommentCreateReqVO { + + @Schema(description = "是å¦åŒ¿å", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是å¦åŒ¿åä¸èƒ½ä¸ºç©º") + private Boolean anonymous; + + @Schema(description = "交易订å•项编å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "2312312") + @NotNull(message = "交易订å•项编å·ä¸èƒ½ä¸ºç©º") + private Long orderItemId; + + @Schema(description = "æè¿°æ˜Ÿçº§ 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + @NotNull(message = "æè¿°æ˜Ÿçº§ 1-5 分ä¸èƒ½ä¸ºç©º") + private Integer descriptionScores; + + @Schema(description = "æœåŠ¡æ˜Ÿçº§ 1-5 分", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + @NotNull(message = "æœåŠ¡æ˜Ÿçº§ 1-5 分ä¸èƒ½ä¸ºç©º") + private Integer benefitScores; + + @Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "穿身上很漂亮诶(*^â–½^*)") + @NotNull(message = "评论内容ä¸èƒ½ä¸ºç©º") + private String content; + + @Schema(description = "è¯„è®ºå›¾ç‰‡åœ°å€æ•°ç»„,以逗å·åˆ†éš”æœ€å¤šä¸Šä¼  9 å¼ ", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/xx.png]") + @Size(max = 9, message = "è¯„è®ºå›¾ç‰‡åœ°å€æ•°ç»„长度ä¸èƒ½è¶…过 9 å¼ ") + private List picUrls; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/item/AppTradeOrderItemRespVO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/item/AppTradeOrderItemRespVO.java new file mode 100644 index 0000000..1db5a42 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/app/order/vo/item/AppTradeOrderItemRespVO.java @@ -0,0 +1,61 @@ +package com.tashow.cloud.trade.controller.app.order.vo.item; + +import com.tashow.cloud.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "用户 App - 订å•交易项 Response VO") +@Data +public class AppTradeOrderItemRespVO { + + @Schema(description = "ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "订å•ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long orderId; + + @Schema(description = "å•†å“ SPU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long spuId; + @Schema(description = "å•†å“ SPU åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "èŠ‹é“æºç ") + private String spuName; + + @Schema(description = "å•†å“ SKU ç¼–å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long skuId; + + /** + * 属性数组 + */ + private List properties; + + @Schema(description = "商å“图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + private String picUrl; + + @Schema(description = "è´­ä¹°æ•°é‡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer count; + + @Schema(description = "是å¦è¯„ä»·", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean commentStatus; + + // ========== ä»·æ ¼ + æ”¯ä»˜åŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "商å“原价(å•)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Integer price; + + @Schema(description = "应付金é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ†", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") + private Integer payPrice; + + // ========== è¥é”€åŸºæœ¬ä¿¡æ¯ ========== + + // TODO èŠ‹è‰¿ï¼šåœ¨æ‰æ‘¸ä¸€ä¸‹ + + // ========== å”®åŽåŸºæœ¬ä¿¡æ¯ ========== + + @Schema(description = "å”®åŽç¼–å·", example = "1024") + private Long afterSaleId; + + @Schema(description = "å”®åŽçжæ€", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer afterSaleStatus; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/package-info.java new file mode 100644 index 0000000..516cd24 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * æä¾› RESTful API ç»™å‰ç«¯ï¼š + * 1. admin 包:æä¾›ç»™ç®¡ç†åŽå° yudao-ui-admin å‰ç«¯é¡¹ç›® + * 2. app 包:æä¾›ç»™ç”¨æˆ· APP yudao-ui-app å‰ç«¯é¡¹ç›®ï¼Œå®ƒçš„ Controller å’Œ VO éƒ½è¦æ·»åŠ  App å‰ç¼€ï¼Œç”¨äºŽå’Œç®¡ç†åŽå°è¿›è¡ŒåŒºåˆ† + */ +package com.tashow.cloud.trade.controller; diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/aftersale/AfterSaleConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/aftersale/AfterSaleConvert.java new file mode 100644 index 0000000..e8c5e71 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/aftersale/AfterSaleConvert.java @@ -0,0 +1,88 @@ +package com.tashow.cloud.trade.convert.aftersale; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.AfterSaleDetailRespVO; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.AfterSaleRespPageItemVO; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.log.AfterSaleLogRespVO; +import com.tashow.cloud.trade.controller.admin.base.member.user.MemberUserRespVO; +import com.tashow.cloud.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderBaseVO; +import com.tashow.cloud.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; +import com.tashow.cloud.trade.controller.app.aftersale.vo.AppAfterSaleRespVO; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleDO; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleLogDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.tashow.cloud.trade.framework.order.config.TradeOrderProperties; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +@Mapper +public interface AfterSaleConvert { + + AfterSaleConvert INSTANCE = Mappers.getMapper(AfterSaleConvert.class); + + @Mappings({ + @Mapping(target = "id", ignore = true), + @Mapping(target = "createTime", ignore = true), + @Mapping(target = "updateTime", ignore = true), + @Mapping(target = "creator", ignore = true), + @Mapping(target = "updater", ignore = true), + }) + AfterSaleDO convert(AppAfterSaleCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItem); + + @Mappings({ + @Mapping(source = "afterSale.orderId", target = "merchantOrderId"), + @Mapping(source = "afterSale.id", target = "merchantRefundId"), + @Mapping(source = "afterSale.applyReason", target = "reason"), + @Mapping(source = "afterSale.refundPrice", target = "price"), + @Mapping(source = "orderProperties.payAppKey", target = "appKey") + }) + PayRefundCreateReqDTO convert(String userIp, AfterSaleDO afterSale, TradeOrderProperties orderProperties); + + MemberUserRespVO convert(MemberUserRespDTO bean); + + PageResult convertPage(PageResult page); + + default PageResult convertPage(PageResult pageResult, + Map memberUsers) { + PageResult voPageResult = convertPage(pageResult); + // 处ç†ä¼šå‘˜ + voPageResult.getList().forEach(afterSale -> afterSale.setUser( + convert(memberUsers.get(afterSale.getUserId())))); + return voPageResult; + } + + ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean); + + AppAfterSaleRespVO convert(AfterSaleDO bean); + + PageResult convertPage02(PageResult page); + + default AfterSaleDetailRespVO convert(AfterSaleDO afterSale, TradeOrderDO order, TradeOrderItemDO orderItem, + MemberUserRespDTO user, List logs) { + AfterSaleDetailRespVO respVO = convert02(afterSale); + // 处ç†ç”¨æˆ·ä¿¡æ¯ + respVO.setUser(convert(user)); + // 处ç†è®¢å•ä¿¡æ¯ + respVO.setOrder(convert(order)); + respVO.setOrderItem(convert02(orderItem)); + // 处ç†å”®åŽæ—¥å¿— + respVO.setLogs(convertList1(logs)); + return respVO; + } + + List convertList1(List list); + AfterSaleDetailRespVO convert02(AfterSaleDO bean); + AfterSaleDetailRespVO.OrderItem convert02(TradeOrderItemDO bean); + TradeOrderBaseVO convert(TradeOrderDO bean); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/aftersale/AfterSaleLogConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/aftersale/AfterSaleLogConvert.java new file mode 100644 index 0000000..2beeb60 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/aftersale/AfterSaleLogConvert.java @@ -0,0 +1,15 @@ +package com.tashow.cloud.trade.convert.aftersale; + +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleLogDO; +import com.tashow.cloud.trade.service.aftersale.bo.AfterSaleLogCreateReqBO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface AfterSaleLogConvert { + + AfterSaleLogConvert INSTANCE = Mappers.getMapper(AfterSaleLogConvert.class); + + AfterSaleLogDO convert(AfterSaleLogCreateReqBO bean); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageRecordConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageRecordConvert.java new file mode 100644 index 0000000..cd913b0 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageRecordConvert.java @@ -0,0 +1,79 @@ +package com.tashow.cloud.trade.convert.brokerage; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.record.BrokerageRecordRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByPriceRespVO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageRecordDO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 佣金记录 Convert + * + * @author owen + */ +@Mapper +public interface BrokerageRecordConvert { + + BrokerageRecordConvert INSTANCE = Mappers.getMapper(BrokerageRecordConvert.class); + + BrokerageRecordRespVO convert(BrokerageRecordDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + default BrokerageRecordDO convert(BrokerageUserDO user, BrokerageRecordBizTypeEnum bizType, String bizId, + Integer brokerageFrozenDays, int brokeragePrice, LocalDateTime unfreezeTime, + String title, Long sourceUserId, Integer sourceUserLevel) { + brokerageFrozenDays = ObjectUtil.defaultIfNull(brokerageFrozenDays, 0); + // ä¸å†»ç»“æ—¶ï¼Œä½£é‡‘ç›´æŽ¥å°±æ˜¯ç»“ç®—çŠ¶æ€ + Integer status = brokerageFrozenDays > 0 + ? BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus() + : BrokerageRecordStatusEnum.SETTLEMENT.getStatus(); + return new BrokerageRecordDO().setUserId(user.getId()) + .setBizType(bizType.getType()).setBizId(bizId) + .setPrice(brokeragePrice).setTotalPrice(user.getBrokeragePrice()) + .setTitle(title) + .setDescription(StrUtil.format(bizType.getDescription(), MoneyUtils.fenToYuanStr(Math.abs(brokeragePrice)))) + .setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime) + .setSourceUserLevel(sourceUserLevel).setSourceUserId(sourceUserId); + } + + default PageResult convertPage(PageResult pageResult, Map userMap) { + PageResult result = convertPage(pageResult); + for (BrokerageRecordRespVO respVO : result.getList()) { + Optional.ofNullable(userMap.get(respVO.getUserId())).ifPresent(user -> + respVO.setUserNickname(user.getNickname()).setUserAvatar(user.getAvatar())); + Optional.ofNullable(userMap.get(respVO.getSourceUserId())).ifPresent(user -> + respVO.setSourceUserNickname(user.getNickname()).setSourceUserAvatar(user.getAvatar())); + } + return result; + } + + BrokerageRecordPageReqVO convert(AppBrokerageRecordPageReqVO pageReqVO, Long userId); + + default PageResult convertPage03(PageResult pageResult, Map userMap) { + for (AppBrokerageUserRankByPriceRespVO vo : pageResult.getList()) { + copyTo(userMap.get(vo.getId()), vo); + } + return pageResult; + } + + void copyTo(MemberUserRespDTO from, @MappingTarget AppBrokerageUserRankByPriceRespVO to); +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageUserConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageUserConvert.java new file mode 100644 index 0000000..302059c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageUserConvert.java @@ -0,0 +1,99 @@ +package com.tashow.cloud.trade.convert.brokerage; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.user.BrokerageUserRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserMySummaryRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; +import com.tashow.cloud.trade.service.brokerage.bo.UserBrokerageSummaryRespBO; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 分销用户 Convert + * + * @author owen + */ +@Mapper +public interface BrokerageUserConvert { + + BrokerageUserConvert INSTANCE = Mappers.getMapper(BrokerageUserConvert.class); + + BrokerageUserRespVO convert(BrokerageUserDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page, Map userMap, Map brokerageUserCountMap, Map userOrderSummaryMap); + + default PageResult convertPage(PageResult pageResult, + Map userMap, + Map brokerageUserCountMap, + Map userOrderSummaryMap, + Map withdrawMap) { + PageResult result = convertPage(pageResult, userMap, brokerageUserCountMap, userOrderSummaryMap); + for (BrokerageUserRespVO userVO : result.getList()) { + // ç”¨æˆ·ä¿¡æ¯ + copyTo(userMap.get(userVO.getId()), userVO); + // æŽ¨å¹¿ç”¨æˆ·æ•°é‡ + userVO.setBrokerageUserCount(MapUtil.getInt(brokerageUserCountMap, userVO.getId(), 0)); + // æŽ¨å¹¿è®¢å•æ•°é‡ã€æŽ¨å¹¿è®¢å•é‡‘é¢ + Optional orderSummaryOptional = Optional.ofNullable(userOrderSummaryMap.get(userVO.getId())); + userVO.setBrokerageOrderCount(orderSummaryOptional.map(UserBrokerageSummaryRespBO::getCount).orElse(0)) + .setBrokerageOrderPrice(orderSummaryOptional.map(UserBrokerageSummaryRespBO::getPrice).orElse(0)); + // å·²æçŽ°æ¬¡æ•°ã€å·²æçŽ°é‡‘é¢ + Optional withdrawSummaryOptional = Optional.ofNullable(withdrawMap.get(userVO.getId())); + userVO.setWithdrawCount(withdrawSummaryOptional.map(BrokerageWithdrawSummaryRespBO::getCount).orElse(0)) + .setWithdrawPrice(withdrawSummaryOptional.map(BrokerageWithdrawSummaryRespBO::getPrice).orElse(0)); + } + return result; + } + + default BrokerageUserRespVO copyTo(MemberUserRespDTO source, BrokerageUserRespVO target) { + if (target == null) { + return null; + } + Optional.ofNullable(source).ifPresent( + user -> target.setNickname(user.getNickname()).setAvatar(user.getAvatar())); + return target; + } + + default PageResult convertPage03(PageResult pageResult, + Map userMap) { + pageResult.getList().forEach(vo -> copyTo(userMap.get(vo.getId()), vo)); + return pageResult; + } + + void copyTo(MemberUserRespDTO from, @MappingTarget AppBrokerageUserRankByUserCountRespVO to); + + default AppBrokerageUserMySummaryRespVO convert(Integer yesterdayPrice, Integer withdrawPrice, + Long firstBrokerageUserCount, Long secondBrokerageUserCount, + BrokerageUserDO brokerageUser) { + AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO() + .setYesterdayPrice(ObjUtil.defaultIfNull(yesterdayPrice, 0)) + .setWithdrawPrice(ObjUtil.defaultIfNull(withdrawPrice, 0)) + .setBrokeragePrice(0).setFrozenPrice(0) + .setFirstBrokerageUserCount(ObjUtil.defaultIfNull(firstBrokerageUserCount, 0L)) + .setSecondBrokerageUserCount(ObjUtil.defaultIfNull(secondBrokerageUserCount, 0L)); + // 设置 brokeragePriceã€frozenPrice 字段 + Optional.ofNullable(brokerageUser) + .ifPresent(user -> respVO.setBrokeragePrice(user.getBrokeragePrice()).setFrozenPrice(user.getFrozenPrice())); + return respVO; + } + + default void copyTo(List list, Map userMap) { + for (AppBrokerageUserChildSummaryRespVO vo : list) { + Optional.ofNullable(userMap.get(vo.getId())).ifPresent(user -> + vo.setNickname(user.getNickname()).setAvatar(user.getAvatar())); + } + } +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageWithdrawConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageWithdrawConvert.java new file mode 100644 index 0000000..ed66039 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/brokerage/BrokerageWithdrawConvert.java @@ -0,0 +1,57 @@ +package com.tashow.cloud.trade.convert.brokerage; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawRespVO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; +import cn.iocoder.yudao.module.trade.enums.DictTypeConstants; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 佣金æçް Convert + * + * @author èŠ‹é“æºç  + */ +@Mapper +public interface BrokerageWithdrawConvert { + + BrokerageWithdrawConvert INSTANCE = Mappers.getMapper(BrokerageWithdrawConvert.class); + + BrokerageWithdrawDO convert(AppBrokerageWithdrawCreateReqVO createReqVO, Long userId, Integer feePrice); + + BrokerageWithdrawRespVO convert(BrokerageWithdrawDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + default PageResult convertPage(PageResult pageResult, Map userMap) { + PageResult result = convertPage(pageResult); + for (BrokerageWithdrawRespVO vo : result.getList()) { + vo.setUserNickname(Optional.ofNullable(userMap.get(vo.getUserId())).map(MemberUserRespDTO::getNickname).orElse(null)); + } + return result; + } + + PageResult convertPage02(PageResult pageResult); + + default PageResult convertPage03(PageResult pageResult) { + PageResult result = convertPage02(pageResult); + for (AppBrokerageWithdrawRespVO vo : result.getList()) { + vo.setStatusName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.BROKERAGE_WITHDRAW_STATUS, vo.getStatus())); + } + return result; + } + + BrokerageWithdrawPageReqVO convert(AppBrokerageWithdrawPageReqVO pageReqVO, Long userId); +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/cart/TradeCartConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/cart/TradeCartConvert.java new file mode 100644 index 0000000..99a9b33 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/cart/TradeCartConvert.java @@ -0,0 +1,53 @@ +package com.tashow.cloud.trade.convert.cart; + +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; +import com.tashow.cloud.trade.controller.app.base.sku.AppProductSkuBaseRespVO; +import com.tashow.cloud.trade.controller.app.base.spu.AppProductSpuBaseRespVO; +import com.tashow.cloud.trade.controller.app.cart.vo.AppCartListRespVO; +import com.tashow.cloud.trade.dal.dataobject.cart.CartDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +@Mapper +public interface TradeCartConvert { + + TradeCartConvert INSTANCE = Mappers.getMapper(TradeCartConvert.class); + + default AppCartListRespVO convertList(List carts, + List spus, List skus) { + Map spuMap = convertMap(spus, ProductSpuRespDTO::getId); + Map skuMap = convertMap(skus, ProductSkuRespDTO::getId); + // éåŽ†ï¼Œå¼€å§‹è½¬æ¢ + List validList = new ArrayList<>(carts.size()); + List invalidList = new ArrayList<>(); + carts.forEach(cart -> { + AppCartListRespVO.Cart cartVO = new AppCartListRespVO.Cart(); + cartVO.setId(cart.getId()).setCount(cart.getCount()).setSelected(cart.getSelected()); + ProductSpuRespDTO spu = spuMap.get(cart.getSpuId()); + ProductSkuRespDTO sku = skuMap.get(cart.getSkuId()); + cartVO.setSpu(convert(spu)).setSku(convert(sku)); + // 如果 SPU ä¸å­˜åœ¨ï¼Œæˆ–者下架,或者库存ä¸è¶³ï¼Œè¯´æ˜Žæ˜¯æ— æ•ˆçš„ + if (spu == null + || !ProductSpuStatusEnum.isEnable(spu.getStatus()) + || spu.getStock() <= 0) { + cartVO.setSelected(false); // 强制设置æˆä¸å¯é€‰ä¸­ + invalidList.add(cartVO); + } else { + // 虽然 SKU å¯èƒ½ä¹Ÿä¼šä¸å­˜åœ¨ï¼Œä½†æ˜¯å¯ä»¥é€šè¿‡è´­ç‰©è½¦é‡æ–°é€‰æ‹© + validList.add(cartVO); + } + }); + return new AppCartListRespVO().setValidList(validList).setInvalidList(invalidList); + } + AppProductSpuBaseRespVO convert(ProductSpuRespDTO spu); + AppProductSkuBaseRespVO convert(ProductSkuRespDTO sku); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/config/TradeConfigConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/config/TradeConfigConvert.java new file mode 100644 index 0000000..a21589f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/config/TradeConfigConvert.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.trade.convert.config; + +import com.tashow.cloud.trade.controller.admin.config.vo.TradeConfigRespVO; +import com.tashow.cloud.trade.controller.admin.config.vo.TradeConfigSaveReqVO; +import com.tashow.cloud.trade.controller.app.config.vo.AppTradeConfigRespVO; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 交易中心é…ç½® Convert + * + * @author owen + */ +@Mapper +public interface TradeConfigConvert { + + TradeConfigConvert INSTANCE = Mappers.getMapper(TradeConfigConvert.class); + + TradeConfigDO convert(TradeConfigSaveReqVO bean); + + TradeConfigRespVO convert(TradeConfigDO bean); + + AppTradeConfigRespVO convert02(TradeConfigDO tradeConfig); +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryExpressConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryExpressConvert.java new file mode 100644 index 0000000..81c10ce --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryExpressConvert.java @@ -0,0 +1,34 @@ +package com.tashow.cloud.trade.convert.delivery; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.*; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.*; +import com.tashow.cloud.trade.controller.app.delivery.vo.express.AppDeliveryExpressRespVO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface DeliveryExpressConvert { + + DeliveryExpressConvert INSTANCE = Mappers.getMapper(DeliveryExpressConvert.class); + + DeliveryExpressDO convert(DeliveryExpressCreateReqVO bean); + + DeliveryExpressDO convert(DeliveryExpressUpdateReqVO bean); + + DeliveryExpressRespVO convert(DeliveryExpressDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + List convertList02(List list); + + List convertList1(List list); + + List convertList03(List list); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryExpressTemplateConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryExpressTemplateConvert.java new file mode 100644 index 0000000..02b20be --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryExpressTemplateConvert.java @@ -0,0 +1,94 @@ +package com.tashow.cloud.trade.convert.delivery; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.*; +import com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate.*; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; +import com.tashow.cloud.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 { + + DeliveryExpressTemplateConvert INSTANCE = Mappers.getMapper(DeliveryExpressTemplateConvert.class); + + // ========== Template ========== + + DeliveryExpressTemplateDO convert(DeliveryExpressTemplateCreateReqVO bean); + + DeliveryExpressTemplateDO convert(DeliveryExpressTemplateUpdateReqVO bean); + + DeliveryExpressTemplateRespVO convert(DeliveryExpressTemplateDO bean); + + DeliveryExpressTemplateDetailRespVO convert2(DeliveryExpressTemplateDO bean); + + List convertList(List list); + + List convertList1(List list); + + PageResult convertPage(PageResult page); + + default DeliveryExpressTemplateDetailRespVO convert(DeliveryExpressTemplateDO bean, + List chargeList, + List freeList) { + DeliveryExpressTemplateDetailRespVO respVO = convert2(bean); + respVO.setCharges(convertTemplateChargeList(chargeList)); + respVO.setFrees(convertTemplateFreeList(freeList)); + return respVO; + } + + // ========== Template Charge ========== + + DeliveryExpressTemplateChargeDO convertTemplateCharge(Long templateId, Integer chargeMode, DeliveryExpressTemplateChargeBaseVO vo); + + DeliveryExpressTemplateRespBO.Charge convertTemplateCharge(DeliveryExpressTemplateChargeDO bean); + + default List convertTemplateChargeList(Long templateId, Integer chargeMode, List list) { + return CollectionUtils.convertList(list, vo -> convertTemplateCharge(templateId, chargeMode, vo)); + } + + // ========== Template Free ========== + + DeliveryExpressTemplateFreeDO convertTemplateFree(Long templateId, DeliveryExpressTemplateFreeBaseVO vo); + + DeliveryExpressTemplateRespBO.Free convertTemplateFree(DeliveryExpressTemplateFreeDO bean); + + List convertTemplateChargeList(List list); + + List convertTemplateFreeList(List list); + + default List convertTemplateFreeList(Long templateId, List list) { + return CollectionUtils.convertList(list, vo -> convertTemplateFree(templateId, vo)); + } + + default Map convertMap(Integer areaId, List templateList, + List chargeList, + List freeList) { + Map> templateIdChargeMap = convertMultiMap(chargeList, + DeliveryExpressTemplateChargeDO::getTemplateId); + Map> templateIdFreeMap = convertMultiMap(freeList, + DeliveryExpressTemplateFreeDO::getTemplateId); + // 组åˆè¿è´¹æ¨¡æ¿é…ç½® RespBO + Map 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; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryPickUpStoreConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryPickUpStoreConvert.java new file mode 100644 index 0000000..3b6b841 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/delivery/DeliveryPickUpStoreConvert.java @@ -0,0 +1,55 @@ +package com.tashow.cloud.trade.convert.delivery; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreCreateReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreRespVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreSimpleRespVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreUpdateReqVO; +import com.tashow.cloud.trade.controller.app.delivery.vo.pickup.AppDeliveryPickUpStoreRespVO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface DeliveryPickUpStoreConvert { + + DeliveryPickUpStoreConvert INSTANCE = Mappers.getMapper(DeliveryPickUpStoreConvert.class); + + DeliveryPickUpStoreDO convert(DeliveryPickUpStoreCreateReqVO bean); + + DeliveryPickUpStoreDO convert(DeliveryPickUpStoreUpdateReqVO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + List convertList1(List list); + @Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName") + DeliveryPickUpStoreSimpleRespVO convert02(DeliveryPickUpStoreDO bean); + + @Named("convertAreaIdToAreaName") + default String convertAreaIdToAreaName(Integer areaId) { + return AreaUtils.format(areaId); + } + + default List convertList(List list, + Double latitude, Double longitude) { + return CollectionUtils.convertList(list, store -> { + AppDeliveryPickUpStoreRespVO storeVO = convert03(store); + if (latitude != null && longitude != null) { + storeVO.setDistance(NumberUtils.getDistance(latitude, longitude, storeVO.getLatitude(), storeVO.getLongitude())); + } + return storeVO; + }); + } + @Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName") + AppDeliveryPickUpStoreRespVO convert03(DeliveryPickUpStoreDO bean); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/order/TradeOrderConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/order/TradeOrderConvert.java new file mode 100644 index 0000000..160b0de --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/order/TradeOrderConvert.java @@ -0,0 +1,291 @@ +package com.tashow.cloud.trade.convert.order; + +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; +import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; +import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; +import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; +import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO; +import com.tashow.cloud.trade.controller.admin.base.member.user.MemberUserRespVO; +import com.tashow.cloud.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; +import cn.iocoder.yudao.module.trade.controller.admin.order.vo.*; +import com.tashow.cloud.trade.controller.admin.order.vo.*; +import com.tashow.cloud.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; +import cn.iocoder.yudao.module.trade.controller.app.order.vo.*; +import com.tashow.cloud.trade.controller.app.order.vo.*; +import com.tashow.cloud.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; +import com.tashow.cloud.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; +import com.tashow.cloud.trade.dal.dataobject.cart.CartDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderLogDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; +import com.tashow.cloud.trade.framework.order.config.TradeOrderProperties; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageAddReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; + +@Mapper +public interface TradeOrderConvert { + + TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class); + + @Mappings({ + @Mapping(target = "id", ignore = true), + @Mapping(source = "userId", target = "userId"), + @Mapping(source = "createReqVO.couponId", target = "couponId"), + @Mapping(target = "remark", ignore = true), + @Mapping(source = "createReqVO.remark", target = "userRemark"), + @Mapping(source = "calculateRespBO.price.totalPrice", target = "totalPrice"), + @Mapping(source = "calculateRespBO.price.discountPrice", target = "discountPrice"), + @Mapping(source = "calculateRespBO.price.deliveryPrice", target = "deliveryPrice"), + @Mapping(source = "calculateRespBO.price.couponPrice", target = "couponPrice"), + @Mapping(source = "calculateRespBO.price.pointPrice", target = "pointPrice"), + @Mapping(source = "calculateRespBO.price.vipPrice", target = "vipPrice"), + @Mapping(source = "calculateRespBO.price.payPrice", target = "payPrice") + }) + TradeOrderDO convert(Long userId, AppTradeOrderCreateReqVO createReqVO, TradePriceCalculateRespBO calculateRespBO); + + TradeOrderRespDTO convert(TradeOrderDO orderDO); + + default List convertList(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) { + return CollectionUtils.convertList(calculateRespBO.getItems(), item -> { + TradeOrderItemDO orderItem = convert(item); + orderItem.setOrderId(tradeOrderDO.getId()); + orderItem.setUserId(tradeOrderDO.getUserId()); + orderItem.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); + orderItem.setCommentStatus(false); + return orderItem; + }); + } + + TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item); + + default ProductSkuUpdateStockReqDTO convert(List list) { + List items = CollectionUtils.convertList(list, item -> + new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(item.getCount())); + return new ProductSkuUpdateStockReqDTO(items); + } + + default ProductSkuUpdateStockReqDTO convertNegative(List list) { + List items = CollectionUtils.convertList(list, item -> + new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(-item.getCount())); + return new ProductSkuUpdateStockReqDTO(items); + } + + default PayOrderCreateReqDTO convert(TradeOrderDO order, List orderItems, + TradeOrderProperties orderProperties) { + PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO() + .setAppKey(orderProperties.getPayAppKey()).setUserIp(order.getUserIp()); + // 商户相关字段 + createReqDTO.setMerchantOrderId(String.valueOf(order.getId())); + String subject = orderItems.get(0).getSpuName(); + subject = StrUtils.maxLength(subject, PayOrderCreateReqDTO.SUBJECT_MAX_LENGTH); // é¿å…超过 32 ä½ + createReqDTO.setSubject(subject); + createReqDTO.setBody(subject); // TODO 芋艿:临时写死 + // 订å•相关字段 + createReqDTO.setPrice(order.getPayPrice()).setExpireTime(addTime(orderProperties.getPayExpireTime())); + return createReqDTO; + } + + default PageResult convertPage(PageResult pageResult, + List orderItems, + Map memberUserMap) { + Map> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId); + // 转化 List + List orderVOs = CollectionUtils.convertList(pageResult.getList(), order -> { + List xOrderItems = orderItemMap.get(order.getId()); + TradeOrderPageItemRespVO orderVO = convert(order, xOrderItems); + // å¤„ç†æ”¶è´§åœ°å€ + orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId())); + // å¢žåŠ ç”¨æˆ·ä¿¡æ¯ + orderVO.setUser(convertUser(memberUserMap.get(orderVO.getUserId()))); + // å¢žåŠ æŽ¨å¹¿äººä¿¡æ¯ + orderVO.setBrokerageUser(convertUser(memberUserMap.get(orderVO.getBrokerageUserId()))); + return orderVO; + }); + return new PageResult<>(orderVOs, pageResult.getTotal()); + } + + MemberUserRespVO convertUser(MemberUserRespDTO memberUserRespDTO); + + TradeOrderPageItemRespVO convert(TradeOrderDO order, List items); + + ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean); + + default TradeOrderDetailRespVO convert(TradeOrderDO order, List orderItems, + List orderLogs, + MemberUserRespDTO user, MemberUserRespDTO brokerageUser) { + TradeOrderDetailRespVO orderVO = convert2(order, orderItems); + // å¤„ç†æ”¶è´§åœ°å€ + orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId())); + // 处ç†ç”¨æˆ·ä¿¡æ¯ + orderVO.setUser(convert(user)); + orderVO.setBrokerageUser(convert(brokerageUser)); + // å¤„ç†æ—¥å¿— + orderVO.setLogs(convertList03(orderLogs)); + return orderVO; + } + List convertList03(List orderLogs); + + TradeOrderDetailRespVO convert2(TradeOrderDO order, List items); + + MemberUserRespVO convert(MemberUserRespDTO bean); + + default PageResult convertPage02(PageResult pageResult, + List orderItems) { + Map> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId); + // 转化 List + List orderVOs = CollectionUtils.convertList(pageResult.getList(), order -> { + List xOrderItems = orderItemMap.get(order.getId()); + return convert02(order, xOrderItems); + }); + return new PageResult<>(orderVOs, pageResult.getTotal()); + } + + AppTradeOrderPageItemRespVO convert02(TradeOrderDO order, List items); + + AppProductPropertyValueDetailRespVO convert02(ProductPropertyValueDetailRespDTO bean); + + default AppTradeOrderDetailRespVO convert02(TradeOrderDO order, List orderItems, + TradeOrderProperties tradeOrderProperties, + DeliveryExpressDO express) { + AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems); + orderVO.setPayExpireTime(order.getCreateTime().plus(tradeOrderProperties.getPayExpireTime())); + if (StrUtil.isNotEmpty(order.getPayChannelCode())) { + orderVO.setPayChannelName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.CHANNEL_CODE, order.getPayChannelCode())); + } + // å¤„ç†æ”¶è´§åœ°å€ + orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId())); + if (express != null) { + orderVO.setLogisticsId(express.getId()).setLogisticsName(express.getName()); + } + return orderVO; + } + + AppTradeOrderDetailRespVO convert3(TradeOrderDO order, List items); + + AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean); + + @Mappings({ + @Mapping(target = "skuId", source = "tradeOrderItemDO.skuId"), + @Mapping(target = "orderId", source = "tradeOrderItemDO.orderId"), + @Mapping(target = "orderItemId", source = "tradeOrderItemDO.id"), + @Mapping(target = "descriptionScores", source = "createReqVO.descriptionScores"), + @Mapping(target = "benefitScores", source = "createReqVO.benefitScores"), + @Mapping(target = "content", source = "createReqVO.content"), + @Mapping(target = "picUrls", source = "createReqVO.picUrls"), + @Mapping(target = "anonymous", source = "createReqVO.anonymous"), + @Mapping(target = "userId", source = "tradeOrderItemDO.userId") + }) + ProductCommentCreateReqDTO convert04(AppTradeOrderItemCommentCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItemDO); + + TradePriceCalculateReqBO convert(AppTradeOrderSettlementReqVO settlementReqVO); + + default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO, + List cartList) { + TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId) + .setItems(new ArrayList<>(settlementReqVO.getItems().size())) + .setCouponId(settlementReqVO.getCouponId()).setPointStatus(settlementReqVO.getPointStatus()) + // 物æµä¿¡æ¯ + .setDeliveryType(settlementReqVO.getDeliveryType()).setAddressId(settlementReqVO.getAddressId()) + .setPickUpStoreId(settlementReqVO.getPickUpStoreId()) + // å„ç§æ´»åЍ + .setSeckillActivityId(settlementReqVO.getSeckillActivityId()) + .setBargainRecordId(settlementReqVO.getBargainRecordId()) + .setCombinationActivityId(settlementReqVO.getCombinationActivityId()) + .setCombinationHeadId(settlementReqVO.getCombinationHeadId()) + .setPointActivityId(settlementReqVO.getPointActivityId()); + // 商å“项的构建 + Map cartMap = convertMap(cartList, CartDO::getId); + for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) { + // 情况一:skuId + count + if (item.getSkuId() != null) { + reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(item.getSkuId()).setCount(item.getCount()) + .setSelected(true)); // true 的原因,下å•一定选中 + continue; + } + // 情况二:cartId + CartDO cart = cartMap.get(item.getCartId()); + if (cart == null) { + continue; + } + reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(cart.getSkuId()).setCount(cart.getCount()) + .setCartId(item.getCartId()).setSelected(true)); // true 的原因,下å•一定选中 + } + return reqBO; + } + + default AppTradeOrderSettlementRespVO convert(TradePriceCalculateRespBO calculate, MemberAddressRespDTO address) { + AppTradeOrderSettlementRespVO respVO = convert0(calculate, address); + if (address != null) { + respVO.getAddress().setAreaName(AreaUtils.format(address.getAreaId())); + } + return respVO; + } + + AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, MemberAddressRespDTO address); + + List convertList02(List list); + + TradeOrderDO convert(TradeOrderUpdateAddressReqVO reqVO); + + TradeOrderDO convert(TradeOrderUpdatePriceReqVO reqVO); + + TradeOrderDO convert(TradeOrderRemarkReqVO reqVO); + + default BrokerageAddReqBO convert(MemberUserRespDTO user, TradeOrderItemDO item, + ProductSpuRespDTO spu, ProductSkuRespDTO sku) { + BrokerageAddReqBO bo = new BrokerageAddReqBO().setBizId(String.valueOf(item.getId())).setSourceUserId(item.getUserId()) + .setBasePrice(item.getPayPrice()) + .setTitle(StrUtil.format("{}æˆåŠŸè´­ä¹°{}", user.getNickname(), item.getSpuName())) + .setFirstFixedPrice(0).setSecondFixedPrice(0); + if (BooleanUtil.isTrue(spu.getSubCommissionType())) { + bo.setFirstFixedPrice(sku.getFirstBrokeragePrice()).setSecondFixedPrice(sku.getSecondBrokeragePrice()); + } + return bo; + } + + @Named("convertList04") + List convertList04(List list); + + @Mappings({ + @Mapping(target = "activityId", source = "order.combinationActivityId"), + @Mapping(target = "spuId", source = "item.spuId"), + @Mapping(target = "skuId", source = "item.skuId"), + @Mapping(target = "count", source = "item.count"), + @Mapping(target = "orderId", source = "order.id"), + @Mapping(target = "userId", source = "order.userId"), + @Mapping(target = "headId", source = "order.combinationHeadId"), + @Mapping(target = "combinationPrice", source = "item.payPrice"), + }) + CombinationRecordCreateReqDTO convert(TradeOrderDO order, TradeOrderItemDO item); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/order/TradeOrderLogConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/order/TradeOrderLogConvert.java new file mode 100644 index 0000000..dfe7aee --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/convert/order/TradeOrderLogConvert.java @@ -0,0 +1,15 @@ +package com.tashow.cloud.trade.convert.order; + +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderLogDO; +import com.tashow.cloud.trade.service.order.bo.TradeOrderLogCreateReqBO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface TradeOrderLogConvert { + + TradeOrderLogConvert INSTANCE = Mappers.getMapper(TradeOrderLogConvert.class); + + TradeOrderLogDO convert(TradeOrderLogCreateReqBO bean); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/aftersale/AfterSaleDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/aftersale/AfterSaleDO.java new file mode 100644 index 0000000..52bbb34 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/aftersale/AfterSaleDO.java @@ -0,0 +1,203 @@ +package com.tashow.cloud.trade.dal.dataobject.aftersale; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleTypeEnum; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * å”®åŽè®¢å•ï¼Œç”¨äºŽå¤„ç† {@link TradeOrderDO} 交易订å•的退款退货æµç¨‹ + * + * @author èŠ‹é“æºç  + */ +@TableName(value = "trade_after_sale", autoResultMap = true) +@KeySequence("trade_after_sale_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class AfterSaleDO extends BaseDO { + + /** + * å”®åŽç¼–å·ï¼Œä¸»é”®è‡ªå¢ž + */ + private Long id; + /** + * å”®åŽå•å· + * + * 例如说,1146347329394184195 + */ + private String no; + /** + * é€€æ¬¾çŠ¶æ€ + * + * 枚举 {@link AfterSaleStatusEnum} + */ + private Integer status; + /** + * å”®åŽæ–¹å¼ + * + * 枚举 {@link AfterSaleWayEnum} + */ + private Integer way; + /** + * å”®åŽç±»åž‹ + * + * 枚举 {@link AfterSaleTypeEnum} + */ + private Integer type; + /** + * ç”¨æˆ·ç¼–å· + * + * å…³è” MemberUserDO çš„ id ç¼–å· + */ + private Long userId; + /** + * 申请原因 + * + * type = 退款,对应 trade_after_sale_refund_reason 类型 + * type = 退货退款,对应 trade_after_sale_refund_and_return_reason 类型 + */ + private String applyReason; + /** + * 补充æè¿° + */ + private String applyDescription; + /** + * 补充凭è¯å›¾ç‰‡ + * + * 数组,以逗å·åˆ†éš” + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List applyPicUrls; + + // ========== 交易订å•相关 ========== + /** + * 交易订å•ç¼–å· + * + * å…³è” {@link TradeOrderDO#getId()} + */ + private Long orderId; + /** + * è®¢å•æµæ°´å· + * + * 冗余 {@link TradeOrderDO#getNo()} + */ + private String orderNo; + /** + * 交易订å•é¡¹ç¼–å· + * + * å…³è” {@link TradeOrderItemDO#getId()} + */ + private Long orderItemId; + /** + * å•†å“ SPU ç¼–å· + * + * å…³è” ProductSpuDO çš„ id 字段 + * 冗余 {@link TradeOrderItemDO#getSpuId()} + */ + private Long spuId; + /** + * å•†å“ SPU åç§° + * + * å…³è” ProductSkuDO çš„ name 字段 + * 冗余 {@link TradeOrderItemDO#getSpuName()} + */ + private String spuName; + /** + * å•†å“ SKU ç¼–å· + * + * å…³è” ProductSkuDO çš„ç¼–å· + */ + private Long skuId; + /** + * 属性数组,JSON æ ¼å¼ + * + * 冗余 {@link TradeOrderItemDO#getProperties()} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List properties; + /** + * 商å“图片 + * + * 冗余 {@link TradeOrderItemDO#getPicUrl()} + */ + private String picUrl; + /** + * é€€è´§å•†å“æ•°é‡ + */ + private Integer count; + + // ========== 审批相关 ========== + + /** + * 审批时间 + */ + private LocalDateTime auditTime; + /** + * 审批人 + * + * å…³è” AdminUserDO çš„ id ç¼–å· + */ + private Long auditUserId; + /** + * 审批备注 + * + * 注æ„ï¼Œåªæœ‰å®¡æ‰¹ä¸é€šè¿‡æ‰ä¼šå¡«å†™ + */ + private String auditReason; + + // ========== 退款相关 ========== + /** + * 退款金é¢ï¼Œå•ä½ï¼šåˆ†ã€‚ + */ + private Integer refundPrice; + /** + * æ”¯ä»˜é€€æ¬¾ç¼–å· + * + * 对接 pay-module-biz 支付æœåŠ¡çš„é€€æ¬¾è®¢å•ç¼–å·ï¼Œå³ PayRefundDO çš„ id ç¼–å· + */ + private Long payRefundId; + /** + * 退款时间 + */ + private LocalDateTime refundTime; + + // ========== 退货相关 ========== + /** + * 退货物æµå…¬å¸ç¼–å· + * + * å…³è” LogisticsDO çš„ id ç¼–å· + */ + private Long logisticsId; + /** + * 退货物æµå•å· + */ + private String logisticsNo; + /** + * 退货时间 + */ + private LocalDateTime deliveryTime; + /** + * æ”¶è´§æ—¶é—´ + */ + private LocalDateTime receiveTime; + /** + * 收货备注 + * + * 注æ„ï¼Œåªæœ‰æ‹’ç»æ”¶è´§æ‰ä¼šå¡«å†™ + */ + private String receiveReason; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/aftersale/AfterSaleLogDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/aftersale/AfterSaleLogDO.java new file mode 100644 index 0000000..e52ef02 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/aftersale/AfterSaleLogDO.java @@ -0,0 +1,71 @@ +package com.tashow.cloud.trade.dal.dataobject.aftersale; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * äº¤æ˜“å”®åŽæ—¥å¿— DO + * + * @author èŠ‹é“æºç  + */ +@TableName("trade_after_sale_log") +@KeySequence("trade_after_sale_log_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AfterSaleLogDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + /** + * ç”¨æˆ·ç¼–å· + * + * å…³è” 1:AdminUserDO çš„ id 字段 + * å…³è” 2:MemberUserDO çš„ id 字段 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + + /** + * å”®åŽç¼–å· + * + * å…³è” {@link AfterSaleDO#getId()} + */ + private Long afterSaleId; + /** + * æ“作å‰çŠ¶æ€ + */ + private Integer beforeStatus; + /** + * æ“作åŽçŠ¶æ€ + */ + private Integer afterStatus; + + /** + * æ“作类型 + * + * 枚举 {@link AfterSaleOperateTypeEnum} + */ + private Integer operateType; + /** + * æ“作明细 + */ + private String content; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageRecordDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageRecordDO.java new file mode 100644 index 0000000..415b9d2 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageRecordDO.java @@ -0,0 +1,97 @@ +package com.tashow.cloud.trade.dal.dataobject.brokerage; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 佣金记录 DO + * + * @author owen + */ +@TableName("trade_brokerage_record") +@KeySequence("trade_brokerage_record_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BrokerageRecordDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Integer id; + /** + * ç”¨æˆ·ç¼–å· + *

+ * å…³è” MemberUserDO.id + */ + private Long userId; + /** + * ä¸šåŠ¡ç¼–å· + */ + private String bizId; + /** + * 业务类型 + *

+ * 枚举 {@link BrokerageRecordBizTypeEnum} + */ + private Integer bizType; + + /** + * 标题 + */ + private String title; + /** + * 说明 + */ + private String description; + + /** + * é‡‘é¢ + */ + private Integer price; + /** + * 当剿€»ä½£é‡‘ + */ + private Integer totalPrice; + + /** + * çŠ¶æ€ + *

+ * 枚举 {@link BrokerageRecordStatusEnum} + */ + private Integer status; + + /** + * 冻结时间(天) + */ + private Integer frozenDays; + /** + * 解冻时间 + */ + private LocalDateTime unfreezeTime; + + /** + * æ¥æºç”¨æˆ·ç­‰çº§ + *

+ * 被推广用户和 {@link #userId} 的推广层级关系 + */ + private Integer sourceUserLevel; + /** + * æ¥æºç”¨æˆ·ç¼–å· + *

+ * å…³è” MemberUserDO.id å­—æ®µï¼Œè¢«æŽ¨å¹¿ç”¨æˆ·çš„ç¼–å· + */ + private Long sourceUserId; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageUserDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageUserDO.java new file mode 100644 index 0000000..66c6907 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageUserDO.java @@ -0,0 +1,62 @@ +package com.tashow.cloud.trade.dal.dataobject.brokerage; + +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.time.LocalDateTime; + +/** + * 分销用户 DO + * + * @author owen + */ +@TableName("trade_brokerage_user") +@KeySequence("trade_brokerage_user_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BrokerageUserDO extends BaseDO { + + /** + * ç”¨æˆ·ç¼–å· + *

+ * 对应 MemberUserDO çš„ id 字段 + */ + @TableId + private Long id; + + /** + * æŽ¨å¹¿å‘˜ç¼–å· + *

+ * å…³è” MemberUserDO çš„ id 字段 + */ + private Long bindUserId; + /** + * 推广员绑定时间 + */ + private LocalDateTime bindUserTime; + + /** + * æ˜¯å¦æœ‰åˆ†é”€èµ„æ ¼ + */ + private Boolean brokerageEnabled; + /** + * æˆä¸ºåˆ†é”€å‘˜æ—¶é—´ + */ + private LocalDateTime brokerageTime; + + /** + * å¯ç”¨ä½£é‡‘ + */ + private Integer brokeragePrice; + /** + * 冻结佣金 + */ + private Integer frozenPrice; +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageWithdrawDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageWithdrawDO.java new file mode 100644 index 0000000..8e81bc9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/brokerage/BrokerageWithdrawDO.java @@ -0,0 +1,98 @@ +package com.tashow.cloud.trade.dal.dataobject.brokerage; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 佣金æçް DO + * + * @author èŠ‹é“æºç  + */ +@TableName("trade_brokerage_withdraw") +@KeySequence("trade_brokerage_withdraw_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BrokerageWithdrawDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + /** + * ç”¨æˆ·ç¼–å· + * + * å…³è” MemberUserDO çš„ id 字段 + */ + private Long userId; + + /** + * æçް金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer price; + /** + * æçŽ°æ‰‹ç»­è´¹ï¼Œå•ä½ï¼šåˆ† + */ + private Integer feePrice; + /** + * 当剿€»ä½£é‡‘,å•ä½ï¼šåˆ† + */ + private Integer totalPrice; + /** + * æçŽ°ç±»åž‹ + *

+ * 枚举 {@link BrokerageWithdrawTypeEnum} + */ + private Integer type; + + /** + * 真实姓å + */ + private String name; + /** + * è´¦å· + */ + private String accountNo; + /** + * 银行åç§° + */ + private String bankName; + /** + * å¼€æˆ·åœ°å€ + */ + private String bankAddress; + /** + * æ”¶æ¬¾ç  + */ + private String accountQrCodeUrl; + /** + * çŠ¶æ€ + *

+ * 枚举 {@link BrokerageWithdrawStatusEnum} + */ + private Integer status; + /** + * 审核驳回原因 + */ + private String auditReason; + /** + * 审核时间 + */ + private LocalDateTime auditTime; + /** + * 备注 + */ + private String remark; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/cart/CartDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/cart/CartDO.java new file mode 100644 index 0000000..65da533 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/cart/CartDO.java @@ -0,0 +1,61 @@ +package com.tashow.cloud.trade.dal.dataobject.cart; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 购物车的商å“ä¿¡æ¯ DO + * + * æ¯ä¸ªå•†å“,对应一æ¡è®°å½•,通过 {@link #spuId} å’Œ {@link #skuId} å…³è” + * + * @author èŠ‹é“æºç  + */ +@TableName("trade_cart") +@KeySequence("trade_cart_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class CartDO extends BaseDO { + + // ========= 基础字段 BEGIN ========= + + /** + * ç¼–å·ï¼Œå”¯ä¸€è‡ªå¢ž + */ + private Long id; + + /** + * ç”¨æˆ·ç¼–å· + * + * å…³è” MemberUserDO çš„ id ç¼–å· + */ + private Long userId; + + // ========= 商å“ä¿¡æ¯ ========= + + /** + * å•†å“ SPU ç¼–å· + * + * å…³è” ProductSpuDO çš„ id ç¼–å· + */ + private Long spuId; + /** + * å•†å“ SKU ç¼–å· + * + * å…³è” ProductSkuDO çš„ id ç¼–å· + */ + private Long skuId; + /** + * 商å“è´­ä¹°æ•°é‡ + */ + private Integer count; + /** + * 是å¦é€‰ä¸­ + */ + private Boolean selected; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/config/TradeConfigDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/config/TradeConfigDO.java new file mode 100644 index 0000000..061407e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/config/TradeConfigDO.java @@ -0,0 +1,118 @@ +package com.tashow.cloud.trade.dal.dataobject.config; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.util.List; + +/** + * 交易中心é…ç½® DO + * + * @author owen + */ +@TableName(value = "trade_config", autoResultMap = true) +@KeySequence("trade_config_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TradeConfigDO extends BaseDO { + + /** + * 自增主键 + */ + @TableId + private Long id; + + // ========== å”®åŽç›¸å…³ ========== + + /** + * å”®åŽçš„退款ç†ç”± + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List afterSaleRefundReasons; + /** + * å”®åŽçš„退货ç†ç”± + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List afterSaleReturnReasons; + + // ========== é…é€ç›¸å…³ ========== + + /** + * 是å¦å¯ç”¨å…¨åœºåŒ…é‚® + */ + private Boolean deliveryExpressFreeEnabled; + /** + * 全场包邮的最å°é‡‘é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer deliveryExpressFreePrice; + + /** + * 是å¦å¼€å¯è‡ªæ + */ + private Boolean deliveryPickUpEnabled; + + // ========== 分销相关 ========== + + /** + * 是å¦å¯ç”¨åˆ†ä½£ + */ + private Boolean brokerageEnabled; + /** + * åˆ†ä½£æ¨¡å¼ + *

+ * 枚举 {@link BrokerageEnabledConditionEnum 对应的类} + */ + private Integer brokerageEnabledCondition; + /** + * åˆ†é”€å…³ç³»ç»‘å®šæ¨¡å¼ + *

+ * 枚举 {@link BrokerageBindModeEnum 对应的类} + */ + private Integer brokerageBindMode; + /** + * åˆ†é”€æµ·æŠ¥å›¾åœ°å€æ•°ç»„ + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List brokeragePosterUrls; + /** + * 一级返佣比例 + */ + private Integer brokerageFirstPercent; + /** + * 二级返佣比例 + */ + private Integer brokerageSecondPercent; + /** + * 用户æçŽ°æœ€ä½Žé‡‘é¢ + */ + private Integer brokerageWithdrawMinPrice; + /** + * 用户æçŽ°æ‰‹ç»­è´¹ç™¾åˆ†æ¯” + */ + private Integer brokerageWithdrawFeePercent; + /** + * 佣金冻结时间(天) + */ + private Integer brokerageFrozenDays; + /** + * æçŽ°æ–¹å¼ + *

+ * 枚举 {@link BrokerageWithdrawTypeEnum 对应的类} + */ + @TableField(typeHandler = IntegerListTypeHandler.class) + private List brokerageWithdrawTypes; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressDO.java new file mode 100644 index 0000000..0aeaef8 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressDO.java @@ -0,0 +1,60 @@ +package com.tashow.cloud.trade.dal.dataobject.delivery; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.Data; + +/** + * å¿«é€’å…¬å¸ DO + * + * @author jason + */ +@TableName(value ="trade_delivery_express") +@KeySequence("trade_delivery_express_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class DeliveryExpressDO extends BaseDO { + + /** + * ç¼–å·ï¼Œè‡ªå¢ž + */ + @TableId + private Long id; + + /** + * å¿«é€’å…¬å¸ code + */ + private String code; + + /** + * 快递公å¸åç§° + */ + private String name; + + /** + * å¿«é€’å…¬å¸ logo + */ + private String logo; + + /** + * æŽ’åº + */ + private Integer sort; + + /** + * çŠ¶æ€ + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + // TODO 芋艿:c 和结算相关的字段,åŽç»­åœ¨çœ‹ + // partnerId 是å¦éœ€è¦æœˆç»“è´¦å· + // partnerKey 是å¦éœ€è¦æœˆç»“å¯†ç  + // net 是å¦éœ€è¦å–件网店 + // account è´¦å· + // password 网点åç§° + // isShow æ˜¯å¦æ˜¾ç¤º +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateChargeDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateChargeDO.java new file mode 100644 index 0000000..c04f086 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateChargeDO.java @@ -0,0 +1,67 @@ +package com.tashow.cloud.trade.dal.dataobject.delivery; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.List; + +/** + * 快递è¿è´¹æ¨¡æ¿è®¡è´¹é…ç½® DO + * + * @author jason + */ +@TableName(value ="trade_delivery_express_template_charge", autoResultMap = true) +@KeySequence("trade_delivery_express_template_charge_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class DeliveryExpressTemplateChargeDO extends BaseDO { + + /** + * ç¼–å·ï¼Œè‡ªå¢ž + */ + @TableId + private Long id; + + /** + * é…逿¨¡æ¿ç¼–å· + * + * å…³è” {@link DeliveryExpressTemplateDO#getId()} + */ + private Long templateId; + + /** + * é…é€åŒºåŸŸç¼–å·åˆ—表 + */ + @TableField(typeHandler = IntegerListTypeHandler.class) + private List areaIds; + + /** + * é…é€è®¡è´¹æ–¹å¼ + * + * 冗余 {@link DeliveryExpressTemplateDO#getChargeMode()} + */ + private Integer chargeMode; + + /** + * 首件数é‡(ä»¶æ•°,é‡é‡ï¼Œæˆ–体积) + */ + private Double startCount; + /** + * 起步价,å•ä½ï¼šåˆ† + */ + private Integer startPrice; + + /** + * ç»­ä»¶æ•°é‡(ä»¶, é‡é‡ï¼Œæˆ–体积) + */ + private Double extraCount; + /** + * é¢å¤–价,å•ä½ï¼šåˆ† + */ + private Integer extraPrice; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateDO.java new file mode 100644 index 0000000..00d2df5 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateDO.java @@ -0,0 +1,43 @@ +package com.tashow.cloud.trade.dal.dataobject.delivery; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 快递è¿è´¹æ¨¡æ¿ DO + * + * @author jason + */ +@TableName("trade_delivery_express_template") +@KeySequence("trade_delivery_express_template_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class DeliveryExpressTemplateDO extends BaseDO { + + /** + * ç¼–å·ï¼Œè‡ªå¢ž + */ + @TableId + private Long id; + + /** + * 模æ¿åç§° + */ + private String name; + + /** + * é…é€è®¡è´¹æ–¹å¼ + * + * 枚举 {@link DeliveryExpressChargeModeEnum} + */ + private Integer chargeMode; + + /** + * æŽ’åº + */ + private Integer sort; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateFreeDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateFreeDO.java new file mode 100644 index 0000000..e666b97 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryExpressTemplateFreeDO.java @@ -0,0 +1,57 @@ +package com.tashow.cloud.trade.dal.dataobject.delivery; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.List; + +/** + * 快递è¿è´¹æ¨¡æ¿åŒ…é‚®é…ç½® DO + * + * @author jason + */ +@TableName(value ="trade_delivery_express_template_free", autoResultMap = true) +@KeySequence("trade_delivery_express_template_free_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class DeliveryExpressTemplateFreeDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + + /** + * é…逿¨¡æ¿ç¼–å· + * + * å…³è” {@link DeliveryExpressTemplateDO#getId()} + */ + private Long templateId; + + + /** + * é…é€åŒºåŸŸç¼–å·åˆ—表 + */ + @TableField(typeHandler = IntegerListTypeHandler.class) + private List areaIds; + + /** + * 包邮金é¢ï¼Œå•ä½ï¼šåˆ† + * + * è®¢å•æ€»é‡‘é¢ > åŒ…é‚®é‡‘é¢æ—¶ï¼Œæ‰å…è¿è´¹ + */ + private Integer freePrice; + + /** + * 包邮件数 + * + * è®¢å•æ€»ä»¶æ•° > 包邮件数时,æ‰å…è¿è´¹ + */ + private Integer freeCount; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryPickUpStoreDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryPickUpStoreDO.java new file mode 100644 index 0000000..fd29a8f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/delivery/DeliveryPickUpStoreDO.java @@ -0,0 +1,98 @@ +package com.tashow.cloud.trade.dal.dataobject.delivery; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalTime; +import java.util.List; + +/** + * 自æé—¨åº— DO + * + * @author jason + */ +@TableName(value ="trade_delivery_pick_up_store", autoResultMap = true) +@KeySequence("trade_delivery_pick_up_store_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +public class DeliveryPickUpStoreDO extends BaseDO { + + /** + * ç¼–å· + */ + @TableId + private Long id; + + /** + * 门店åç§° + */ + private String name; + + /** + * 门店简介 + */ + private String introduction; + + /** + * 门店手机 + */ + private String phone; + + /** + * åŒºåŸŸç¼–å· + */ + private Integer areaId; + + /** + * é—¨åº—è¯¦ç»†åœ°å€ + */ + private String detailAddress; + + /** + * 门店 logo + */ + private String logo; + + /** + * è¥ä¸šå¼€å§‹æ—¶é—´ + */ + private LocalTime openingTime; + + /** + * è¥ä¸šç»“æŸæ—¶é—´ + */ + private LocalTime closingTime; + + /** + * 纬度 + */ + private Double latitude; + /** + * ç»åº¦ + */ + private Double longitude; + + /** + * æ ¸é”€å‘˜å·¥ç”¨æˆ·ç¼–å·æ•°ç»„ + * + * 订å•è‡ªææ ¸é”€æ—¶ï¼Œåªæœ‰å¯¹åº”门店的店员æ‰èƒ½æ ¸é”€ + * + * å…³è” {@link AdminUserRespDTO#getId()} 管ç†å‘˜ç¼–å· + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List verifyUserIds; + + /** + * é—¨åº—çŠ¶æ€ + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderDO.java new file mode 100644 index 0000000..f81552e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderDO.java @@ -0,0 +1,363 @@ +package com.tashow.cloud.trade.dal.dataobject.order; + +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * äº¤æ˜“è®¢å• DO + * + * @author èŠ‹é“æºç  + */ +@TableName(value = "trade_order", autoResultMap = true) +@KeySequence("trade_order_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TradeOrderDO extends BaseDO { + + /** + * å‘货物æµå…¬å¸ç¼–å· - 空(无需å‘货) + */ + public static final Long LOGISTICS_ID_NULL = 0L; + + // ========== 订å•åŸºæœ¬ä¿¡æ¯ ========== + /** + * 订å•ç¼–å·ï¼Œä¸»é”®è‡ªå¢ž + */ + private Long id; + /** + * è®¢å•æµæ°´å· + * + * 例如说,1146347329394184195 + */ + private String no; + /** + * 订å•类型 + * + * 枚举 {@link TradeOrderTypeEnum} + */ + private Integer type; + /** + * è®¢å•æ¥æº + * + * 枚举 {@link TerminalEnum} + */ + private Integer terminal; + /** + * ç”¨æˆ·ç¼–å· + * + * å…³è” MemberUserDO çš„ id ç¼–å· + */ + private Long userId; + /** + * 用户 IP + */ + private String userIp; + /** + * 用户备注 + */ + private String userRemark; + /** + * 订å•çŠ¶æ€ + * + * 枚举 {@link TradeOrderStatusEnum} + */ + private Integer status; + /** + * è´­ä¹°çš„å•†å“æ•°é‡ + */ + private Integer productCount; + /** + * 订å•å®Œæˆæ—¶é—´ + */ + private LocalDateTime finishTime; + /** + * 订å•å–æ¶ˆæ—¶é—´ + */ + private LocalDateTime cancelTime; + /** + * å–æ¶ˆç±»åž‹ + * + * 枚举 {@link TradeOrderCancelTypeEnum} + */ + private Integer cancelType; + /** + * 商家备注 + */ + private String remark; + /** + * 是å¦è¯„ä»· + * + * true - 已评价 + * false - 未评价 + */ + private Boolean commentStatus; + + /** + * æŽ¨å¹¿äººç¼–å· + * + * å…³è” {@link BrokerageUserDO#getId()} å­—æ®µï¼Œå³ {@link MemberUserRespDTO#getId()} 字段 + */ + private Long brokerageUserId; + + // ========== ä»·æ ¼ + æ”¯ä»˜åŸºæœ¬ä¿¡æ¯ ========== + + // 价格文档 - æ·˜å®ï¼šhttps://open.taobao.com/docV3.htm?docId=108471&docType=1 + // 价格文档 - 京东到家:https://openo2o.jddj.com/api/getApiDetail/182/4d1494c5e7ac4679bfdaaed950c5bc7f.htm + // 价格文档 - 有赞:https://doc.youzanyun.com/detail/API/0/906 + + /** + * 支付订å•ç¼–å· + * + * 对接 pay-module-biz 支付æœåŠ¡çš„æ”¯ä»˜è®¢å•ç¼–å·ï¼Œå³ PayOrderDO çš„ id ç¼–å· + */ + private Long payOrderId; + /** + * 是å¦å·²æ”¯ä»˜ + * + * true - å·²ç»æ”¯ä»˜è¿‡ + * false - 没有支付过 + */ + private Boolean payStatus; + /** + * 付款时间 + */ + private LocalDateTime payTime; + /** + * æ”¯ä»˜æ¸ é“ + * + * 对应 PayChannelEnum 枚举 + */ + private String payChannelCode; + + /** + * 商å“原价,å•ä½ï¼šåˆ† + * + * totalPrice = {@link TradeOrderItemDO#getPrice()} * {@link TradeOrderItemDO#getCount()} 求和 + * + * 对应 taobao çš„ trade.total_fee 字段 + */ + private Integer totalPrice; + /** + * 优惠金é¢ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ order.discount_fee 字段 + */ + private Integer discountPrice; + /** + * è¿è´¹é‡‘é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer deliveryPrice; + /** + * 订å•调价,å•ä½ï¼šåˆ† + * + * 正数,加价;负数,å‡ä»· + */ + private Integer adjustPrice; + /** + * 应付金é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ† + * + * = {@link #totalPrice} + * - {@link #couponPrice} + * - {@link #pointPrice} + * - {@link #discountPrice} + * + {@link #deliveryPrice} + * + {@link #adjustPrice} + * - {@link #vipPrice} + */ + private Integer payPrice; + + // ========== æ”¶ä»¶ + 物æµåŸºæœ¬ä¿¡æ¯ ========== + /** + * é…逿–¹å¼ + * + * 枚举 {@link DeliveryTypeEnum} + */ + private Integer deliveryType; + /** + * å‘货物æµå…¬å¸ç¼–å· + * + * 如果无需å‘货,则 logisticsId 设置为 0ã€‚åŽŸå› æ˜¯ï¼Œä¸æƒ³å†æ·»åŠ é¢å¤–字段 + * + * å…³è” {@link DeliveryExpressDO#getId()} + */ + private Long logisticsId; + /** + * å‘货物æµå•å· + * + * 如果无需å‘货,则 logisticsNo 设置 ""ã€‚åŽŸå› æ˜¯ï¼Œä¸æƒ³å†æ·»åŠ é¢å¤–字段 + */ + private String logisticsNo; + /** + * å‘è´§æ—¶é—´ + */ + private LocalDateTime deliveryTime; + + /** + * æ”¶è´§æ—¶é—´ + */ + private LocalDateTime receiveTime; + /** + * 收件人åç§° + */ + private String receiverName; + /** + * 收件人手机 + */ + private String receiverMobile; + /** + * æ”¶ä»¶äººåœ°åŒºç¼–å· + */ + private Integer receiverAreaId; + /** + * æ”¶ä»¶äººè¯¦ç»†åœ°å€ + */ + private String receiverDetailAddress; + + /** + * 自æé—¨åº—ç¼–å· + * + * å…³è” {@link DeliveryPickUpStoreDO#getId()} + */ + private Long pickUpStoreId; + /** + * è‡ªææ ¸é”€ç  + */ + private String pickUpVerifyCode; + + // ========== å”®åŽåŸºæœ¬ä¿¡æ¯ ========== + /** + * å”®åŽçŠ¶æ€ + * + * 枚举 {@link TradeOrderRefundStatusEnum} + */ + private Integer refundStatus; + /** + * 退款金é¢ï¼Œå•ä½ï¼šåˆ† + * + * 注æ„,退款并ä¸ä¼šå½±å“ {@link #payPrice} å®žé™…æ”¯ä»˜é‡‘é¢ + * ä¹Ÿå°±è¯´ï¼Œä¸€ä¸ªè®¢å•æœ€ç»ˆäº§ç”Ÿå¤šå°‘金é¢çš„æ”¶å…¥ = payPrice - refundPrice + */ + private Integer refundPrice; + + // ========== è¥é”€åŸºæœ¬ä¿¡æ¯ ========== + /** + * ä¼˜æƒ åŠµç¼–å· + */ + private Long couponId; + /** + * 优惠劵å‡å…金é¢ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ trade.coupon_fee 字段 + */ + private Integer couponPrice; + /** + * 使用的积分 + */ + private Integer usePoint; + /** + * 积分抵扣的金é¢ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ trade.point_fee 字段 + */ + private Integer pointPrice; + /** + * èµ é€çš„积分 + */ + private Integer givePoint; + /** + * 退还的使用的积分 + */ + private Integer refundPoint; + /** + * VIP å‡å…金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer vipPrice; + + /** + * èµ é€çš„优惠劵 + * + * key: ä¼˜æƒ åŠµæ¨¡ç‰ˆç¼–å· + * valueï¼šå¯¹åº”çš„ä¼˜æƒ åˆ¸æ•°é‡ + * + * ç›®çš„ï¼šç”¨äºŽè®¢å•æ”¯ä»˜åŽèµ é€ä¼˜æƒ åˆ¸ + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map giveCouponTemplateCounts; + /** + * èµ é€çš„ä¼˜æƒ åŠµç¼–å· + * + * 目的:用于åŽç»­å–消或者售åŽè®¢å•æ—¶ï¼Œéœ€è¦æ‰£å‡èµ é€ + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List giveCouponIds; + + /** + * ç§’æ€æ´»åŠ¨ç¼–å· + * + * å…³è” SeckillActivityDO çš„ id 字段 + */ + private Long seckillActivityId; + + /** + * ç ä»·æ´»åŠ¨ç¼–å· + * + * å…³è” BargainActivityDO çš„ id 字段 + */ + private Long bargainActivityId; + /** + * ç ä»·è®°å½•ç¼–å· + * + * å…³è” BargainRecordDO çš„ id 字段 + */ + private Long bargainRecordId; + + /** + * æ‹¼å›¢æ´»åŠ¨ç¼–å· + * + * å…³è” CombinationActivityDO çš„ id 字段 + */ + private Long combinationActivityId; + /** + * æ‹¼å›¢å›¢é•¿ç¼–å· + * + * å…³è” CombinationRecordDO çš„ headId 字段 + */ + private Long combinationHeadId; + /** + * æ‹¼å›¢è®°å½•ç¼–å· + * + * å…³è” CombinationRecordDO çš„ id 字段 + */ + private Long combinationRecordId; + + /** + * ç§¯åˆ†å•†åŸŽæ´»åŠ¨çš„ç¼–å· + * + * å…³è” PointActivityDO çš„ id 字段 + */ + private Long pointActivityId; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderItemDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderItemDO.java new file mode 100644 index 0000000..6b36292 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderItemDO.java @@ -0,0 +1,215 @@ +package com.tashow.cloud.trade.dal.dataobject.order; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleDO; +import com.tashow.cloud.trade.dal.dataobject.cart.CartDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 交易订å•项 DO + * + * @author èŠ‹é“æºç  + */ +@TableName(value = "trade_order_item", autoResultMap = true) +@KeySequence("trade_order_item_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class TradeOrderItemDO extends BaseDO { + + // ========== 订å•é¡¹åŸºæœ¬ä¿¡æ¯ ========== + /** + * ç¼–å· + */ + private Long id; + /** + * ç”¨æˆ·ç¼–å· + * + * å…³è” MemberUserDO çš„ id ç¼–å· + */ + private Long userId; + /** + * 订å•ç¼–å· + * + * å…³è” {@link TradeOrderDO#getId()} + */ + private Long orderId; + /** + * è´­ç‰©è½¦é¡¹ç¼–å· + * + * å…³è” {@link CartDO#getId()} + */ + private Long cartId; + + // ========== 商å“基本信æ¯; 冗余较多字段,å‡å°‘å…³è”æŸ¥è¯¢ ========== + /** + * å•†å“ SPU ç¼–å· + * + * å…³è” ProductSkuDO çš„ spuId ç¼–å· + */ + private Long spuId; + /** + * å•†å“ SPU åç§° + * + * 冗余 ProductSkuDO çš„ spuName ç¼–å· + */ + private String spuName; + /** + * å•†å“ SKU ç¼–å· + * + * å…³è” ProductSkuDO çš„ id ç¼–å· + */ + private Long skuId; + /** + * 属性数组,JSON æ ¼å¼ + * + * 冗余 ProductSkuDO çš„ properties 字段 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List properties; + /** + * 商å“图片 + */ + private String picUrl; + /** + * è´­ä¹°æ•°é‡ + */ + private Integer count; + /** + * 是å¦è¯„ä»· + * + * true - 已评价 + * false - 未评价 + */ + private Boolean commentStatus; + + // ========== ä»·æ ¼ + æ”¯ä»˜åŸºæœ¬ä¿¡æ¯ ========== + + /** + * 商å“原价(å•),å•ä½ï¼šåˆ† + * + * 对应 ProductSkuDO çš„ price 字段 + * 对应 taobao çš„ order.price 字段 + */ + private Integer price; + /** + * 优惠金é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ order.discount_fee 字段 + */ + private Integer discountPrice; + /** + * è¿è´¹é‡‘é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ† + */ + private Integer deliveryPrice; + /** + * 订å•调价(总),å•ä½ï¼šåˆ† + * + * 正数,加价;负数,å‡ä»· + */ + private Integer adjustPrice; + /** + * 应付金é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ† + * + * = {@link #price} * {@link #count} + * - {@link #couponPrice} + * - {@link #pointPrice} + * - {@link #discountPrice} + * + {@link #deliveryPrice} + * + {@link #adjustPrice} + * - {@link #vipPrice} + */ + private Integer payPrice; + + // ========== è¥é”€åŸºæœ¬ä¿¡æ¯ ========== + + /** + * 优惠劵å‡å…金é¢ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ trade.coupon_fee 字段 + */ + private Integer couponPrice; + /** + * 积分抵扣的金é¢ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ trade.point_fee 字段 + */ + private Integer pointPrice; + /** + * 使用的积分 + * + * 目的:用于åŽç»­å–消或者售åŽè®¢å•时,需è¦å½’è¿˜èµ é€ + */ + private Integer usePoint; + /** + * èµ é€çš„积分 + * + * 目的:用于åŽç»­å–消或者售åŽè®¢å•æ—¶ï¼Œéœ€è¦æ‰£å‡èµ é€ + */ + private Integer givePoint; + /** + * VIP å‡å…金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer vipPrice; + + // ========== å”®åŽåŸºæœ¬ä¿¡æ¯ ========== + + /** + * å”®åŽå•ç¼–å· + * + * å…³è” {@link AfterSaleDO#getId()} 字段 + */ + private Long afterSaleId; + /** + * å”®åŽçŠ¶æ€ + * + * 枚举 {@link TradeOrderItemAfterSaleStatusEnum} + */ + private Integer afterSaleStatus; + + /** + * 商å“属性 + */ + @Data + public static class Property implements Serializable { + + /** + * å±žæ€§ç¼–å· + * + * å…³è” ProductPropertyDO çš„ id ç¼–å· + */ + private Long propertyId; + /** + * 属性åå­— + * + * å…³è” ProductPropertyDO çš„ name 字段 + */ + private String propertyName; + + /** + * å±žæ€§å€¼ç¼–å· + * + * å…³è” ProductPropertyValueDO çš„ id ç¼–å· + */ + private Long valueId; + /** + * 属性值åå­— + * + * å…³è” ProductPropertyValueDO çš„ name 字段 + */ + private String valueName; + + } + +} + diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderLogDO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderLogDO.java new file mode 100644 index 0000000..8b8d9c8 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/dataobject/order/TradeOrderLogDO.java @@ -0,0 +1,81 @@ +package com.tashow.cloud.trade.dal.dataobject.order; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderOperateTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * è®¢å•æ—¥å¿— DO + * + * @author é™ˆè³ + */ +@TableName("trade_order_log") +@KeySequence("trade_order_log_seq") // 用于 Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“的主键自增。如果是 MySQL 等数æ®åº“,å¯ä¸å†™ã€‚ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TradeOrderLogDO extends BaseDO { + + /** + * 用户类型 - 系统 + * + * 例如说:Job è‡ªåŠ¨è¿‡æœŸè®¢å•æ—¶ï¼Œé€šè¿‡ç³»ç»Ÿè‡ªåЍæ“作 + */ + public static final Integer USER_TYPE_SYSTEM = 0; + /** + * ç”¨æˆ·ç¼–å· - 系统 + */ + public static final Long USER_ID_SYSTEM = 0L; + + /** + * ç¼–å· + */ + @TableId + private Long id; + /** + * ç”¨æˆ·ç¼–å· + * + * å…³è” AdminUserDO çš„ id å­—æ®µã€æˆ–者 MemberUserDO çš„ id 字段 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + + /** + * 订å•å· + * + * å…³è” {@link TradeOrderDO#getId()} + */ + private Long orderId; + /** + * æ“作å‰çŠ¶æ€ + */ + private Integer beforeStatus; + /** + * æ“作åŽçŠ¶æ€ + */ + private Integer afterStatus; + + /** + * æ“作类型 + * + * {@link TradeOrderOperateTypeEnum} + */ + private Integer operateType; + /** + * è®¢å•æ—¥å¿—ä¿¡æ¯ + */ + private String content; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/aftersale/AfterSaleLogMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/aftersale/AfterSaleLogMapper.java new file mode 100644 index 0000000..9d31711 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/aftersale/AfterSaleLogMapper.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.trade.dal.mysql.aftersale; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleLogDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface AfterSaleLogMapper extends BaseMapperX { + + default List selectListByAfterSaleId(Long afterSaleId) { + return selectList(new LambdaQueryWrapper() + .eq(AfterSaleLogDO::getAfterSaleId, afterSaleId) + .orderByDesc(AfterSaleLogDO::getCreateTime)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/aftersale/AfterSaleMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/aftersale/AfterSaleMapper.java new file mode 100644 index 0000000..d6ed877 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/aftersale/AfterSaleMapper.java @@ -0,0 +1,52 @@ +package com.tashow.cloud.trade.dal.mysql.aftersale; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; + +@Mapper +public interface AfterSaleMapper extends BaseMapperX { + + default PageResult selectPage(AfterSalePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(AfterSaleDO::getUserId, reqVO.getUserId()) + .likeIfPresent(AfterSaleDO::getNo, reqVO.getNo()) + .eqIfPresent(AfterSaleDO::getStatus, reqVO.getStatus()) + .eqIfPresent(AfterSaleDO::getType, reqVO.getType()) + .eqIfPresent(AfterSaleDO::getWay, reqVO.getWay()) + .likeIfPresent(AfterSaleDO::getOrderNo, reqVO.getOrderNo()) + .likeIfPresent(AfterSaleDO::getSpuName, reqVO.getSpuName()) + .betweenIfPresent(AfterSaleDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(AfterSaleDO::getId)); + } + + default PageResult selectPage(Long userId, PageParam pageParam) { + return selectPage(pageParam, new LambdaQueryWrapperX() + .eqIfPresent(AfterSaleDO::getUserId, userId) + .orderByDesc(AfterSaleDO::getId)); + } + + default int updateByIdAndStatus(Long id, Integer status, AfterSaleDO update) { + return update(update, new LambdaUpdateWrapper() + .eq(AfterSaleDO::getId, id).eq(AfterSaleDO::getStatus, status)); + } + + default AfterSaleDO selectByIdAndUserId(Long id, Long userId) { + return selectOne(AfterSaleDO::getId, id, + AfterSaleDO::getUserId, userId); + } + + default Long selectCountByUserIdAndStatus(Long userId, Collection statuses) { + return selectCount(new LambdaQueryWrapperX() + .eq(AfterSaleDO::getUserId, userId) + .in(AfterSaleDO::getStatus, statuses)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageRecordMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageRecordMapper.java new file mode 100644 index 0000000..284ce2f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageRecordMapper.java @@ -0,0 +1,112 @@ +package com.tashow.cloud.trade.dal.mysql.brokerage; + +import cn.hutool.core.bean.BeanUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByPriceRespVO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageRecordDO; +import com.tashow.cloud.trade.service.brokerage.bo.UserBrokerageSummaryRespBO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.github.yulichang.toolkit.MPJWrappers; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 佣金记录 Mapper + * + * @author owen + */ +@Mapper +public interface BrokerageRecordMapper extends BaseMapperX { + + default PageResult selectPage(BrokerageRecordPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(BrokerageRecordDO::getUserId, reqVO.getUserId()) + .eqIfPresent(BrokerageRecordDO::getBizType, reqVO.getBizType()) + .eqIfPresent(BrokerageRecordDO::getStatus, reqVO.getStatus()) + .eqIfPresent(BrokerageRecordDO::getSourceUserLevel, reqVO.getSourceUserLevel()) + .betweenIfPresent(BrokerageRecordDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BrokerageRecordDO::getId)); + } + + default List selectListByStatusAndUnfreezeTimeLt(Integer status, LocalDateTime unfreezeTime) { + return selectList(new LambdaQueryWrapper() + .eq(BrokerageRecordDO::getStatus, status) + .lt(BrokerageRecordDO::getUnfreezeTime, unfreezeTime)); + } + + default int updateByIdAndStatus(Integer id, Integer status, BrokerageRecordDO updateObj) { + return update(updateObj, new LambdaQueryWrapper() + .eq(BrokerageRecordDO::getId, id) + .eq(BrokerageRecordDO::getStatus, status)); + } + + default List selectListByBizTypeAndBizId(Integer bizType, String bizId) { + return selectList(BrokerageRecordDO::getBizType, bizType, + BrokerageRecordDO::getBizId, bizId); + } + + default List selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(Collection userIds, + Integer bizType, + Integer status) { + List> list = selectMaps(MPJWrappers.lambdaJoin(BrokerageRecordDO.class) + .select(BrokerageRecordDO::getUserId) + .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryRespBO::getCount) + .selectSum(BrokerageRecordDO::getPrice) + .in(BrokerageRecordDO::getUserId, userIds) + .eq(BrokerageRecordDO::getBizType, bizType) + .eq(BrokerageRecordDO::getStatus, status) + .groupBy(BrokerageRecordDO::getUserId)); // 按照 userId èšåˆ + return BeanUtil.copyToList(list, UserBrokerageSummaryRespBO.class); + // selectJoinList有BUG,会与租户æ’件冲çªï¼šè§£æžSQL时,å‘生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW +// return selectJoinList(UserBrokerageSummaryBO.class, MPJWrappers.lambdaJoin(BrokerageRecordDO.class) +// .select(BrokerageRecordDO::getUserId) +// .selectCount(BrokerageRecordDO::getId, UserBrokerageSummaryBO::getCount) +// .selectSum(BrokerageRecordDO::getPrice) +// .in(BrokerageRecordDO::getUserId, userIds) +// .eq(BrokerageRecordDO::getBizType, bizType) +// .eq(BrokerageRecordDO::getStatus, status) +// .groupBy(BrokerageRecordDO::getUserId)); + } + + @Select("SELECT SUM(price) FROM trade_brokerage_record " + + "WHERE user_id = #{userId} AND biz_type = #{bizType} AND status = #{status} " + + "AND unfreeze_time BETWEEN #{beginTime} AND #{endTime} AND deleted = FALSE") + Integer selectSummaryPriceByUserIdAndBizTypeAndCreateTimeBetween(@Param("userId") Long userId, + @Param("bizType") Integer bizType, + @Param("status") Integer status, + @Param("beginTime") LocalDateTime beginTime, + @Param("endTime") LocalDateTime endTime); + + // TODO @芋艿:收敛掉 @Select 注解æ“ä½œï¼Œç»Ÿä¸€æˆ MyBatis-Plus 的方å¼ï¼Œæˆ–者 xml + @Select("SELECT user_id AS id, SUM(price) AS brokeragePrice FROM trade_brokerage_record " + + "WHERE biz_type = #{bizType} AND status = #{status} AND deleted = FALSE " + + "AND unfreeze_time BETWEEN #{beginTime} AND #{endTime} " + + "GROUP BY user_id " + + "ORDER BY brokeragePrice DESC") + IPage selectSummaryPricePageGroupByUserId(IPage page, + @Param("bizType") Integer bizType, + @Param("status") Integer status, + @Param("beginTime") LocalDateTime beginTime, + @Param("endTime") LocalDateTime endTime); + + @Select("SELECT COUNT(1) FROM trade_brokerage_record " + + "WHERE biz_type = #{bizType} AND status = #{status} AND deleted = FALSE " + + "AND unfreeze_time BETWEEN #{beginTime} AND #{endTime} " + + "GROUP BY user_id HAVING SUM(price) > #{brokeragePrice}") + Integer selectCountByPriceGt(@Param("brokeragePrice") Integer brokeragePrice, + @Param("bizType") Integer bizType, + @Param("status") Integer status, + @Param("beginTime") LocalDateTime beginTime, + @Param("endTime") LocalDateTime endTime); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageUserMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageUserMapper.java new file mode 100644 index 0000000..ded1c6a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageUserMapper.java @@ -0,0 +1,167 @@ +package com.tashow.cloud.trade.dal.mysql.brokerage; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.SortingField; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * 分销用户 Mapper + * + * @author owen + */ +@Mapper +public interface BrokerageUserMapper extends BaseMapperX { + + default PageResult selectPage(BrokerageUserPageReqVO reqVO, List ids) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .inIfPresent(BrokerageUserDO::getId, ids) + .eqIfPresent(BrokerageUserDO::getBrokerageEnabled, reqVO.getBrokerageEnabled()) + .betweenIfPresent(BrokerageUserDO::getCreateTime, reqVO.getCreateTime()) + .betweenIfPresent(BrokerageUserDO::getBindUserTime, reqVO.getBindUserTime()) + .orderByDesc(BrokerageUserDO::getId)); + } + + /** + * 更新用户å¯ç”¨ä½£é‡‘(增加) + * + * @param id ç”¨æˆ·ç¼–å· + * @param incrCount 增加佣金(正数) + */ + default void updatePriceIncr(Long id, Integer incrCount) { + Assert.isTrue(incrCount > 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" brokerage_price = brokerage_price + " + incrCount) + .eq(BrokerageUserDO::getId, id); + update(null, lambdaUpdateWrapper); + } + + /** + * 更新用户å¯ç”¨ä½£é‡‘(å‡å°‘) + * 注æ„:ç†è®ºä¸Šä½£é‡‘å¯èƒ½å·²ç»æçŽ°ï¼Œè¿™æ—¶ä¼šæ‰£å‡ºè´Ÿæ•°ï¼Œç¡®ä¿å¹³å°ä¸ä¼šé€ æˆæŸå¤± + * + * @param id ç”¨æˆ·ç¼–å· + * @param incrCount 增加佣金(负数) + * @return 更新行数 + */ + default int updatePriceDecr(Long id, Integer incrCount) { + Assert.isTrue(incrCount < 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" brokerage_price = brokerage_price + " + incrCount) // 负数,所以使用 + å· + .eq(BrokerageUserDO::getId, id); + return update(null, lambdaUpdateWrapper); + } + + /** + * 更新用户冻结佣金(增加) + * + * @param id ç”¨æˆ·ç¼–å· + * @param incrCount 增加冻结佣金(正数) + */ + default void updateFrozenPriceIncr(Long id, Integer incrCount) { + Assert.isTrue(incrCount > 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" frozen_price = frozen_price + " + incrCount) + .eq(BrokerageUserDO::getId, id); + update(null, lambdaUpdateWrapper); + } + + /** + * 更新用户冻结佣金(å‡å°‘) + * 注æ„:ç†è®ºä¸Šå†»ç»“佣金å¯èƒ½å·²ç»è§£å†»ï¼Œè¿™æ—¶ä¼šæ‰£å‡ºè´Ÿæ•°ï¼Œç¡®ä¿å¹³å°ä¸ä¼šé€ æˆæŸå¤± + * + * @param id ç”¨æˆ·ç¼–å· + * @param incrCount å‡å°‘冻结佣金(负数) + */ + default void updateFrozenPriceDecr(Long id, Integer incrCount) { + Assert.isTrue(incrCount < 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" frozen_price = frozen_price + " + incrCount) // 负数,所以使用 + å· + .eq(BrokerageUserDO::getId, id); + update(null, lambdaUpdateWrapper); + } + + /** + * 更新用户冻结佣金(å‡å°‘), 更新用户佣金(增加) + * + * @param id ç”¨æˆ·ç¼–å· + * @param incrCount å‡å°‘冻结佣金(负数) + * @return æ›´æ–°æ¡æ•° + */ + default int updateFrozenPriceDecrAndPriceIncr(Long id, Integer incrCount) { + Assert.isTrue(incrCount < 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" frozen_price = frozen_price + " + incrCount + // 负数,所以使用 + å· + ", brokerage_price = brokerage_price + " + -incrCount) // 负数,所以使用 - å· + .eq(BrokerageUserDO::getId, id) + .ge(BrokerageUserDO::getFrozenPrice, -incrCount); // cas 逻辑 + return update(null, lambdaUpdateWrapper); + } + + default void updateBindUserIdAndBindUserTimeToNull(Long id) { + update(null, new LambdaUpdateWrapper() + .eq(BrokerageUserDO::getId, id) + .set(BrokerageUserDO::getBindUserId, null).set(BrokerageUserDO::getBindUserTime, null)); + } + + default void updateEnabledFalseAndBrokerageTimeToNull(Long id) { + update(null, new LambdaUpdateWrapper() + .eq(BrokerageUserDO::getId, id) + .set(BrokerageUserDO::getBrokerageEnabled, false).set(BrokerageUserDO::getBrokerageTime, null)); + } + + @Select("SELECT bind_user_id AS id, COUNT(1) AS brokerageUserCount FROM trade_brokerage_user " + + "WHERE bind_user_id IS NOT NULL AND deleted = FALSE " + + "AND bind_user_time BETWEEN #{beginTime} AND #{endTime} " + + "GROUP BY bind_user_id " + + "ORDER BY brokerageUserCount DESC") + IPage selectCountPageGroupByBindUserId(Page page, + @Param("beginTime") LocalDateTime beginTime, + @Param("endTime") LocalDateTime endTime); + + /** + * 下级分销统计(分页) + * + * @param bizType 业务类型 + * @param status çŠ¶æ€ + * @param ids 用户编å·åˆ—表 + * @param sortingField 排åºå­—段 + * @return 下级分销统计分页列表 + */ + IPage selectSummaryPageByUserId(Page page, + @Param("bizType") Integer bizType, + @Param("status") Integer status, + @Param("ids") Collection ids, + @Param("sortingField") SortingField sortingField); + + /** + * 获得被 bindUserIds æŽ¨å¹¿çš„ç”¨æˆ·ç¼–å·æ•°ç»„ + * + * @param bindUserIds æŽ¨å¹¿å‘˜ç¼–å·æ•°ç»„ + * @return ç”¨æˆ·ç¼–å·æ•°ç»„ + */ + default List selectIdListByBindUserIdIn(Collection bindUserIds) { + return Convert.toList(Long.class, + selectObjs(new LambdaQueryWrapperX() + .select(Collections.singletonList(BrokerageUserDO::getId)) // åªæŸ¥è¯¢ id 字段,加速返回速度 + .in(BrokerageUserDO::getBindUserId, bindUserIds))); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java new file mode 100644 index 0000000..200a7c9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java @@ -0,0 +1,64 @@ +package com.tashow.cloud.trade.dal.mysql.brokerage; + +import cn.hutool.core.bean.BeanUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 佣金æçް Mapper + * + * @author èŠ‹é“æºç  + */ +@Mapper +public interface BrokerageWithdrawMapper extends BaseMapperX { + + default PageResult selectPage(BrokerageWithdrawPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(BrokerageWithdrawDO::getUserId, reqVO.getUserId()) + .eqIfPresent(BrokerageWithdrawDO::getType, reqVO.getType()) + .likeIfPresent(BrokerageWithdrawDO::getName, reqVO.getName()) + .eqIfPresent(BrokerageWithdrawDO::getAccountNo, reqVO.getAccountNo()) + .likeIfPresent(BrokerageWithdrawDO::getBankName, reqVO.getBankName()) + .eqIfPresent(BrokerageWithdrawDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BrokerageWithdrawDO::getCreateTime, reqVO.getCreateTime()) + .orderByAsc(BrokerageWithdrawDO::getStatus).orderByDesc(BrokerageWithdrawDO::getId)); + } + + default int updateByIdAndStatus(Long id, Integer status, BrokerageWithdrawDO updateObj) { + return update(updateObj, new LambdaUpdateWrapper() + .eq(BrokerageWithdrawDO::getId, id) + .eq(BrokerageWithdrawDO::getStatus, status)); + } + + default List selectCountAndSumPriceByUserIdAndStatus(Collection userIds, + Collection status) { + List> list = selectMaps(new MPJLambdaWrapper() + .select(BrokerageWithdrawDO::getUserId) + .selectCount(BrokerageWithdrawDO::getId, BrokerageWithdrawSummaryRespBO::getCount) + .selectSum(BrokerageWithdrawDO::getPrice) + .in(BrokerageWithdrawDO::getUserId, userIds) + .in(BrokerageWithdrawDO::getStatus, status) + .groupBy(BrokerageWithdrawDO::getUserId)); + return BeanUtil.copyToList(list, BrokerageWithdrawSummaryRespBO.class); + // selectJoinList有BUG,会与租户æ’件冲çªï¼šè§£æžSQL时,å‘生异常 https://gitee.com/best_handsome/mybatis-plus-join/issues/I84GYW +// return selectJoinList(UserWithdrawSummaryBO.class, new MPJLambdaWrapper() +// .select(BrokerageWithdrawDO::getUserId) +// .selectCount(BrokerageWithdrawDO::getId, UserWithdrawSummaryBO::getCount) +// .selectSum(BrokerageWithdrawDO::getPrice) +// .in(BrokerageWithdrawDO::getUserId, userIds) +// .eq(BrokerageWithdrawDO::getStatus, status) +// .groupBy(BrokerageWithdrawDO::getUserId)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/cart/CartMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/cart/CartMapper.java new file mode 100644 index 0000000..4aecbc5 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/cart/CartMapper.java @@ -0,0 +1,62 @@ +package com.tashow.cloud.trade.dal.mysql.cart; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.trade.dal.dataobject.cart.CartDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Mapper +public interface CartMapper extends BaseMapperX { + + default CartDO selectByUserIdAndSkuId(Long userId, Long skuId) { + return selectOne(CartDO::getUserId, userId, + CartDO::getSkuId, skuId); + } + + default Integer selectSumByUserId(Long userId) { + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("SUM(count) AS sumCount") + .eq("user_id", userId) + .eq("selected", true)); // åªè®¡ç®—选中的 + // èŽ·å¾—æ•°é‡ + return CollUtil.getFirst(result) != null ? MapUtil.getInt(result.get(0), "sumCount") : 0; + } + + default CartDO selectById(Long id, Long userId) { + return selectOne(CartDO::getId, id, + CartDO::getUserId, userId); + } + + default List selectListByIds(Collection ids, Long userId) { + return selectList(new LambdaQueryWrapper() + .in(CartDO::getId, ids) + .eq(CartDO::getUserId, userId)); + } + + default List selectListByUserId(Long userId) { + return selectList(new LambdaQueryWrapper() + .eq(CartDO::getUserId, userId)); + } + + default List selectListByUserId(Long userId, Set ids) { + return selectList(new LambdaQueryWrapper() + .eq(CartDO::getUserId, userId) + .in(CartDO::getId, ids)); + } + + default void updateByIds(Collection ids, Long userId, CartDO updateObj) { + update(updateObj, new LambdaQueryWrapper() + .in(CartDO::getId, ids) + .eq(CartDO::getUserId, userId)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/config/TradeConfigMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/config/TradeConfigMapper.java new file mode 100644 index 0000000..ed27b62 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/config/TradeConfigMapper.java @@ -0,0 +1,15 @@ +package com.tashow.cloud.trade.dal.mysql.config; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 交易中心é…ç½® Mapper + * + * @author owen + */ +@Mapper +public interface TradeConfigMapper extends BaseMapperX { + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressMapper.java new file mode 100644 index 0000000..6774000 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressMapper.java @@ -0,0 +1,48 @@ +package com.tashow.cloud.trade.dal.mysql.delivery; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressExportReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressPageReqVO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface DeliveryExpressMapper extends BaseMapperX { + + default PageResult selectPage(DeliveryExpressPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(DeliveryExpressDO::getCode, reqVO.getCode()) + .likeIfPresent(DeliveryExpressDO::getName, reqVO.getName()) + .eqIfPresent(DeliveryExpressDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(DeliveryExpressDO::getCreateTime, reqVO.getCreateTime()) + .orderByAsc(DeliveryExpressDO::getSort)); + } + + default List selectList(DeliveryExpressExportReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(DeliveryExpressDO::getCode, reqVO.getCode()) + .likeIfPresent(DeliveryExpressDO::getName, reqVO.getName()) + .eqIfPresent(DeliveryExpressDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(DeliveryExpressDO::getCreateTime, reqVO.getCreateTime()) + .orderByAsc(DeliveryExpressDO::getSort)); + } + + default DeliveryExpressDO selectByCode(String code) { + return selectOne(new LambdaQueryWrapper() + .eq(DeliveryExpressDO::getCode, code)); + } + + default List selectListByStatus(Integer status) { + return selectList(DeliveryExpressDO::getStatus, status); + } + +} + + + + diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateChargeMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateChargeMapper.java new file mode 100644 index 0000000..8df59fa --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateChargeMapper.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.trade.dal.mysql.delivery; + + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface DeliveryExpressTemplateChargeMapper extends BaseMapperX { + + default List selectListByTemplateId(Long templateId){ + return selectList(new LambdaQueryWrapper() + .eq(DeliveryExpressTemplateChargeDO::getTemplateId, templateId)); + } + + default int deleteByTemplateId(Long templateId){ + return delete(new LambdaQueryWrapper() + .eq(DeliveryExpressTemplateChargeDO::getTemplateId, templateId)); + } + + default List selectByTemplateIds(Collection templateIds) { + return selectList(DeliveryExpressTemplateChargeDO::getTemplateId, templateIds); + } + +} + + + + diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateFreeMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateFreeMapper.java new file mode 100644 index 0000000..96c0c1a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateFreeMapper.java @@ -0,0 +1,31 @@ +package com.tashow.cloud.trade.dal.mysql.delivery; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface DeliveryExpressTemplateFreeMapper extends BaseMapperX { + + default List selectListByTemplateId(Long templateId) { + return selectList(new LambdaQueryWrapper() + .eq(DeliveryExpressTemplateFreeDO::getTemplateId, templateId)); + } + + default int deleteByTemplateId(Long templateId) { + return delete(new LambdaQueryWrapper() + .eq(DeliveryExpressTemplateFreeDO::getTemplateId, templateId)); + } + + default List selectListByTemplateIds(Collection templateIds) { + return selectList(DeliveryExpressTemplateFreeDO::getTemplateId, templateIds); + } +} + + + + diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateMapper.java new file mode 100644 index 0000000..a2e8a74 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryExpressTemplateMapper.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.trade.dal.mysql.delivery; + + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface DeliveryExpressTemplateMapper extends BaseMapperX { + + default PageResult selectPage(DeliveryExpressTemplatePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(DeliveryExpressTemplateDO::getName, reqVO.getName()) + .eqIfPresent(DeliveryExpressTemplateDO::getChargeMode, reqVO.getChargeMode()) + .betweenIfPresent(DeliveryExpressTemplateDO::getCreateTime, reqVO.getCreateTime()) + .orderByAsc(DeliveryExpressTemplateDO::getSort)); + } + + default DeliveryExpressTemplateDO selectByName(String name) { + return selectOne(DeliveryExpressTemplateDO::getName,name); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryPickUpStoreMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryPickUpStoreMapper.java new file mode 100644 index 0000000..409250a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/delivery/DeliveryPickUpStoreMapper.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.trade.dal.mysql.delivery; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStorePageReqVO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface DeliveryPickUpStoreMapper extends BaseMapperX { + + default PageResult selectPage(DeliveryPickUpStorePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(DeliveryPickUpStoreDO::getName, reqVO.getName()) + .eqIfPresent(DeliveryPickUpStoreDO::getPhone, reqVO.getPhone()) + .eqIfPresent(DeliveryPickUpStoreDO::getAreaId, reqVO.getAreaId()) + .eqIfPresent(DeliveryPickUpStoreDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(DeliveryPickUpStoreDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(DeliveryPickUpStoreDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(DeliveryPickUpStoreDO::getStatus, status); + } + +} + + + + diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderItemMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderItemMapper.java new file mode 100644 index 0000000..e9926d3 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderItemMapper.java @@ -0,0 +1,56 @@ +package com.tashow.cloud.trade.dal.mysql.order; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Mapper +public interface TradeOrderItemMapper extends BaseMapperX { + + default int updateAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus, + Long afterSaleId) { + return update(new TradeOrderItemDO().setAfterSaleStatus(newAfterSaleStatus).setAfterSaleId(afterSaleId), + new LambdaUpdateWrapper<>(new TradeOrderItemDO().setId(id).setAfterSaleStatus(oldAfterSaleStatus))); + } + + default List selectListByOrderId(Long orderId) { + return selectList(TradeOrderItemDO::getOrderId, orderId); + } + + default List selectListByOrderId(Collection orderIds) { + return selectList(TradeOrderItemDO::getOrderId, orderIds); + } + + default TradeOrderItemDO selectByIdAndUserId(Long orderItemId, Long loginUserId) { + return selectOne(new LambdaQueryWrapperX() + .eq(TradeOrderItemDO::getId, orderItemId) + .eq(TradeOrderItemDO::getUserId, loginUserId)); + } + + default List selectListByOrderIdAndCommentStatus(Long orderId, Boolean commentStatus) { + return selectList(new LambdaQueryWrapperX() + .eq(TradeOrderItemDO::getOrderId, orderId) + .eq(TradeOrderItemDO::getCommentStatus, commentStatus)); + } + + default int selectProductSumByOrderId(@Param("orderIds") Set orderIds) { + // SQL sum 查询 + List> result = selectMaps(new QueryWrapper() + .select("SUM(count) AS sumCount") + .in("order_id", orderIds)); // åªè®¡ç®—选中的 + // èŽ·å¾—æ•°é‡ + return CollUtil.getFirst(result) != null ? MapUtil.getInt(result.get(0), "sumCount") : 0; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderLogMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderLogMapper.java new file mode 100644 index 0000000..2328179 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderLogMapper.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.trade.dal.mysql.order; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderLogDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TradeOrderLogMapper extends BaseMapperX { + + default List selectListByOrderId(Long orderId) { + return selectList(new LambdaQueryWrapper() + .eq(TradeOrderLogDO::getOrderId, orderId) + .orderByDesc(TradeOrderLogDO::getCreateTime)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderMapper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderMapper.java new file mode 100644 index 0000000..674da2b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/mysql/order/TradeOrderMapper.java @@ -0,0 +1,140 @@ +package com.tashow.cloud.trade.dal.mysql.order; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderPageReqVO; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderPageReqVO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Mapper +public interface TradeOrderMapper extends BaseMapperX { + + default int updateByIdAndStatus(Long id, Integer status, TradeOrderDO update) { + return update(update, new LambdaUpdateWrapper() + .eq(TradeOrderDO::getId, id).eq(TradeOrderDO::getStatus, status)); + } + + default TradeOrderDO selectByIdAndUserId(Long id, Long userId) { + return selectOne(TradeOrderDO::getId, id, TradeOrderDO::getUserId, userId); + } + + default PageResult selectPage(TradeOrderPageReqVO reqVO, Set userIds) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(TradeOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(TradeOrderDO::getUserId, reqVO.getUserId()) + .eqIfPresent(TradeOrderDO::getDeliveryType, reqVO.getDeliveryType()) + .inIfPresent(TradeOrderDO::getUserId, userIds) + .eqIfPresent(TradeOrderDO::getType, reqVO.getType()) + .eqIfPresent(TradeOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(TradeOrderDO::getPayChannelCode, reqVO.getPayChannelCode()) + .eqIfPresent(TradeOrderDO::getTerminal, reqVO.getTerminal()) + .eqIfPresent(TradeOrderDO::getLogisticsId, reqVO.getLogisticsId()) + .inIfPresent(TradeOrderDO::getPickUpStoreId, reqVO.getPickUpStoreIds()) + .likeIfPresent(TradeOrderDO::getPickUpVerifyCode, reqVO.getPickUpVerifyCode()) + .betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(TradeOrderDO::getId)); + } + + // TODO @疯狂:如果用 map 返回,è¦ä¸è¿™é‡Œç›´æŽ¥ç”¨ TradeOrderSummaryRespVO 返回?也算åˆç†ï¼Œå°±å½“ sql 查询出这么个玩æ„~~ + default List> selectOrderSummaryGroupByRefundStatus(TradeOrderPageReqVO reqVO, Set userIds) { + return selectMaps(new MPJLambdaWrapperX() + .selectAs(TradeOrderDO::getRefundStatus, TradeOrderDO::getRefundStatus) // å”®åŽçŠ¶æ€ + .selectCount(TradeOrderDO::getId, "count") // å”®åŽçжæ€å¯¹åº”çš„æ•°é‡ + .selectSum(TradeOrderDO::getPayPrice, "price") // å”®åŽçжæ€å¯¹åº”çš„æ”¯ä»˜é‡‘é¢ + .likeIfPresent(TradeOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(TradeOrderDO::getUserId, reqVO.getUserId()) + .eqIfPresent(TradeOrderDO::getDeliveryType, reqVO.getDeliveryType()) + .inIfPresent(TradeOrderDO::getUserId, userIds) + .eqIfPresent(TradeOrderDO::getType, reqVO.getType()) + .eqIfPresent(TradeOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(TradeOrderDO::getPayChannelCode, reqVO.getPayChannelCode()) + .eqIfPresent(TradeOrderDO::getTerminal, reqVO.getTerminal()) + .eqIfPresent(TradeOrderDO::getLogisticsId, reqVO.getLogisticsId()) + .inIfPresent(TradeOrderDO::getPickUpStoreId, reqVO.getPickUpStoreIds()) + .likeIfPresent(TradeOrderDO::getPickUpVerifyCode, reqVO.getPickUpVerifyCode()) + .betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime()) + .groupBy(TradeOrderDO::getRefundStatus)); // 按售åŽçжæ€åˆ†ç»„ + } + + default PageResult selectPage(AppTradeOrderPageReqVO reqVO, Long userId) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(TradeOrderDO::getUserId, userId) + .eqIfPresent(TradeOrderDO::getStatus, reqVO.getStatus()) + .eqIfPresent(TradeOrderDO::getCommentStatus, reqVO.getCommentStatus()) + .orderByDesc(TradeOrderDO::getId)); // TODO 芋艿:未æ¥ä¸åŒçš„ status,ä¸åŒçš„æŽ’åº + } + + default Long selectCountByUserIdAndStatus(Long userId, Integer status, Boolean commentStatus) { + return selectCount(new LambdaQueryWrapperX() + .eq(TradeOrderDO::getUserId, userId) + .eqIfPresent(TradeOrderDO::getStatus, status) + .eqIfPresent(TradeOrderDO::getCommentStatus, commentStatus)); + } + + default TradeOrderDO selectOrderByIdAndUserId(Long orderId, Long loginUserId) { + return selectOne(new LambdaQueryWrapperX() + .eq(TradeOrderDO::getId, orderId) + .eq(TradeOrderDO::getUserId, loginUserId)); + } + + default List selectListByStatusAndCreateTimeLt(Integer status, LocalDateTime createTime) { + return selectList(new LambdaUpdateWrapper() + .eq(TradeOrderDO::getStatus, status) + .lt(TradeOrderDO::getCreateTime, createTime)); + } + + default List selectListByStatusAndDeliveryTimeLt(Integer status, LocalDateTime deliveryTime) { + return selectList(new LambdaUpdateWrapper() + .eq(TradeOrderDO::getStatus, status) + .lt(TradeOrderDO::getDeliveryTime, deliveryTime)); + } + + default List selectListByStatusAndReceiveTimeLt(Integer status, LocalDateTime receive, + Boolean commentStatus) { + return selectList(new LambdaUpdateWrapper() + .eq(TradeOrderDO::getStatus, status) + .lt(TradeOrderDO::getReceiveTime, receive) + .eq(TradeOrderDO::getCommentStatus, commentStatus)); + } + + default List selectListByUserIdAndActivityId(Long userId, Long activityId, TradeOrderTypeEnum type) { + LambdaQueryWrapperX queryWrapperX = new LambdaQueryWrapperX<>(); + queryWrapperX.eq(TradeOrderDO::getUserId, userId); + if (TradeOrderTypeEnum.isSeckill(type.getType())) { + queryWrapperX.eq(TradeOrderDO::getSeckillActivityId, activityId); + } + if (TradeOrderTypeEnum.isBargain(type.getType())) { + queryWrapperX.eq(TradeOrderDO::getBargainActivityId, activityId); + } + if (TradeOrderTypeEnum.isCombination(type.getType())) { + queryWrapperX.eq(TradeOrderDO::getCombinationActivityId, activityId); + } + if (TradeOrderTypeEnum.isPoint(type.getType())) { + queryWrapperX.eq(TradeOrderDO::getPointActivityId, activityId); + } + return selectList(queryWrapperX); + } + + default TradeOrderDO selectOneByPickUpVerifyCode(String pickUpVerifyCode) { + return selectOne(TradeOrderDO::getPickUpVerifyCode, pickUpVerifyCode); + } + + default TradeOrderDO selectByUserIdAndCombinationActivityIdAndStatus(Long userId, Long combinationActivityId, Integer status) { + return selectOne(new LambdaQueryWrapperX() + .eq(TradeOrderDO::getUserId, userId) + .eq(TradeOrderDO::getStatus, status) + .eq(TradeOrderDO::getCombinationActivityId, combinationActivityId) + ); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/redis/RedisKeyConstants.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/redis/RedisKeyConstants.java new file mode 100644 index 0000000..6876753 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/redis/RedisKeyConstants.java @@ -0,0 +1,26 @@ +package com.tashow.cloud.trade.dal.redis; + +/** + * 交易 Redis Key 枚举类 + * + * @author èŠ‹é“æºç  + */ +public interface RedisKeyConstants { + + /** + * 交易åºå·çš„缓存 + * + * KEY æ ¼å¼ï¼štrade_no:{prefix} + * VALUE æ•°æ®æ ¼å¼ï¼šç¼–å·è‡ªå¢ž + */ + String TRADE_NO = "trade_no:"; + + /** + * 交易åºå·çš„缓存 + * + * KEY æ ¼å¼ï¼šexpress_track:{code-logisticsNo-receiverMobile} + * VALUE æ•°æ®æ ¼å¼ String, 物æµä¿¡æ¯é›†åˆ + */ + String EXPRESS_TRACK = "express_track"; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/redis/no/TradeNoRedisDAO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/redis/no/TradeNoRedisDAO.java new file mode 100644 index 0000000..08f61d7 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/dal/redis/no/TradeNoRedisDAO.java @@ -0,0 +1,44 @@ +package com.tashow.cloud.trade.dal.redis.no; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import com.tashow.cloud.trade.dal.redis.RedisKeyConstants; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import jakarta.annotation.Resource; +import java.time.Duration; +import java.time.LocalDateTime; + +/** + * 订å•åºå·çš„ Redis DAO + * + * @author HUIHUI + */ +@Repository +public class TradeNoRedisDAO { + + public static final String TRADE_ORDER_NO_PREFIX = "o"; + + public static final String AFTER_SALE_NO_PREFIX = "r"; + + @Resource + private StringRedisTemplate stringRedisTemplate; + + /** + * 生æˆåºå· + * + * @param prefix å‰ç¼€ + * @return åºå· + */ + public String generate(String prefix) { + // 递增åºå· + String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN); + String key = RedisKeyConstants.TRADE_NO + noPrefix; + Long no = stringRedisTemplate.opsForValue().increment(key); + // 设置过期时间 + stringRedisTemplate.expire(key, Duration.ofMinutes(1L)); + return noPrefix + no; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/config/AfterSaleLogConfiguration.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/config/AfterSaleLogConfiguration.java new file mode 100644 index 0000000..12c3961 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/config/AfterSaleLogConfiguration.java @@ -0,0 +1,22 @@ +package com.tashow.cloud.trade.framework.aftersale.config; + +import com.tashow.cloud.trade.framework.aftersale.core.aop.AfterSaleLogAspect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +// TODO @chenchenï¼šæ”¹æˆ aftersale 好点哈; +/** + * trade 模å—çš„ afterSaleLog 组件的 Configuration + * + * @author é™ˆè³ + * @since 2023/6/18 11:09 + */ +@Configuration(proxyBeanMethods = false) +public class AfterSaleLogConfiguration { + + @Bean + public AfterSaleLogAspect afterSaleLogAspect() { + return new AfterSaleLogAspect(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/annotations/AfterSaleLog.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/annotations/AfterSaleLog.java new file mode 100644 index 0000000..fafa151 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/annotations/AfterSaleLog.java @@ -0,0 +1,27 @@ +package com.tashow.cloud.trade.framework.aftersale.core.annotations; + +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum; +import com.tashow.cloud.trade.framework.aftersale.core.aop.AfterSaleLogAspect; + +import java.lang.annotation.*; + +/** + * å”®åŽæ—¥å¿—的注解 + * + * å†™åœ¨æ–¹æ³•ä¸Šæ—¶ï¼Œä¼šè‡ªåŠ¨è®°å½•å”®åŽæ—¥å¿— + * + * @author é™ˆè³ + * @since 2023/6/8 17:04 + * @see AfterSaleLogAspect + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AfterSaleLog { + + /** + * æ“作类型 + */ + AfterSaleOperateTypeEnum operateType(); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/aop/AfterSaleLogAspect.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/aop/AfterSaleLogAspect.java new file mode 100644 index 0000000..fb67a3f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/aop/AfterSaleLogAspect.java @@ -0,0 +1,133 @@ +package com.tashow.cloud.trade.framework.aftersale.core.aop; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderLogDO; +import com.tashow.cloud.trade.framework.aftersale.core.annotations.AfterSaleLog; +import com.tashow.cloud.trade.service.aftersale.AfterSaleLogService; +import com.tashow.cloud.trade.service.aftersale.bo.AfterSaleLogCreateReqBO; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; + +import jakarta.annotation.Resource; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static java.util.Collections.emptyMap; + +/** + * å”®åŽè®¢å•çš„æ“作记录的 AOP åˆ‡é¢ + * + * @author é™ˆè³ + * @since 2023/6/13 13:54 + */ +@Slf4j +@Aspect +public class AfterSaleLogAspect { + + /** + * ç”¨æˆ·ç¼–å· + * + * ç›®å‰çš„使用场景:支付回调时,需è¦å¼ºåˆ¶è®¾ç½®ä¸‹ç”¨æˆ·ç¼–å· + */ + private static final ThreadLocal USER_ID = new ThreadLocal<>(); + /** + * 用户类型 + */ + private static final ThreadLocal USER_TYPE = new ThreadLocal<>(); + /** + * 订å•ç¼–å· + */ + private static final ThreadLocal AFTER_SALE_ID = new ThreadLocal<>(); + /** + * æ“作å‰çš„çŠ¶æ€ + */ + private static final ThreadLocal BEFORE_STATUS = new ThreadLocal<>(); + /** + * æ“作åŽçš„çŠ¶æ€ + */ + private static final ThreadLocal AFTER_STATUS = new ThreadLocal<>(); + /** + * æ‹“å±•å‚æ•° Map,用于格å¼åŒ–æ“作内容 + */ + private static final ThreadLocal> EXTS = new ThreadLocal<>(); + + @Resource + private AfterSaleLogService afterSaleLogService; + + @AfterReturning(pointcut = "@annotation(afterSaleLog)") + public void doAfterReturning(JoinPoint joinPoint, AfterSaleLog afterSaleLog) { + try { + // 1.1 æ“作用户 + Integer userType = getUserType(); + Long userId = getUserId(); + // 1.2 å”®åŽä¿¡æ¯ + Long afterSaleId = AFTER_SALE_ID.get(); + if (afterSaleId == null) { // å¦‚æžœæœªè®¾ç½®ï¼Œåªæœ‰æ³¨è§£ï¼Œè¯´æ˜Žä¸éœ€è¦è®°å½•日志 + return; + } + Integer beforeStatus = BEFORE_STATUS.get(); + Integer afterStatus = AFTER_STATUS.get(); + Map exts = ObjectUtil.defaultIfNull(EXTS.get(), emptyMap()); + String content = StrUtil.format(afterSaleLog.operateType().getContent(), exts); + + // 2. 记录日志 + AfterSaleLogCreateReqBO createBO = new AfterSaleLogCreateReqBO() + .setUserId(userId).setUserType(userType) + .setAfterSaleId(afterSaleId).setBeforeStatus(beforeStatus).setAfterStatus(afterStatus) + .setOperateType(afterSaleLog.operateType().getType()).setContent(content); + afterSaleLogService.createAfterSaleLog(createBO); + } catch (Exception exception) { + log.error("[doAfterReturning][afterSaleLog({}) 日志记录错误]", toJsonString(afterSaleLog), exception); + } finally { + clear(); + } + } + + /** + * 获得用户类型 + * + * 如果没有,则约定为 {@link TradeOrderLogDO#getUserType()} 系统 + * + * @return 用户类型 + */ + private static Integer getUserType() { + return ObjectUtil.defaultIfNull(WebFrameworkUtils.getLoginUserType(), TradeOrderLogDO.USER_TYPE_SYSTEM); + } + + /** + * èŽ·å¾—ç”¨æˆ·ç¼–å· + * + * 如果没有,则约定为 {@link TradeOrderLogDO#getUserId()} 系统 + * + * @return 用户类型 + */ + private static Long getUserId() { + return ObjectUtil.defaultIfNull(WebFrameworkUtils.getLoginUserId(), TradeOrderLogDO.USER_ID_SYSTEM); + } + + public static void setAfterSale(Long id, Integer beforeStatus, Integer afterStatus, Map exts) { + AFTER_SALE_ID.set(id); + BEFORE_STATUS.set(beforeStatus); + AFTER_STATUS.set(afterStatus); + EXTS.set(exts); + } + + public static void setUserInfo(Long userId, Integer userType) { + USER_ID.set(userId); + USER_TYPE.set(userType); + } + + private static void clear() { + USER_ID.remove(); + USER_TYPE.remove(); + AFTER_SALE_ID.remove(); + BEFORE_STATUS.remove(); + AFTER_STATUS.remove(); + EXTS.remove(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/utils/AfterSaleLogUtils.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/utils/AfterSaleLogUtils.java new file mode 100644 index 0000000..4e4d83c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/aftersale/core/utils/AfterSaleLogUtils.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.trade.framework.aftersale.core.utils; + + +import com.tashow.cloud.trade.framework.aftersale.core.aop.AfterSaleLogAspect; + +import java.util.Map; + +/** + * æ“作日志工具类 + * ç›®å‰ä¸»è¦çš„作用,是æä¾›ç»™ä¸šåС代ç ï¼Œè®°å½•æ“作明细和拓展字段 + * + * @author èŠ‹é“æºç  + */ +public class AfterSaleLogUtils { + + public static void setAfterSaleInfo(Long id, Integer beforeStatus, Integer afterStatus) { + setAfterSaleInfo(id, beforeStatus, afterStatus, null); + } + + public static void setAfterSaleInfo(Long id, Integer beforeStatus, Integer afterStatus, + Map exts) { + AfterSaleLogAspect.setAfterSale(id, beforeStatus, afterStatus, exts); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/config/ExpressClientConfig.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/config/ExpressClientConfig.java new file mode 100644 index 0000000..35fe77b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/config/ExpressClientConfig.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.trade.framework.delivery.config; + +import com.tashow.cloud.trade.framework.delivery.core.client.ExpressClient; +import com.tashow.cloud.trade.framework.delivery.core.client.ExpressClientFactory; +import com.tashow.cloud.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(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/config/TradeExpressProperties.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/config/TradeExpressProperties.java new file mode 100644 index 0000000..8251209 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/config/TradeExpressProperties.java @@ -0,0 +1,89 @@ +package com.tashow.cloud.trade.framework.delivery.config; + +import com.tashow.cloud.trade.framework.delivery.core.enums.ExpressClientEnum; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; + +// TODO @芋艿:未æ¥è¦ä¸è¦æ”¾æ•°æ®åº“中?考虑 saas 多租户时,ä¸åŒç§Ÿæˆ·ä½¿ç”¨ä¸åŒçš„é…置? +/** + * 交易è¿è´¹å¿«é€’çš„é…置项 + * + * @author jason + */ +@Component +@ConfigurationProperties(prefix = "yudao.trade.express") +@Data +@Validated +public class TradeExpressProperties { + + /** + * 快递客户端 + * + * é»˜è®¤ä¸æä¾›ï¼Œéœ€è¦æé†’ç”¨æˆ·é…置一个快递æœåŠ¡å•†ã€‚ + */ + private ExpressClientEnum client = ExpressClientEnum.NOT_PROVIDE; + + /** + * 快递鸟é…ç½® + */ + @Valid + private KdNiaoConfig kdNiao; + /** + * 快递 100 é…ç½® + */ + @Valid + private Kd100Config kd100; + + /** + * 快递鸟é…置项目 + */ + @Data + public static class KdNiaoConfig { + + /** + * 快递鸟用户 ID + */ + @NotEmpty(message = "快递鸟用户 ID é…置项ä¸èƒ½ä¸ºç©º") + private String businessId; + /** + * 快递鸟 API Key + */ + @NotEmpty(message = "快递鸟 Api Key é…置项ä¸èƒ½ä¸ºç©º") + private String apiKey; + + /** + * æŽ¥å£æŒ‡ä»¤ + * + * 1. 1002:å…费版(åªèƒ½æŸ¥è¯¢ç”³é€šã€åœ†é€šå¿«é€’) + * 2. 8001:付费版 + */ + @NotEmpty(message = "RequestType é…置项ä¸èƒ½ä¸ºç©º") + private String requestType = "1002"; + + } + + /** + * 快递 100 é…置项 + */ + @Data + public static class Kd100Config { + + /** + * 快递 100 授æƒç  + */ + @NotEmpty(message = "快递 100 授æƒç é…置项ä¸èƒ½ä¸ºç©º") + private String customer; + /** + * 快递 100 æŽˆæƒ key + */ + @NotEmpty(message = "快递 100 æŽˆæƒ Key é…置项ä¸èƒ½ä¸ºç©º") + private String key; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/ExpressClient.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/ExpressClient.java new file mode 100644 index 0000000..63c343e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/ExpressClient.java @@ -0,0 +1,23 @@ +package com.tashow.cloud.trade.framework.delivery.core.client; + +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; +import com.tashow.cloud.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 getExpressTrackList(ExpressTrackQueryReqDTO reqDTO); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/ExpressClientFactory.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/ExpressClientFactory.java new file mode 100644 index 0000000..33398bb --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/ExpressClientFactory.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.trade.framework.delivery.core.client; + +import com.tashow.cloud.trade.framework.delivery.core.enums.ExpressClientEnum; + +/** + * 快递客户端工厂接å£ï¼šç”¨äºŽåˆ›å»ºå’Œç¼“存快递客户端 + * + * @author jason + */ +public interface ExpressClientFactory { + + /** + * 获å–默认的快递客户端 + */ + ExpressClient getDefaultExpressClient(); + + /** + * 通过枚举获å–快递客户端,如果ä¸å­˜åœ¨ï¼Œå°±åˆ›å»ºä¸€ä¸ªå¯¹åº”快递客户端 + * + * @param clientEnum 快递客户端枚举 + */ + ExpressClient getOrCreateExpressClient(ExpressClientEnum clientEnum); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/convert/ExpressQueryConvert.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/convert/ExpressQueryConvert.java new file mode 100644 index 0000000..3e65fdd --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/convert/ExpressQueryConvert.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.trade.framework.delivery.core.client.convert; + +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryReqDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryRespDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryReqDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface ExpressQueryConvert { + + ExpressQueryConvert INSTANCE = Mappers.getMapper(ExpressQueryConvert.class); + + List convertList(List list); + @Mapping(source = "acceptTime", target = "time") + @Mapping(source = "acceptStation", target = "content") + ExpressTrackRespDTO convert(KdNiaoExpressQueryRespDTO.ExpressTrack track); + + List convertList2(List list); + @Mapping(source = "context", target = "content") + ExpressTrackRespDTO convert(Kd100ExpressQueryRespDTO.ExpressTrack track); + + KdNiaoExpressQueryReqDTO convert(ExpressTrackQueryReqDTO dto); + + Kd100ExpressQueryReqDTO convert2(ExpressTrackQueryReqDTO dto); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/ExpressTrackQueryReqDTO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/ExpressTrackQueryReqDTO.java new file mode 100644 index 0000000..6f3f871 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/ExpressTrackQueryReqDTO.java @@ -0,0 +1,36 @@ +package com.tashow.cloud.trade.framework.delivery.core.client.dto; + +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import lombok.Data; + +/** + * 快递轨迹的查询 Req DTO + * + * @author jason + */ +@Data +public class ExpressTrackQueryReqDTO { + + /** + * 快递公å¸ç¼–ç  + *

+ * 对应 {@link DeliveryExpressDO#getCode()} + */ + private String expressCode; + + /** + * å‘货快递å•å· + */ + private String logisticsNo; + + /** + * æ”¶ã€å¯„件人的电è¯å·ç  + */ + private String phone; + + /** + * 自定义å称(顺丰专用) + */ + private String customerName; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/ExpressTrackRespDTO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/ExpressTrackRespDTO.java new file mode 100644 index 0000000..53f09d7 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/ExpressTrackRespDTO.java @@ -0,0 +1,25 @@ +package com.tashow.cloud.trade.framework.delivery.core.client.dto; + +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 快递查询的轨迹 Resp DTO + * + * @author jason + */ +@Data +public class ExpressTrackRespDTO { + + /** + * å‘生时间 + */ + private LocalDateTime time; + + /** + * å¿«é€’çŠ¶æ€ + */ + private String content; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryReqDTO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryReqDTO.java new file mode 100644 index 0000000..5d5592a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryReqDTO.java @@ -0,0 +1,33 @@ +package com.tashow.cloud.trade.framework.delivery.core.client.dto.kd100; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * 快递 100 快递查询 Req DTO + * + * @author jason + */ +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Kd100ExpressQueryReqDTO { + + /** + * 快递公å¸ç¼–ç  + */ + @JsonProperty("com") + private String expressCode; + + /** + * 快递å•å· + */ + @JsonProperty("num") + private String logisticsNo; + + /** + * æ”¶ã€å¯„件人的电è¯å·ç  + */ + private String phone; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java new file mode 100644 index 0000000..54cc4ec --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java @@ -0,0 +1,71 @@ +package com.tashow.cloud.trade.framework.delivery.core.client.dto.kd100; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; + +/** + * 快递 100 实时快递查询 Resp DTO + * + * å‚è§ å¿«é€’ 100 文档 + * + * @author jason + */ +@Data +public class Kd100ExpressQueryRespDTO { + + /** + * 快递公å¸ç¼–ç  + */ + @JsonProperty("com") + private String expressCompanyCode; + /** + * 快递å•å· + */ + @JsonProperty("nu") + private String logisticsNo; + /** + * 快递å•当å‰çŠ¶æ€ + */ + private String state; + + /** + * 查询结果 + * + * 失败返回 "false" + */ + private String result; + /** + * æŸ¥è¯¢ç»“æžœå¤±è´¥æ—¶çš„é”™è¯¯ä¿¡æ¯ + */ + private String message; + + /** + * 轨迹数组 + */ + @JsonProperty("data") + private List tracks; + + @Data + public static class ExpressTrack { + + /** + * 轨迹å‘生时间 + */ + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) + private LocalDateTime time; + + /** + * 轨迹æè¿° + */ + private String context; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryReqDTO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryReqDTO.java new file mode 100644 index 0000000..e8ecce3 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryReqDTO.java @@ -0,0 +1,38 @@ +package com.tashow.cloud.trade.framework.delivery.core.client.dto.kdniao; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * 快递鸟快递查询 Req DTO + * + * @author jason + */ +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class KdNiaoExpressQueryReqDTO { + + /** + * 快递公å¸ç¼–ç  + */ + @JsonProperty("ShipperCode") + private String expressCode; + /** + * 快递å•å· + */ + @JsonProperty("LogisticCode") + private String logisticsNo; + /** + * 订å•ç¼–å· + */ + @JsonProperty("OrderCode") + private String orderNo; + + /** + * 自定义å称(顺丰专用) + */ + @JsonProperty("CustomerName") + private String customerName; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java new file mode 100644 index 0000000..edbabc6 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java @@ -0,0 +1,99 @@ +package com.tashow.cloud.trade.framework.delivery.core.client.dto.kdniao; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; + +/** + * 快递鸟快递查询 Resp DTO + * + * å‚è§ å¿«é€’é¸ŸæŽ¥å£æ–‡æ¡£ + * + * @author jason + */ +@Data +public class KdNiaoExpressQueryRespDTO { + + /** + * 快递公å¸ç¼–ç  + */ + @JsonProperty("ShipperCode") + private String shipperCode; + + /** + * 快递å•å· + */ + @JsonProperty("LogisticCode") + private String logisticsNo; + + /** + * 订å•ç¼–å· + */ + @JsonProperty("OrderCode") + private String orderNo; + + /** + * 用户 ID + */ + @JsonProperty("EBusinessID") + private String businessId; + + /** + * 普通物æµçŠ¶æ€ + * + * 0 - æš‚æ— è½¨è¿¹ä¿¡æ¯ + * 1 - å·²æ½æ”¶ + * 2 - 在途中 + * 3 - 签收 + * 4 - 问题件 + * 5 - 转寄 + * 6 - 清关 + */ + @JsonProperty("State") + private String state; + + /** + * æˆåŠŸä¸Žå¦ + */ + @JsonProperty("Success") + private Boolean success; + /** + * 失败原因 + */ + @JsonProperty("Reason") + private String reason; + + /** + * 轨迹数组 + */ + @JsonProperty("Traces") + private List tracks; + + @Data + public static class ExpressTrack { + + /** + * å‘生时间 + */ + @JsonProperty("AcceptTime") + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime acceptTime; + + /** + * 轨迹æè¿° + */ + @JsonProperty("AcceptStation") + private String acceptStation; + + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/ExpressClientFactoryImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/ExpressClientFactoryImpl.java new file mode 100644 index 0000000..1e4f9f9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/ExpressClientFactoryImpl.java @@ -0,0 +1,54 @@ +package com.tashow.cloud.trade.framework.delivery.core.client.impl; + +import cn.hutool.core.lang.Assert; +import com.tashow.cloud.trade.framework.delivery.config.TradeExpressProperties; +import com.tashow.cloud.trade.framework.delivery.core.client.ExpressClient; +import com.tashow.cloud.trade.framework.delivery.core.client.ExpressClientFactory; +import com.tashow.cloud.trade.framework.delivery.core.client.impl.kd100.Kd100ExpressClient; +import com.tashow.cloud.trade.framework.delivery.core.client.impl.kdniao.KdNiaoExpressClient; +import com.tashow.cloud.trade.framework.delivery.core.enums.ExpressClientEnum; +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 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; + } +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/NoProvideExpressClient.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/NoProvideExpressClient.java new file mode 100644 index 0000000..0bad6b0 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/NoProvideExpressClient.java @@ -0,0 +1,24 @@ +package com.tashow.cloud.trade.framework.delivery.core.client.impl; + +import com.tashow.cloud.trade.framework.delivery.core.client.ExpressClient; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; +import com.tashow.cloud.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 getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) { + throw exception(EXPRESS_CLIENT_NOT_PROVIDE); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/kd100/Kd100ExpressClient.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/kd100/Kd100ExpressClient.java new file mode 100644 index 0000000..6b75399 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/kd100/Kd100ExpressClient.java @@ -0,0 +1,107 @@ +package com.tashow.cloud.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 com.tashow.cloud.trade.framework.delivery.config.TradeExpressProperties; +import com.tashow.cloud.trade.framework.delivery.core.client.ExpressClient; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.kd100.Kd100ExpressQueryReqDTO; +import com.tashow.cloud.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; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.util.Collections; +import java.util.List; +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 com.tashow.cloud.trade.framework.delivery.core.client.convert.ExpressQueryConvert.INSTANCE; + +/** + * 快递 100 客户端 + * + * @author jason + */ +@Slf4j +@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 TradeExpressProperties.Kd100Config config; + + /** + * 查询快递轨迹 + * + * @see æŽ¥å£æ–‡æ¡£ + * + * @param reqDTO æŸ¥è¯¢è¯·æ±‚å‚æ•° + * @return 快递轨迹 + */ + @Override + public List getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) { + // å‘起请求 + Kd100ExpressQueryReqDTO requestDTO = INSTANCE.convert2(reqDTO) + .setExpressCode(reqDTO.getExpressCode().toLowerCase()); + Kd100ExpressQueryRespDTO respDTO = httpRequest(REAL_TIME_QUERY_URL, requestDTO, + Kd100ExpressQueryRespDTO.class); + + // 处ç†ç»“æžœ + if (Objects.equals("false", respDTO.getResult())) { + throw exception(EXPRESS_API_QUERY_FAILED, respDTO.getMessage()); + } + if (CollUtil.isEmpty(respDTO.getTracks())) { + return Collections.emptyList(); + } + return INSTANCE.convertList2(respDTO.getTracks()); + } + + /** + * 快递 100 API 请求 + * + * @param url 请求 url + * @param req å¯¹åº”è¯·æ±‚çš„è¯·æ±‚å‚æ•° + * @param respClass 对应请求的å“应 class + * @param æ¯ä¸ªè¯·æ±‚的请求结构 Req DTO + * @param æ¯ä¸ªè¯·æ±‚çš„å“应结构 Resp DTO + */ + private Resp httpRequest(String url, Req req, Class respClass) { + // 请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + // 请求体 + String param = JsonUtils.toJsonString(req); + String sign = generateReqSign(param, config.getKey(), config.getCustomer()); // ç­¾å + MultiValueMap requestBody = new LinkedMultiValueMap<>(); + requestBody.add("customer", config.getCustomer()); + requestBody.add("sign", sign); + requestBody.add("param", param); + log.debug("[httpRequest][è¯·æ±‚å‚æ•°({})]", requestBody); + + // å‘é€è¯·æ±‚ + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + log.debug("[httpRequest][çš„å“应结果({})]", responseEntity); + // 处ç†å“应 + 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); + return HexUtil.encodeHexStr(DigestUtil.md5(plainText), false); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java new file mode 100644 index 0000000..1a19cb2 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java @@ -0,0 +1,127 @@ +package com.tashow.cloud.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.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.digest.DigestUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import com.tashow.cloud.trade.framework.delivery.config.TradeExpressProperties; +import com.tashow.cloud.trade.framework.delivery.core.client.ExpressClient; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryReqDTO; +import com.tashow.cloud.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; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.util.Collections; +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 com.tashow.cloud.trade.framework.delivery.core.client.convert.ExpressQueryConvert.INSTANCE; + +/** + * 快递鸟客户端 + * + * @author jason + */ +@Slf4j +@AllArgsConstructor +public class KdNiaoExpressClient implements ExpressClient { + + private static final String REAL_TIME_QUERY_URL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx"; + + private final RestTemplate restTemplate; + private final TradeExpressProperties.KdNiaoConfig config; + + /** + * 查询快递轨迹ã€å…费版】 + * + * ä»…æ”¯æŒ 3 家:申通快递ã€åœ†é€šé€Ÿé€’ã€ç™¾ä¸–快递 + * + * @see æŽ¥å£æ–‡æ¡£ + * + * @param reqDTO æŸ¥è¯¢è¯·æ±‚å‚æ•° + * @return 快递轨迹 + */ + @Override + public List getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) { + // å‘起请求 + KdNiaoExpressQueryReqDTO requestDTO = INSTANCE.convert(reqDTO) + .setExpressCode(reqDTO.getExpressCode().toUpperCase()); + if (ObjUtil.equal(requestDTO.getExpressCode(), "SF") + && StrUtil.isBlank(reqDTO.getCustomerName()) + && StrUtil.length(reqDTO.getPhone()) >= 4) { + requestDTO.setCustomerName(StrUtil.subSufByLength(reqDTO.getPhone(), 4)); + } + KdNiaoExpressQueryRespDTO respDTO = httpRequest(REAL_TIME_QUERY_URL, config.getRequestType(), + requestDTO, KdNiaoExpressQueryRespDTO.class); + + // 处ç†ç»“æžœ + if (respDTO == null || !respDTO.getSuccess()) { + throw exception(EXPRESS_API_QUERY_FAILED, respDTO == null ? "" : respDTO.getReason()); + } + if (CollUtil.isEmpty(respDTO.getTracks())) { + return Collections.emptyList(); + } + return INSTANCE.convertList(respDTO.getTracks()); + } + + /** + * 快递鸟 API 请求 + * + * @param url 请求 url + * @param requestType 对应的请求指令 (快递鸟的 RequestType) + * @param req å¯¹åº”è¯·æ±‚çš„è¯·æ±‚å‚æ•° + * @param respClass 对应请求的å“应 class + * @param æ¯ä¸ªè¯·æ±‚的请求结构 Req DTO + * @param æ¯ä¸ªè¯·æ±‚çš„å“应结构 Resp DTO + */ + private Resp httpRequest(String url, String requestType, Req req, Class respClass) { + // 请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + // 请求体 + String reqData = JsonUtils.toJsonString(req); + String dataSign = generateDataSign(reqData, config.getApiKey()); + MultiValueMap requestBody = new LinkedMultiValueMap<>(); + requestBody.add("RequestData", reqData); + requestBody.add("DataType", "2"); + requestBody.add("EBusinessID", config.getBusinessId()); + requestBody.add("DataSign", dataSign); + requestBody.add("RequestType", requestType); + log.debug("[httpRequest][RequestType({}) çš„è¯·æ±‚å‚æ•°({})]", requestType, requestBody); + + // å‘é€è¯·æ±‚ + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + log.debug("[httpRequest][RequestType({}) çš„å“应结果({})", requestType, responseEntity); + // 处ç†å“应 + if (!responseEntity.getStatusCode().is2xxSuccessful()) { + throw exception(EXPRESS_API_QUERY_ERROR); + } + return JsonUtils.parseObject(responseEntity.getBody(), respClass); + } + + /** + * 快递鸟生æˆè¯·æ±‚ç­¾å + * + * å‚è§ ç­¾å说明 + * + * @param reqData 请求实体 + * @param apiKey api Key + */ + private String generateDataSign(String reqData, String apiKey) { + String plainText = String.format("%s%s", reqData, apiKey); + return URLEncodeUtil.encode(Base64.encode(DigestUtil.md5Hex(plainText))); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/enums/ExpressClientEnum.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/enums/ExpressClientEnum.java new file mode 100644 index 0000000..d505136 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/delivery/core/enums/ExpressClientEnum.java @@ -0,0 +1,28 @@ +package com.tashow.cloud.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; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/config/TradeOrderConfig.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/config/TradeOrderConfig.java new file mode 100644 index 0000000..6559535 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/config/TradeOrderConfig.java @@ -0,0 +1,13 @@ +package com.tashow.cloud.trade.framework.order.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * @author LeeYan9 + * @since 2022-09-15 + */ +@Configuration +@EnableConfigurationProperties(TradeOrderProperties.class) +public class TradeOrderConfig { +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/config/TradeOrderProperties.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/config/TradeOrderProperties.java new file mode 100644 index 0000000..243380c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/config/TradeOrderProperties.java @@ -0,0 +1,51 @@ +package com.tashow.cloud.trade.framework.order.config; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.constraints.NotNull; + +import java.time.Duration; + +/** + * 交易订å•çš„é…置项 + * + * @author LeeYan9 + * @since 2022-09-15 + */ +@ConfigurationProperties(prefix = "yudao.trade.order") +@Data +@Validated +public class TradeOrderProperties { + + private static final String PAY_APP_KEY_DEFAULT = "mall"; + + /** + * 支付应用标识 + * + * 在 pay 模å—çš„ [æ”¯ä»˜ç®¡ç† -> 应用信æ¯] 里添加 + */ + @NotEmpty(message = "Pay 应用标识ä¸èƒ½ä¸ºç©º") + private String payAppKey = PAY_APP_KEY_DEFAULT; + + /** + * 支付超时时间 + */ + @NotNull(message = "支付超时时间ä¸èƒ½ä¸ºç©º") + private Duration payExpireTime; + + /** + * æ”¶è´§è¶…æ—¶æ—¶é—´ + */ + @NotNull(message = "æ”¶è´§è¶…æ—¶æ—¶é—´ä¸èƒ½ä¸ºç©º") + private Duration receiveExpireTime; + + /** + * 评论超时时间 + */ + @NotNull(message = "评论超时时间ä¸èƒ½ä¸ºç©º") + private Duration commentExpireTime; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/annotations/TradeOrderLog.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/annotations/TradeOrderLog.java new file mode 100644 index 0000000..0cb0f8a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/annotations/TradeOrderLog.java @@ -0,0 +1,31 @@ +package com.tashow.cloud.trade.framework.order.core.annotations; + +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderOperateTypeEnum; +import com.tashow.cloud.trade.framework.order.core.aop.TradeOrderLogAspect; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.METHOD; + +/** + * 交易订å•çš„æ“作日志 AOP 注解 + * + * @author é™ˆè³ + * @since 2023/7/6 15:37 + * @see TradeOrderLogAspect + */ +@Target({METHOD, ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface TradeOrderLog { + + /** + * æ“作类型 + */ + TradeOrderOperateTypeEnum operateType(); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/aop/TradeOrderLogAspect.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/aop/TradeOrderLogAspect.java new file mode 100644 index 0000000..106e935 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/aop/TradeOrderLogAspect.java @@ -0,0 +1,136 @@ +package com.tashow.cloud.trade.framework.order.core.aop; + + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderLogDO; +import com.tashow.cloud.trade.framework.order.core.annotations.TradeOrderLog; +import com.tashow.cloud.trade.service.order.TradeOrderLogService; +import com.tashow.cloud.trade.service.order.bo.TradeOrderLogCreateReqBO; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static java.util.Collections.emptyMap; + +/** + * 交易订å•çš„æ“作日志的记录 AOP åˆ‡é¢ + * + * @author é™ˆè³ + * @since 2023/6/13 13:54 + */ +@Component +@Aspect +@Slf4j +public class TradeOrderLogAspect { + + /** + * ç”¨æˆ·ç¼–å· + * + * ç›®å‰çš„使用场景:支付回调时,需è¦å¼ºåˆ¶è®¾ç½®ä¸‹ç”¨æˆ·ç¼–å· + */ + private static final ThreadLocal USER_ID = new ThreadLocal<>(); + /** + * 用户类型 + */ + private static final ThreadLocal USER_TYPE = new ThreadLocal<>(); + /** + * 订å•ç¼–å· + */ + private static final ThreadLocal ORDER_ID = new ThreadLocal<>(); + /** + * æ“作å‰çš„çŠ¶æ€ + */ + private static final ThreadLocal BEFORE_STATUS = new ThreadLocal<>(); + /** + * æ“作åŽçš„çŠ¶æ€ + */ + private static final ThreadLocal AFTER_STATUS = new ThreadLocal<>(); + /** + * æ‹“å±•å‚æ•° Map,用于格å¼åŒ–æ“作内容 + */ + private static final ThreadLocal> EXTS = new ThreadLocal<>(); + + @Resource + private TradeOrderLogService orderLogService; + + @AfterReturning("@annotation(orderLog)") + public void doAfterReturning(JoinPoint joinPoint, TradeOrderLog orderLog) { + try { + // 1.1 æ“作用户 + Integer userType = getUserType(); + Long userId = getUserId(); + // 1.2 订å•ä¿¡æ¯ + Long orderId = ORDER_ID.get(); + if (orderId == null) { // å¦‚æžœæœªè®¾ç½®ï¼Œåªæœ‰æ³¨è§£ï¼Œè¯´æ˜Žä¸éœ€è¦è®°å½•日志 + return; + } + Integer beforeStatus = BEFORE_STATUS.get(); + Integer afterStatus = AFTER_STATUS.get(); + Map exts = ObjectUtil.defaultIfNull(EXTS.get(), emptyMap()); + String content = StrUtil.format(orderLog.operateType().getContent(), exts); + + // 2. 记录日志 + TradeOrderLogCreateReqBO createBO = new TradeOrderLogCreateReqBO() + .setUserId(userId).setUserType(userType) + .setOrderId(orderId).setBeforeStatus(beforeStatus).setAfterStatus(afterStatus) + .setOperateType(orderLog.operateType().getType()).setContent(content); + orderLogService.createOrderLog(createBO); + } catch (Exception ex) { + log.error("[doAfterReturning][orderLog({}) è®¢å•æ—¥å¿—错误]", toJsonString(orderLog), ex); + } finally { + clear(); + } + } + + /** + * 获得用户类型 + * + * 如果没有,则约定为 {@link TradeOrderLogDO#getUserType()} 系统 + * + * @return 用户类型 + */ + private static Integer getUserType() { + return ObjectUtil.defaultIfNull(WebFrameworkUtils.getLoginUserType(), TradeOrderLogDO.USER_TYPE_SYSTEM); + } + + /** + * èŽ·å¾—ç”¨æˆ·ç¼–å· + * + * 如果没有,则约定为 {@link TradeOrderLogDO#getUserId()} 系统 + * + * @return 用户类型 + */ + private static Long getUserId() { + return ObjectUtil.defaultIfNull(WebFrameworkUtils.getLoginUserId(), TradeOrderLogDO.USER_ID_SYSTEM); + } + + public static void setOrderInfo(Long id, Integer beforeStatus, Integer afterStatus, Map exts) { + ORDER_ID.set(id); + BEFORE_STATUS.set(beforeStatus); + AFTER_STATUS.set(afterStatus); + EXTS.set(exts); + } + + public static void setUserInfo(Long userId, Integer userType) { + USER_ID.set(userId); + USER_TYPE.set(userType); + } + + private static void clear() { + USER_ID.remove(); + USER_TYPE.remove(); + ORDER_ID.remove(); + BEFORE_STATUS.remove(); + AFTER_STATUS.remove(); + EXTS.remove(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/utils/TradeOrderLogUtils.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/utils/TradeOrderLogUtils.java new file mode 100644 index 0000000..c5c90a8 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/order/core/utils/TradeOrderLogUtils.java @@ -0,0 +1,27 @@ +package com.tashow.cloud.trade.framework.order.core.utils; + +import com.tashow.cloud.trade.framework.order.core.aop.TradeOrderLogAspect; + +import java.util.Map; + +/** + * 交易订å•çš„æ“作日志 Utils + * + * @author èŠ‹é“æºç  + */ +public class TradeOrderLogUtils { + + public static void setOrderInfo(Long id, Integer beforeStatus, Integer afterStatus) { + TradeOrderLogAspect.setOrderInfo(id, beforeStatus, afterStatus, null); + } + + public static void setOrderInfo(Long id, Integer beforeStatus, Integer afterStatus, + Map exts) { + TradeOrderLogAspect.setOrderInfo(id, beforeStatus, afterStatus, exts); + } + + public static void setUserInfo(Long userId, Integer userType) { + TradeOrderLogAspect.setUserInfo(userId, userType); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/package-info.java new file mode 100644 index 0000000..c1fb394 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 trade 模å—çš„ framework å°è£… + * + * @author èŠ‹é“æºç  + */ +package com.tashow.cloud.trade.framework; diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/rpc/config/RpcConfiguration.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/rpc/config/RpcConfiguration.java new file mode 100644 index 0000000..45a201d --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/rpc/config/RpcConfiguration.java @@ -0,0 +1,41 @@ +package com.tashow.cloud.trade.framework.rpc.config; + +import cn.iocoder.yudao.module.member.api.address.MemberAddressApi; +import cn.iocoder.yudao.module.member.api.config.MemberConfigApi; +import cn.iocoder.yudao.module.member.api.level.MemberLevelApi; +import cn.iocoder.yudao.module.member.api.point.MemberPointApi; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; +import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; +import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi; +import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi; +import cn.iocoder.yudao.module.product.api.category.ProductCategoryApi; +import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; +import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi; +import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; +import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; +import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; +import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi; +import cn.iocoder.yudao.module.promotion.api.point.PointActivityApi; +import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi; +import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; +import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; +import cn.iocoder.yudao.module.system.api.social.SocialUserApi; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@EnableFeignClients(clients = { + BargainActivityApi.class, BargainRecordApi.class, CombinationRecordApi.class, + CouponApi.class, DiscountActivityApi.class, RewardActivityApi.class, SeckillActivityApi.class, PointActivityApi.class, + MemberUserApi.class, MemberPointApi.class, MemberLevelApi.class, MemberAddressApi.class, MemberConfigApi.class, + ProductSpuApi.class, ProductSkuApi.class, ProductCommentApi.class, ProductCategoryApi.class, + PayOrderApi.class, PayRefundApi.class, PayTransferApi.class, PayWalletApi.class, + AdminUserApi.class, NotifyMessageSendApi.class, SocialClientApi.class, SocialUserApi.class +}) +public class RpcConfiguration { +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/rpc/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/rpc/package-info.java new file mode 100644 index 0000000..a2ded57 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/rpc/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ + */ +package com.tashow.cloud.trade.framework.rpc; diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/security/config/SecurityConfiguration.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/security/config/SecurityConfiguration.java new file mode 100644 index 0000000..05e19b5 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/security/config/SecurityConfiguration.java @@ -0,0 +1,39 @@ +package com.tashow.cloud.trade.framework.security.config; + +import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; +import cn.iocoder.yudao.module.trade.enums.ApiConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; + +/** + * Trade 模å—çš„ Security é…ç½® + */ +@Configuration("tradeSecurityConfiguration") +public class SecurityConfiguration { + + @Bean("tradeAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + + @Override + public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { + // Swagger æŽ¥å£æ–‡æ¡£ + registry.requestMatchers("/v3/api-docs/**").permitAll() + .requestMatchers("/webjars/**").permitAll() + .requestMatchers("/swagger-ui").permitAll() + .requestMatchers("/swagger-ui/**").permitAll(); + // Spring Boot Actuator 的安全é…ç½® + registry.requestMatchers("/actuator").anonymous() + .requestMatchers("/actuator/**").anonymous(); + // Druid 监控 + registry.requestMatchers("/druid/**").anonymous(); + // RPC æœåŠ¡çš„å®‰å…¨é…ç½® + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); + } + + }; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/security/core/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/security/core/package-info.java new file mode 100644 index 0000000..5aa553c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/framework/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * å ä½ + */ +package com.tashow.cloud.trade.framework.security.core; diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/package-info.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/package-info.java new file mode 100644 index 0000000..bffd270 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/package-info.java @@ -0,0 +1,8 @@ +/** + * trade 模å—,product 模å—,主è¦å®žçް商å“相关功能 + * 例如:å“牌ã€å•†å“分类ã€spuã€sku等功能。 + * + * 1. Controller URL:以 /product/ 开头,é¿å…和其它 Module å†²çª + * 2. DataObject 表å:以 product_ 开头,方便在数æ®åº“中区分 + */ +package com.tashow.cloud.trade; diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleLogService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleLogService.java new file mode 100644 index 0000000..2e957e7 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleLogService.java @@ -0,0 +1,34 @@ +package com.tashow.cloud.trade.service.aftersale; + + +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleLogDO; +import com.tashow.cloud.trade.service.aftersale.bo.AfterSaleLogCreateReqBO; + +import java.util.List; + +/** + * äº¤æ˜“å”®åŽæ—¥å¿— Service æŽ¥å£ + * + * @author é™ˆè³ + * @since 2023/6/12 14:18 + */ +public interface AfterSaleLogService { + + /** + * åˆ›å»ºå”®åŽæ—¥å¿— + * + * @param createReqBO 日志记录 + * @author é™ˆè³ + * @since 2023/6/12 14:18 + */ + void createAfterSaleLog(AfterSaleLogCreateReqBO createReqBO); + + /** + * 获å–å”®åŽæ—¥å¿— + * + * @param afterSaleId å”®åŽç¼–å· + * @return å”®åŽæ—¥å¿— + */ + List getAfterSaleLogList(Long afterSaleId); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleLogServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleLogServiceImpl.java new file mode 100644 index 0000000..70edc5d --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleLogServiceImpl.java @@ -0,0 +1,36 @@ +package com.tashow.cloud.trade.service.aftersale; + +import com.tashow.cloud.trade.convert.aftersale.AfterSaleLogConvert; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleLogDO; +import com.tashow.cloud.trade.dal.mysql.aftersale.AfterSaleLogMapper; +import com.tashow.cloud.trade.service.aftersale.bo.AfterSaleLogCreateReqBO; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + * äº¤æ˜“å”®åŽæ—¥å¿— Service 实现类 + * + * @author èŠ‹é“æºç  + */ +@Service +@Validated +public class AfterSaleLogServiceImpl implements AfterSaleLogService { + + @Resource + private AfterSaleLogMapper afterSaleLogMapper; + + @Override + public void createAfterSaleLog(AfterSaleLogCreateReqBO createReqBO) { + AfterSaleLogDO afterSaleLog = AfterSaleLogConvert.INSTANCE.convert(createReqBO); + afterSaleLogMapper.insert(afterSaleLog); + } + + @Override + public List getAfterSaleLogList(Long afterSaleId) { + return afterSaleLogMapper.selectListByAfterSaleId(afterSaleId); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleService.java new file mode 100644 index 0000000..1bbe972 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleService.java @@ -0,0 +1,127 @@ +package com.tashow.cloud.trade.service.aftersale; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO; +import com.tashow.cloud.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; +import com.tashow.cloud.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleDO; + +/** + * å”®åŽè®¢å• Service æŽ¥å£ + * + * @author èŠ‹é“æºç  + */ +public interface AfterSaleService { + + /** + * ã€ç®¡ç†å‘˜ã€‘获得售åŽè®¢å•分页 + * + * @param pageReqVO 分页查询 + * @return å”®åŽè®¢å•分页 + */ + PageResult getAfterSalePage(AfterSalePageReqVO pageReqVO); + + /** + * ã€ä¼šå‘˜ã€‘获得售åŽè®¢å•分页 + * + * @param userId ç”¨æˆ·ç¼–å· + * @param pageParam åˆ†é¡µå‚æ•° + * @return å”®åŽè®¢å•分页 + */ + PageResult getAfterSalePage(Long userId, PageParam pageParam); + + /** + * ã€ä¼šå‘˜ã€‘获得售åŽå• + * + * @param userId ç”¨æˆ·ç¼–å· + * @param id å”®åŽç¼–å· + * @return å”®åŽè®¢å• + */ + AfterSaleDO getAfterSale(Long userId, Long id); + + /** + * ã€ç®¡ç†å‘˜ã€‘获得售åŽå• + * + * @param id å”®åŽç¼–å· + * @return å”®åŽè®¢å• + */ + AfterSaleDO getAfterSale(Long id); + + /** + * ã€ä¼šå‘˜ã€‘创建售åŽè®¢å• + * + * @param userId ä¼šå‘˜ç”¨æˆ·ç¼–å· + * @param createReqVO 创建 Request ä¿¡æ¯ + * @return å”®åŽç¼–å· + */ + Long createAfterSale(Long userId, AppAfterSaleCreateReqVO createReqVO); + + /** + * ã€ç®¡ç†å‘˜ã€‘åŒæ„å”®åŽè®¢å• + * + * @param userId 管ç†å‘˜ç”¨æˆ·ç¼–å· + * @param id å”®åŽç¼–å· + */ + void agreeAfterSale(Long userId, Long id); + + /** + * ã€ç®¡ç†å‘˜ã€‘æ‹’ç»å”®åŽè®¢å• + * + * @param userId 管ç†å‘˜ç”¨æˆ·ç¼–å· + * @param auditReqVO 审批 Request ä¿¡æ¯ + */ + void disagreeAfterSale(Long userId, AfterSaleDisagreeReqVO auditReqVO); + + /** + * ã€ä¼šå‘˜ã€‘退回货物 + * + * @param userId ä¼šå‘˜ç”¨æˆ·ç¼–å· + * @param deliveryReqVO 退货 Request ä¿¡æ¯ + */ + void deliveryAfterSale(Long userId, AppAfterSaleDeliveryReqVO deliveryReqVO); + + /** + * ã€ç®¡ç†å‘˜ã€‘确认收货 + * + * @param userId 管ç†å‘˜ç¼–å· + * @param id å”®åŽç¼–å· + */ + void receiveAfterSale(Long userId, Long id); + + /** + * ã€ç®¡ç†å‘˜ã€‘æ‹’ç»æ”¶è´§ + * + * @param userId 管ç†å‘˜ç”¨æˆ·ç¼–å· + * @param refuseReqVO æ‹’ç»æ”¶è´§ Request ä¿¡æ¯ + */ + void refuseAfterSale(Long userId, AfterSaleRefuseReqVO refuseReqVO); + + /** + * ã€ç®¡ç†å‘˜ã€‘确认退款 + * + * @param userId 管ç†å‘˜ç”¨æˆ·ç¼–å· + * @param userIp 管ç†å‘˜ç”¨æˆ· IP + * @param id å”®åŽç¼–å· + */ + void refundAfterSale(Long userId, String userIp, Long id); + + /** + * ã€ä¼šå‘˜ã€‘å–æ¶ˆå”®åŽ + * + * @param userId ä¼šå‘˜ç”¨æˆ·ç¼–å· + * @param id å”®åŽç¼–å· + */ + void cancelAfterSale(Long userId, Long id); + + /** + * ã€ä¼šå‘˜ã€‘获得正在进行中的售åŽè®¢å•æ•°é‡ + * + * @param userId ç”¨æˆ·ç¼–å· + * @return æ•°é‡ + */ + Long getApplyingAfterSaleCount(Long userId); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleServiceImpl.java new file mode 100644 index 0000000..9e1e542 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/AfterSaleServiceImpl.java @@ -0,0 +1,430 @@ +package com.tashow.cloud.trade.service.aftersale; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO; +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; +import com.tashow.cloud.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO; +import com.tashow.cloud.trade.controller.app.aftersale.vo.AppAfterSaleCreateReqVO; +import com.tashow.cloud.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO; +import com.tashow.cloud.trade.convert.aftersale.AfterSaleConvert; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.tashow.cloud.trade.dal.mysql.aftersale.AfterSaleMapper; +import com.tashow.cloud.trade.dal.redis.no.TradeNoRedisDAO; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleTypeEnum; +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.framework.aftersale.core.annotations.AfterSaleLog; +import com.tashow.cloud.trade.framework.aftersale.core.utils.AfterSaleLogUtils; +import com.tashow.cloud.trade.framework.order.config.TradeOrderProperties; +import com.tashow.cloud.trade.service.delivery.DeliveryExpressService; +import com.tashow.cloud.trade.service.order.TradeOrderQueryService; +import com.tashow.cloud.trade.service.order.TradeOrderUpdateService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; + +/** + * å”®åŽè®¢å• Service 实现类 + * + * @author èŠ‹é“æºç  + */ +@Slf4j +@Service +@Validated +public class AfterSaleServiceImpl implements AfterSaleService { + + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ– + private TradeOrderUpdateService tradeOrderUpdateService; + @Resource + private TradeOrderQueryService tradeOrderQueryService; + @Resource + private DeliveryExpressService deliveryExpressService; + + @Resource + private AfterSaleMapper tradeAfterSaleMapper; + @Resource + private TradeNoRedisDAO tradeNoRedisDAO; + + @Resource + private PayRefundApi payRefundApi; + @Resource + private CombinationRecordApi combinationRecordApi; + + @Resource + private TradeOrderProperties tradeOrderProperties; + + @Override + public PageResult getAfterSalePage(AfterSalePageReqVO pageReqVO) { + return tradeAfterSaleMapper.selectPage(pageReqVO); + } + + @Override + public PageResult getAfterSalePage(Long userId, PageParam pageParam) { + return tradeAfterSaleMapper.selectPage(userId, pageParam); + } + + @Override + public AfterSaleDO getAfterSale(Long userId, Long id) { + return tradeAfterSaleMapper.selectByIdAndUserId(id, userId); + } + + @Override + public AfterSaleDO getAfterSale(Long id) { + return tradeAfterSaleMapper.selectById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @AfterSaleLog(operateType = AfterSaleOperateTypeEnum.MEMBER_CREATE) + public Long createAfterSale(Long userId, AppAfterSaleCreateReqVO createReqVO) { + // 第一步,å‰ç½®æ ¡éªŒ + TradeOrderItemDO tradeOrderItem = validateOrderItemApplicable(userId, createReqVO); + + // 第二步,存储售åŽè®¢å• + AfterSaleDO afterSale = createAfterSale(createReqVO, tradeOrderItem); + return afterSale.getId(); + } + + /** + * 校验交易订å•项是å¦å¯ä»¥ç”³è¯·å”®åŽ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param createReqVO å”®åŽåˆ›å»ºä¿¡æ¯ + * @return 交易订å•项 + */ + private TradeOrderItemDO validateOrderItemApplicable(Long userId, AppAfterSaleCreateReqVO createReqVO) { + // 校验订å•项存在 + TradeOrderItemDO orderItem = tradeOrderQueryService.getOrderItem(userId, createReqVO.getOrderItemId()); + if (orderItem == null) { + throw exception(ORDER_ITEM_NOT_FOUND); + } + // 已申请售åŽï¼Œä¸å…许å†å‘èµ·å”®åŽç”³è¯· + if (!TradeOrderItemAfterSaleStatusEnum.isNone(orderItem.getAfterSaleStatus())) { + throw exception(AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED); + } + // 申请的退款金é¢ï¼Œä¸èƒ½è¶…过商å“的价格 + if (createReqVO.getRefundPrice() > orderItem.getPayPrice()) { + throw exception(AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR); + } + + // 校验订å•存在 + TradeOrderDO order = tradeOrderQueryService.getOrder(userId, orderItem.getOrderId()); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + // TODO 芋艿:超过一定时间,ä¸å…è®¸å”®åŽ + // 已喿¶ˆï¼Œæ— æ³•å‘èµ·å”®åŽ + if (TradeOrderStatusEnum.isCanceled(order.getStatus())) { + throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED); + } + // 未支付,无法å‘èµ·å”®åŽ + if (!TradeOrderStatusEnum.havePaid(order.getStatus())) { + throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID); + } + // 如果是ã€é€€è´§é€€æ¬¾ã€‘的情况,需è¦é¢å¤–校验是å¦å‘è´§ + if (createReqVO.getWay().equals(AfterSaleWayEnum.RETURN_AND_REFUND.getWay()) + && !TradeOrderStatusEnum.haveDelivered(order.getStatus())) { + throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED); + } + // 如果是拼团订å•,则进行中ä¸å…è®¸å”®åŽ + if (TradeOrderTypeEnum.isCombination(order.getType())) { + CombinationRecordRespDTO combinationRecord = combinationRecordApi.getCombinationRecordByOrderId( + order.getUserId(), order.getId()).getCheckedData(); + if (combinationRecord != null && CombinationRecordStatusEnum.isInProgress(combinationRecord.getStatus())) { + throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_COMBINATION_IN_PROGRESS); + } + } + return orderItem; + } + + private AfterSaleDO createAfterSale(AppAfterSaleCreateReqVO createReqVO, + TradeOrderItemDO orderItem) { + // 创建售åŽå• + AfterSaleDO afterSale = AfterSaleConvert.INSTANCE.convert(createReqVO, orderItem); + afterSale.setNo(tradeNoRedisDAO.generate(TradeNoRedisDAO.AFTER_SALE_NO_PREFIX)); + afterSale.setStatus(AfterSaleStatusEnum.APPLY.getStatus()); + // æ ‡è®°æ˜¯å”®ä¸­è¿˜æ˜¯å”®åŽ + TradeOrderDO order = tradeOrderQueryService.getOrder(orderItem.getUserId(), orderItem.getOrderId()); + afterSale.setOrderNo(order.getNo()); // 记录 orderNo è®¢å•æµæ°´ï¼Œæ–¹ä¾¿åŽç»­æ£€ç´¢ + afterSale.setType(TradeOrderStatusEnum.isCompleted(order.getStatus()) + ? AfterSaleTypeEnum.AFTER_SALE.getType() : AfterSaleTypeEnum.IN_SALE.getType()); + tradeAfterSaleMapper.insert(afterSale); + + // 更新交易订å•项的售åŽçŠ¶æ€ + tradeOrderUpdateService.updateOrderItemWhenAfterSaleCreate(orderItem.getId(), afterSale.getId()); + + // è®°å½•å”®åŽæ—¥å¿— + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), null, + AfterSaleStatusEnum.APPLY.getStatus()); + + // TODO å‘é€å”®åŽæ¶ˆæ¯ + return afterSale; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_AGREE_APPLY) + public void agreeAfterSale(Long userId, Long id) { + // 校验售åŽå•å­˜åœ¨ï¼Œå¹¶çŠ¶æ€æœªå®¡æ‰¹ + AfterSaleDO afterSale = validateAfterSaleAuditable(id); + + // æ›´æ–°å”®åŽå•çš„çŠ¶æ€ + // 情况一:退款:标记为 WAIT_REFUND 状æ€ã€‚åŽç»­ç­‰é€€æ¬¾å‘èµ·æˆåŠŸåŽï¼Œåœ¨æ ‡è®°ä¸º COMPLETE çŠ¶æ€ + // 情况二:退货退款:需è¦ç­‰ç”¨æˆ·é€€è´§åŽï¼Œæ‰èƒ½å‘起退款 + Integer newStatus = afterSale.getWay().equals(AfterSaleWayEnum.REFUND.getWay()) ? + AfterSaleStatusEnum.WAIT_REFUND.getStatus() : AfterSaleStatusEnum.SELLER_AGREE.getStatus(); + updateAfterSaleStatus(afterSale.getId(), AfterSaleStatusEnum.APPLY.getStatus(), new AfterSaleDO() + .setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now())); + + // è®°å½•å”®åŽæ—¥å¿— + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus); + + // TODO å‘é€å”®åŽæ¶ˆæ¯ + } + + @Override + @Transactional(rollbackFor = Exception.class) + @AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_DISAGREE_APPLY) + public void disagreeAfterSale(Long userId, AfterSaleDisagreeReqVO auditReqVO) { + // 校验售åŽå•å­˜åœ¨ï¼Œå¹¶çŠ¶æ€æœªå®¡æ‰¹ + AfterSaleDO afterSale = validateAfterSaleAuditable(auditReqVO.getId()); + + // æ›´æ–°å”®åŽå•çš„çŠ¶æ€ + Integer newStatus = AfterSaleStatusEnum.SELLER_DISAGREE.getStatus(); + updateAfterSaleStatus(afterSale.getId(), AfterSaleStatusEnum.APPLY.getStatus(), new AfterSaleDO() + .setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now()) + .setAuditReason(auditReqVO.getAuditReason())); + + // è®°å½•å”®åŽæ—¥å¿— + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus); + + // TODO å‘é€å”®åŽæ¶ˆæ¯ + + // 更新交易订å•项的售åŽçжæ€ä¸ºã€æœªç”³è¯·ã€‘ + tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId()); + } + + /** + * 校验售åŽå•是å¦å¯å®¡æ‰¹ï¼ˆåŒæ„å”®åŽã€æ‹’ç»å”®åŽï¼‰ + * + * @param id å”®åŽç¼–å· + * @return å”®åŽå• + */ + private AfterSaleDO validateAfterSaleAuditable(Long id) { + AfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id); + if (afterSale == null) { + throw exception(AFTER_SALE_NOT_FOUND); + } + if (ObjectUtil.notEqual(afterSale.getStatus(), AfterSaleStatusEnum.APPLY.getStatus())) { + throw exception(AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY); + } + return afterSale; + } + + private void updateAfterSaleStatus(Long id, Integer status, AfterSaleDO updateObj) { + int updateCount = tradeAfterSaleMapper.updateByIdAndStatus(id, status, updateObj); + if (updateCount == 0) { + throw exception(AFTER_SALE_UPDATE_STATUS_FAIL); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @AfterSaleLog(operateType = AfterSaleOperateTypeEnum.MEMBER_DELIVERY) + public void deliveryAfterSale(Long userId, AppAfterSaleDeliveryReqVO deliveryReqVO) { + // 校验售åŽå•å­˜åœ¨ï¼Œå¹¶çŠ¶æ€æœªé€€è´§ + AfterSaleDO afterSale = tradeAfterSaleMapper.selectByIdAndUserId(deliveryReqVO.getId(), userId); + if (afterSale == null) { + throw exception(AFTER_SALE_NOT_FOUND); + } + if (ObjectUtil.notEqual(afterSale.getStatus(), AfterSaleStatusEnum.SELLER_AGREE.getStatus())) { + throw exception(AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE); + } + DeliveryExpressDO express = deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId()); + + // æ›´æ–°å”®åŽå•的物æµä¿¡æ¯ + updateAfterSaleStatus(afterSale.getId(), AfterSaleStatusEnum.SELLER_AGREE.getStatus(), new AfterSaleDO() + .setStatus(AfterSaleStatusEnum.BUYER_DELIVERY.getStatus()) + .setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo()) + .setDeliveryTime(LocalDateTime.now())); + + // è®°å½•å”®åŽæ—¥å¿— + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), + AfterSaleStatusEnum.BUYER_DELIVERY.getStatus(), + MapUtil.builder().put("deliveryName", express.getName()) + .put("logisticsNo", deliveryReqVO.getLogisticsNo()).build()); + + // TODO å‘é€å”®åŽæ¶ˆæ¯ + } + + @Override + @Transactional(rollbackFor = Exception.class) + @AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_AGREE_RECEIVE) + public void receiveAfterSale(Long userId, Long id) { + // 校验售åŽå•存在,并状æ€ä¸ºå·²é€€è´§ + AfterSaleDO afterSale = validateAfterSaleReceivable(id); + + // æ›´æ–°å”®åŽå•çš„çŠ¶æ€ + updateAfterSaleStatus(afterSale.getId(), AfterSaleStatusEnum.BUYER_DELIVERY.getStatus(), new AfterSaleDO() + .setStatus(AfterSaleStatusEnum.WAIT_REFUND.getStatus()).setReceiveTime(LocalDateTime.now())); + + // è®°å½•å”®åŽæ—¥å¿— + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), + AfterSaleStatusEnum.WAIT_REFUND.getStatus()); + + // TODO å‘é€å”®åŽæ¶ˆæ¯ + } + + @Override + @Transactional(rollbackFor = Exception.class) + @AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_DISAGREE_RECEIVE) + public void refuseAfterSale(Long userId, AfterSaleRefuseReqVO refuseReqVO) { + // 校验售åŽå•存在,并状æ€ä¸ºå·²é€€è´§ + AfterSaleDO afterSale = tradeAfterSaleMapper.selectById(refuseReqVO.getId()); + if (afterSale == null) { + throw exception(AFTER_SALE_NOT_FOUND); + } + if (ObjectUtil.notEqual(afterSale.getStatus(), AfterSaleStatusEnum.BUYER_DELIVERY.getStatus())) { + throw exception(AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY); + } + + // æ›´æ–°å”®åŽå•çš„çŠ¶æ€ + updateAfterSaleStatus(afterSale.getId(), AfterSaleStatusEnum.BUYER_DELIVERY.getStatus(), new AfterSaleDO() + .setStatus(AfterSaleStatusEnum.SELLER_REFUSE.getStatus()).setReceiveTime(LocalDateTime.now()) + .setReceiveReason(refuseReqVO.getRefuseMemo())); + + // è®°å½•å”®åŽæ—¥å¿— + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), + AfterSaleStatusEnum.SELLER_REFUSE.getStatus(), + MapUtil.of("reason", refuseReqVO.getRefuseMemo())); + + // TODO å‘é€å”®åŽæ¶ˆæ¯ + + // 更新交易订å•项的售åŽçжæ€ä¸ºã€æœªç”³è¯·ã€‘ + tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId()); + } + + /** + * 校验售åŽå•是å¦å¯æ”¶è´§ï¼Œå³å¤„于买家已å‘è´§ + * + * @param id å”®åŽç¼–å· + * @return å”®åŽå• + */ + private AfterSaleDO validateAfterSaleReceivable(Long id) { + AfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id); + if (afterSale == null) { + throw exception(AFTER_SALE_NOT_FOUND); + } + if (ObjectUtil.notEqual(afterSale.getStatus(), AfterSaleStatusEnum.BUYER_DELIVERY.getStatus())) { + throw exception(AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY); + } + return afterSale; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @AfterSaleLog(operateType = AfterSaleOperateTypeEnum.ADMIN_REFUND) + public void refundAfterSale(Long userId, String userIp, Long id) { + // 校验售åŽå•的状æ€ï¼Œå¹¶çжæ€å¾…退款 + AfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id); + if (afterSale == null) { + throw exception(AFTER_SALE_NOT_FOUND); + } + if (ObjectUtil.notEqual(afterSale.getStatus(), AfterSaleStatusEnum.WAIT_REFUND.getStatus())) { + throw exception(AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND); + } + + // å‘起退款å•。注æ„,需è¦åœ¨äº‹åŠ¡æäº¤åŽï¼Œå†è¿›è¡Œå‘起,é¿å…é‡å¤å‘èµ· + createPayRefund(userIp, afterSale); + + // æ›´æ–°å”®åŽå•的状æ€ä¸ºã€å·²å®Œæˆã€‘ + updateAfterSaleStatus(afterSale.getId(), AfterSaleStatusEnum.WAIT_REFUND.getStatus(), new AfterSaleDO() + .setStatus(AfterSaleStatusEnum.COMPLETE.getStatus()).setRefundTime(LocalDateTime.now())); + + // è®°å½•å”®åŽæ—¥å¿— + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), + AfterSaleStatusEnum.COMPLETE.getStatus()); + + // TODO å‘é€å”®åŽæ¶ˆæ¯ + + // 更新交易订å•项的售åŽçжæ€ä¸ºã€å·²å®Œæˆã€‘ + tradeOrderUpdateService.updateOrderItemWhenAfterSaleSuccess(afterSale.getOrderItemId(), afterSale.getRefundPrice()); + } + + private void createPayRefund(String userIp, AfterSaleDO afterSale) { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + + @Override + public void afterCommit() { + // åˆ›å»ºé€€æ¬¾å• + PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties) + .setReason(StrUtil.format("退款ã€{}】", afterSale.getSpuName()));; + Long payRefundId = payRefundApi.createRefund(createReqDTO).getCheckedData(); + // æ›´æ–°å”®åŽå•的退款å•å· + tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId)); + } + }); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @AfterSaleLog(operateType = AfterSaleOperateTypeEnum.MEMBER_CANCEL) + public void cancelAfterSale(Long userId, Long id) { + // 校验售åŽå•的状æ€ï¼Œå¹¶çжæ€å¾…退款 + AfterSaleDO afterSale = tradeAfterSaleMapper.selectByIdAndUserId(id, userId); + if (afterSale == null) { + throw exception(AFTER_SALE_NOT_FOUND); + } + if (!ObjectUtils.equalsAny(afterSale.getStatus(), AfterSaleStatusEnum.APPLY.getStatus(), + AfterSaleStatusEnum.SELLER_AGREE.getStatus(), + AfterSaleStatusEnum.BUYER_DELIVERY.getStatus())) { + throw exception(AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY); + } + + // æ›´æ–°å”®åŽå•的状æ€ä¸ºã€å·²å–消】 + updateAfterSaleStatus(afterSale.getId(), afterSale.getStatus(), new AfterSaleDO() + .setStatus(AfterSaleStatusEnum.BUYER_CANCEL.getStatus())); + + // è®°å½•å”®åŽæ—¥å¿— + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), + AfterSaleStatusEnum.BUYER_CANCEL.getStatus()); + + // TODO å‘é€å”®åŽæ¶ˆæ¯ + + // 更新交易订å•项的售åŽçжæ€ä¸ºã€æœªç”³è¯·ã€‘ + tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId()); + } + + @Override + public Long getApplyingAfterSaleCount(Long userId) { + return tradeAfterSaleMapper.selectCountByUserIdAndStatus(userId, AfterSaleStatusEnum.APPLYING_STATUSES); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/bo/AfterSaleLogCreateReqBO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/bo/AfterSaleLogCreateReqBO.java new file mode 100644 index 0000000..842ce12 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/aftersale/bo/AfterSaleLogCreateReqBO.java @@ -0,0 +1,57 @@ +package com.tashow.cloud.trade.service.aftersale.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * å”®åŽæ—¥å¿—的创建 Request BO + * + * @author é™ˆè³ + * @since 2023/6/19 09:54 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AfterSaleLogCreateReqBO { + + /** + * ç”¨æˆ·ç¼–å· + */ + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + /** + * 用户类型 + */ + @NotNull(message = "用户类型ä¸èƒ½ä¸ºç©º") + private Integer userType; + + /** + * å”®åŽç¼–å· + */ + @NotNull(message = "å”®åŽç¼–å·ä¸èƒ½ä¸ºç©º") + private Long afterSaleId; + /** + * æ“作å‰çŠ¶æ€ + */ + private Integer beforeStatus; + /** + * æ“作åŽçŠ¶æ€ + */ + @NotNull(message = "æ“作åŽçš„状æ€ä¸èƒ½ä¸ºç©º") + private Integer afterStatus; + + /** + * æ“作类型 + */ + @NotNull(message = "æ“作类型ä¸èƒ½ä¸ºç©º") + private Integer operateType; + /** + * æ“作明细 + */ + @NotEmpty(message = "æ“作明细ä¸èƒ½ä¸ºç©º") + private String content; +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageRecordService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageRecordService.java new file mode 100644 index 0000000..fc4825a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageRecordService.java @@ -0,0 +1,158 @@ +package com.tashow.cloud.trade.service.brokerage; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByPriceRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankPageReqVO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageRecordDO; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageAddReqBO; +import com.tashow.cloud.trade.service.brokerage.bo.UserBrokerageSummaryRespBO; +import jakarta.validation.Valid; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 佣金记录 Service æŽ¥å£ + * + * @author owen + */ +public interface BrokerageRecordService { + + /** + * 获得佣金记录 + * + * @param id ç¼–å· + * @return 佣金记录 + */ + BrokerageRecordDO getBrokerageRecord(Long id); + + /** + * 获得佣金记录分页 + * + * @param pageReqVO 分页查询 + * @return 佣金记录分页 + */ + PageResult getBrokerageRecordPage(BrokerageRecordPageReqVO pageReqVO); + + /** + * 增加佣金ã€å¤šçº§åˆ†ä½£ã€‘ + * + * @param userId ä¼šå‘˜ç¼–å· + * @param bizType 业务类型 + * @param list è¯·æ±‚å‚æ•°åˆ—表 + */ + void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, @Valid List list); + + /** + * 增加佣金ã€åªé’ˆå¯¹è‡ªå·±ã€‘ + * + * @param userId ä¼šå‘˜ç¼–å· + * @param bizType 业务类型 + * @param bizId ä¸šåŠ¡ç¼–å· + * @param brokeragePrice 佣金 + * @param title 标题 + */ + void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title); + + /** + * å‡å°‘佣金ã€åªé’ˆå¯¹è‡ªå·±ã€‘ + * + * @param userId ä¼šå‘˜ç¼–å· + * @param bizType 业务类型 + * @param bizId ä¸šåŠ¡ç¼–å· + * @param brokeragePrice 佣金 + * @param title 标题 + */ + default void reduceBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title) { + addBrokerage(userId, bizType, bizId, -brokeragePrice, title); + } + + /** + * å–æ¶ˆä½£é‡‘:将佣金记录,状æ€ä¿®æ”¹ä¸ºå·²å¤±æ•ˆ + * + * @param bizType 业务类型 + * @param bizId ä¸šåŠ¡ç¼–å· + */ + void cancelBrokerage(BrokerageRecordBizTypeEnum bizType, String bizId); + + /** + * 解冻佣金:将待结算的佣金记录,状æ€ä¿®æ”¹ä¸ºå·²ç»“ç®— + * + * @return è§£å†»ä½£é‡‘çš„æ•°é‡ + */ + int unfreezeRecord(); + + /** + * 按照 userId,汇总æ¯ä¸ªç”¨æˆ·çš„佣金 + * + * @param userIds ç”¨æˆ·ç¼–å· + * @param bizType 业务类型 + * @param status ä½£é‡‘çŠ¶æ€ + * @return 用户佣金汇总 List + */ + List getUserBrokerageSummaryListByUserId(Collection userIds, + Integer bizType, Integer status); + + /** + * 按照 userId,汇总æ¯ä¸ªç”¨æˆ·çš„佣金 + * + * @param userIds ç”¨æˆ·ç¼–å· + * @param bizType 业务类型 + * @param status ä½£é‡‘çŠ¶æ€ + * @return 用户佣金汇总 Map + */ + default Map getUserBrokerageSummaryMapByUserId(Collection userIds, + Integer bizType, Integer status) { + return convertMap(getUserBrokerageSummaryListByUserId(userIds, bizType, status), + UserBrokerageSummaryRespBO::getUserId); + } + + /** + * 获得用户佣金åˆè®¡ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param bizType 业务类型 + * @param status çŠ¶æ€ + * @param beginTime 开始时间 + * @param endTime 截止时间 + * @return 用户佣金åˆè®¡ + */ + Integer getSummaryPriceByUserId(Long userId, BrokerageRecordBizTypeEnum bizType, BrokerageRecordStatusEnum status, + LocalDateTime beginTime, LocalDateTime endTime); + + /** + * 获得用户佣金排行分页列表(基于佣金总数) + * + * @param pageReqVO 分页查询 + * @return 排行榜分页 + */ + PageResult getBrokerageUserChildSummaryPageByPrice( + AppBrokerageUserRankPageReqVO pageReqVO); + + /** + * 获å–用户的排å(基于佣金总数) + * + * @param userId ç”¨æˆ·ç¼–å· + * @param times 时间范围 + * @return 用户的排å + */ + Integer getUserRankByPrice(Long userId, LocalDateTime[] times); + + /** + * 计算商å“被购买åŽï¼ŒæŽ¨å¹¿å‘˜å¯ä»¥å¾—到的佣金 + * + * @param userId ç”¨æˆ·ç¼–å· + * @param spuId 商å“ç¼–å· + * @return 用户佣金 + */ + AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageRecordServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageRecordServiceImpl.java new file mode 100644 index 0000000..223e005 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageRecordServiceImpl.java @@ -0,0 +1,370 @@ +package com.tashow.cloud.trade.service.brokerage; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.*; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByPriceRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankPageReqVO; +import com.tashow.cloud.trade.convert.brokerage.BrokerageRecordConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageRecordDO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; +import com.tashow.cloud.trade.dal.mysql.brokerage.BrokerageRecordMapper; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageAddReqBO; +import com.tashow.cloud.trade.service.brokerage.bo.UserBrokerageSummaryRespBO; +import com.tashow.cloud.trade.service.config.TradeConfigService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMinValue; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH; + +/** + * 佣金记录 Service 实现类 + * + * @author owen + */ +@Slf4j +@Service +@Validated +public class BrokerageRecordServiceImpl implements BrokerageRecordService { + + @Resource + private BrokerageRecordMapper brokerageRecordMapper; + @Resource + private TradeConfigService tradeConfigService; + @Resource + private BrokerageUserService brokerageUserService; + + @Resource + private ProductSpuApi productSpuApi; + @Resource + private ProductSkuApi productSkuApi; + + @Override + public BrokerageRecordDO getBrokerageRecord(Long id) { + return brokerageRecordMapper.selectById(id); + } + + @Override + public PageResult getBrokerageRecordPage(BrokerageRecordPageReqVO pageReqVO) { + return brokerageRecordMapper.selectPage(pageReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, List list) { + TradeConfigDO memberConfig = tradeConfigService.getTradeConfig(); + // 0 未å¯ç”¨åˆ†é”€åŠŸèƒ½ + if (memberConfig == null || !BooleanUtil.isTrue(memberConfig.getBrokerageEnabled())) { + log.error("[addBrokerage][增加佣金失败:brokerageEnabled 未é…置,userId({}) bizType({}) list({})", userId, bizType, list); + return; + } + + // 1.1 获得一级推广人 + BrokerageUserDO firstUser = brokerageUserService.getBindBrokerageUser(userId); + if (firstUser == null || !BooleanUtil.isTrue(firstUser.getBrokerageEnabled())) { + return; + } + // 1.2 计算一级分佣 + addBrokerage(firstUser, list, memberConfig.getBrokerageFrozenDays(), memberConfig.getBrokerageFirstPercent(), + bizType, 1); + + // 2.1 获得二级推广员 + if (firstUser.getBindUserId() == null) { + return; + } + BrokerageUserDO secondUser = brokerageUserService.getBrokerageUser(firstUser.getBindUserId()); + if (secondUser == null || !BooleanUtil.isTrue(secondUser.getBrokerageEnabled())) { + return; + } + // 2.2 计算二级分佣 + addBrokerage(secondUser, list, memberConfig.getBrokerageFrozenDays(), memberConfig.getBrokerageSecondPercent(), + bizType, 2); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelBrokerage(BrokerageRecordBizTypeEnum bizType, String bizId) { + List records = brokerageRecordMapper.selectListByBizTypeAndBizId(bizType.getType(), bizId); + if (CollUtil.isEmpty(records)) { + log.error("[cancelBrokerage][bizId({}) bizType({}) 更新为已失效失败:记录ä¸å­˜åœ¨]", bizId, bizType); + return; + } + + records.forEach(record -> { + // 1. 更新佣金记录为已失效 + BrokerageRecordDO updateObj = new BrokerageRecordDO().setStatus(BrokerageRecordStatusEnum.CANCEL.getStatus()); + int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj); + if (updateRows == 0) { + log.error("[cancelBrokerage][record({}) 更新为已失效失败]", record.getId()); + return; + } + + // 2. 更新用户的佣金 + if (BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus().equals(record.getStatus())) { + brokerageUserService.updateUserFrozenPrice(record.getUserId(), -record.getPrice()); + } else if (BrokerageRecordStatusEnum.SETTLEMENT.getStatus().equals(record.getStatus())) { + brokerageUserService.updateUserPrice(record.getUserId(), -record.getPrice()); + } + }); + } + + /** + * 计算佣金 + * + * @param basePrice 佣金基数 + * @param percent 佣金比例 + * @param fixedPrice 固定佣金 + * @return 佣金 + */ + int calculatePrice(Integer basePrice, Integer percent, Integer fixedPrice) { + // 1. 优先使用固定佣金 + if (fixedPrice != null && fixedPrice > 0) { + return ObjectUtil.defaultIfNull(fixedPrice, 0); + } + // 2. æ ¹æ®æ¯”例计算佣金 + if (basePrice != null && basePrice > 0 && percent != null && percent > 0) { + return MoneyUtils.calculateRatePriceFloor(basePrice, Double.valueOf(percent)); + } + return 0; + } + + /** + * 增加用户佣金 + * + * @param user 用户 + * @param list ä½£é‡‘å¢žåŠ å‚æ•°åˆ—表 + * @param brokerageFrozenDays 冻结天数 + * @param brokeragePercent 佣金比例 + * @param bizType 业务类型 + * @param sourceUserLevel æ¥æºç”¨æˆ·ç­‰çº§ + */ + private void addBrokerage(BrokerageUserDO user, List list, Integer brokerageFrozenDays, + Integer brokeragePercent, BrokerageRecordBizTypeEnum bizType, Integer sourceUserLevel) { + // 1.1 处ç†å†»ç»“æ—¶é—´ + LocalDateTime unfreezeTime = null; + if (brokerageFrozenDays != null && brokerageFrozenDays > 0) { + unfreezeTime = LocalDateTime.now().plusDays(brokerageFrozenDays); + } + // 1.2 计算分佣 + int totalBrokerage = 0; + List records = new ArrayList<>(); + for (BrokerageAddReqBO item : list) { + // è®¡ç®—é‡‘é¢ + Integer fixedPrice; + if (Objects.equals(sourceUserLevel, 1)) { + fixedPrice = item.getFirstFixedPrice(); + } else if (Objects.equals(sourceUserLevel, 2)) { + fixedPrice = item.getSecondFixedPrice(); + } else { + throw new IllegalArgumentException(StrUtil.format("用户等级({}) ä¸åˆæ³•", sourceUserLevel)); + } + int brokeragePrice = calculatePrice(item.getBasePrice(), brokeragePercent, fixedPrice); + if (brokeragePrice <= 0) { + continue; + } + totalBrokerage += brokeragePrice; + // 创建记录实体 + records.add(BrokerageRecordConvert.INSTANCE.convert(user, bizType, item.getBizId(), + brokerageFrozenDays, brokeragePrice, unfreezeTime, item.getTitle(), + item.getSourceUserId(), sourceUserLevel)); + } + if (CollUtil.isEmpty(records)) { + return; + } + // 1.3 ä¿å­˜ä½£é‡‘记录 + brokerageRecordMapper.insertBatch(records); + + // 2. 更新用户佣金 + if (brokerageFrozenDays != null && brokerageFrozenDays > 0) { // 更新用户冻结佣金 + brokerageUserService.updateUserFrozenPrice(user.getId(), totalBrokerage); + } else { // 更新用户å¯ç”¨ä½£é‡‘ + brokerageUserService.updateUserPrice(user.getId(), totalBrokerage); + } + } + + @Override + public int unfreezeRecord() { + // 1. 查询待结算的佣金记录 + List records = brokerageRecordMapper.selectListByStatusAndUnfreezeTimeLt( + BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus(), LocalDateTime.now()); + if (CollUtil.isEmpty(records)) { + return 0; + } + + // 2. é历执行 + int count = 0; + for (BrokerageRecordDO record : records) { + try { + boolean success = getSelf().unfreezeRecord(record); + if (success) { + count++; + } + } catch (Exception e) { + log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId(), e); + } + } + return count; + } + + /** + * è§£å†»å•æ¡ä½£é‡‘记录 + * + * @param record 佣金记录 + * @return è§£å†»æ˜¯å¦æˆåŠŸ + */ + @Transactional(rollbackFor = Exception.class) + public boolean unfreezeRecord(BrokerageRecordDO record) { + // æ›´æ–°è®°å½•çŠ¶æ€ + BrokerageRecordDO updateObj = new BrokerageRecordDO() + .setStatus(BrokerageRecordStatusEnum.SETTLEMENT.getStatus()) + .setUnfreezeTime(LocalDateTime.now()); + int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj); + if (updateRows == 0) { + log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId()); + return false; + } + + // 更新用户冻结佣金 + brokerageUserService.updateFrozenPriceDecrAndPriceIncr(record.getUserId(), -record.getPrice()); + log.info("[unfreezeRecord][record({}) 更新为已结算æˆåŠŸ]", record.getId()); + return true; + } + + @Override + public List getUserBrokerageSummaryListByUserId(Collection userIds, + Integer bizType, Integer status) { + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + return brokerageRecordMapper.selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(userIds, bizType, status); + } + + @Override + public Integer getSummaryPriceByUserId(Long userId, BrokerageRecordBizTypeEnum bizType, BrokerageRecordStatusEnum status, + LocalDateTime beginTime, LocalDateTime endTime) { + return brokerageRecordMapper.selectSummaryPriceByUserIdAndBizTypeAndCreateTimeBetween(userId, + bizType.getType(), status.getStatus(), beginTime, endTime); + } + + @Override + public PageResult getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) { + IPage pageResult = brokerageRecordMapper.selectSummaryPricePageGroupByUserId( + MyBatisUtils.buildPage(pageReqVO), + BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(), + ArrayUtil.get(pageReqVO.getTimes(), 0), ArrayUtil.get(pageReqVO.getTimes(), 1)); + return new PageResult<>(pageResult.getRecords(), pageResult.getTotal()); + } + + @Override + public Integer getUserRankByPrice(Long userId, LocalDateTime[] times) { + // ç”¨æˆ·çš„æŽ¨å¹¿é‡‘é¢ + Integer price = brokerageRecordMapper.selectSummaryPriceByUserIdAndBizTypeAndCreateTimeBetween(userId, + BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(), + ArrayUtil.get(times, 0), ArrayUtil.get(times, 1)); + // 排在用户å‰é¢çš„人数 + Integer greaterCount = brokerageRecordMapper.selectCountByPriceGt(price, + BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(), + ArrayUtil.get(times, 0), ArrayUtil.get(times, 1)); + // 获得排å + return ObjUtil.defaultIfNull(greaterCount, 0) + 1; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title) { + // 1. æ ¡éªŒä½£é‡‘ä½™é¢ + BrokerageUserDO user = brokerageUserService.getBrokerageUser(userId); + int balance = Optional.of(user) + .map(BrokerageUserDO::getBrokeragePrice).orElse(0); + if (balance + brokeragePrice < 0) { + throw exception(BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH, MoneyUtils.fenToYuanStr(balance)); + } + + // 2. æ›´æ–°ä½£é‡‘ä½™é¢ + boolean success = brokerageUserService.updateUserPrice(userId, brokeragePrice); + if (!success) { + // 失败时,则抛出异常。åªä¼šå‡ºçŽ°æ‰£å‡ä½£é‡‘时,余é¢ä¸è¶³çš„æƒ…况 + throw exception(BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH, MoneyUtils.fenToYuanStr(balance)); + } + + // 3. 新增记录 + BrokerageRecordDO record = BrokerageRecordConvert.INSTANCE.convert(user, bizType, bizId, 0, brokeragePrice, + null, title, null, null); + brokerageRecordMapper.insert(record); + } + + @Override + public AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId) { + // 1. 构建默认的返回值 + AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO().setEnabled(false) + .setBrokerageMinPrice(0).setBrokerageMaxPrice(0); + + // 2.1 校验分销功能是å¦å¼€å¯ + TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig(); + if (tradeConfig == null || BooleanUtil.isFalse(tradeConfig.getBrokerageEnabled())) { + return respVO; + } + // 2.2 æ ¡éªŒç”¨æˆ·æ˜¯å¦æœ‰åˆ†é”€èµ„æ ¼ + respVO.setEnabled(brokerageUserService.getUserBrokerageEnabled(getLoginUserId())); + if (BooleanUtil.isFalse(respVO.getEnabled())) { + return respVO; + } + // 2.3 æ ¡éªŒå•†å“æ˜¯å¦å­˜åœ¨ + ProductSpuRespDTO spu = productSpuApi.getSpu(spuId).getCheckedData(); + if (spu == null) { + return respVO; + } + + // 3.1 商å“å•ç‹¬åˆ†ä½£æ¨¡å¼ + Integer fixedMinPrice = 0; + Integer fixedMaxPrice = 0; + Integer spuMinPrice = 0; + Integer spuMaxPrice = 0; + List skuList = productSkuApi.getSkuListBySpuId(ListUtil.of(spuId)).getCheckedData(); + if (BooleanUtil.isTrue(spu.getSubCommissionType())) { + fixedMinPrice = getMinValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice); + fixedMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice); + // 3.2 全局分佣模å¼ï¼ˆæ ¹æ®å•†å“价格比例计算) + } else { + spuMinPrice = getMinValue(skuList, ProductSkuRespDTO::getPrice); + spuMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getPrice); + } + respVO.setBrokerageMinPrice(calculatePrice(spuMinPrice, tradeConfig.getBrokerageFirstPercent(), fixedMinPrice)); + respVO.setBrokerageMaxPrice(calculatePrice(spuMaxPrice, tradeConfig.getBrokerageFirstPercent(), fixedMaxPrice)); + return respVO; + } + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private BrokerageRecordServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageUserService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageUserService.java new file mode 100644 index 0000000..b2ea00e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageUserService.java @@ -0,0 +1,145 @@ +package com.tashow.cloud.trade.service.brokerage; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.user.BrokerageUserCreateReqVO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankPageReqVO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +/** + * 分销用户 Service æŽ¥å£ + * + * @author owen + */ +public interface BrokerageUserService { + + /** + * 获得分销用户 + * + * @param id ç¼–å· + * @return 分销用户 + */ + BrokerageUserDO getBrokerageUser(Long id); + + /** + * 获得分销用户分页 + * + * @param pageReqVO 分页查询 + * @return 分销用户分页 + */ + PageResult getBrokerageUserPage(BrokerageUserPageReqVO pageReqVO); + + /** + * ä¿®æ”¹æŽ¨å¹¿å‘˜ç¼–å· + * + * @param id ç”¨æˆ·ç¼–å· + * @param bindUserId æŽ¨å¹¿å‘˜ç¼–å· + */ + void updateBrokerageUserId(Long id, Long bindUserId); + + /** + * 修改推广资格 + * + * @param id ç”¨æˆ·ç¼–å· + * @param enabled 推广资格 + */ + void updateBrokerageUserEnabled(Long id, Boolean enabled); + + /** + * 获得用户的推广人 + * + * @param id ç”¨æˆ·ç¼–å· + * @return 用户的推广人 + */ + BrokerageUserDO getBindBrokerageUser(Long id); + + /** + * 获得或创建分销用户 + * + * @param id ç”¨æˆ·ç¼–å· + * @return 分销用户 + */ + BrokerageUserDO getOrCreateBrokerageUser(Long id); + + /** + * 更新用户佣金 + * + * @param id ç”¨æˆ·ç¼–å· + * @param price 用户å¯ç”¨ä½£é‡‘ + * @return 更新结果 + */ + boolean updateUserPrice(Long id, Integer price); + + /** + * 更新用户冻结佣金 + * + * @param id ç”¨æˆ·ç¼–å· + * @param frozenPrice 用户冻结佣金 + */ + void updateUserFrozenPrice(Long id, Integer frozenPrice); + + /** + * 更新用户冻结佣金(å‡å°‘),更新用户佣金(增加) + * + * @param id ç”¨æˆ·ç¼–å· + * @param frozenPrice å‡å°‘冻结佣金(负数) + */ + void updateFrozenPriceDecrAndPriceIncr(Long id, Integer frozenPrice); + + /** + * èŽ·å¾—æŽ¨å¹¿ç”¨æˆ·æ•°é‡ + * + * @param bindUserId ç»‘å®šçš„æŽ¨å¹¿å‘˜ç¼–å· + * @param level 推广用户等级 + * @return æŽ¨å¹¿ç”¨æˆ·æ•°é‡ + */ + Long getBrokerageUserCountByBindUserId(Long bindUserId, Integer level); + + /** + * ã€ä¼šå‘˜ã€‘绑定推广员 + * + * @param userId ç”¨æˆ·ç¼–å· + * @param bindUserId æŽ¨å¹¿å‘˜ç¼–å· + * @return 是å¦ç»‘定 + */ + boolean bindBrokerageUser(@NotNull Long userId, @NotNull Long bindUserId); + + /** + * ã€ç®¡ç†å‘˜ã€‘创建分销用户 + * + * @param createReqVO 请求 + * @return ç¼–å· + */ + Long createBrokerageUser(@Valid BrokerageUserCreateReqVO createReqVO); + + /** + * 获å–ç”¨æˆ·æ˜¯å¦æœ‰åˆ†é”€èµ„æ ¼ + * + * @param userId ç”¨æˆ·ç¼–å· + * @return æ˜¯å¦æœ‰åˆ†é”€èµ„æ ¼ + */ + Boolean getUserBrokerageEnabled(Long userId); + + /** + * 获得推广人排行 + * + * @param pageReqVO 分页查询 + * @return 推广人排行 + */ + PageResult getBrokerageUserRankPageByUserCount(AppBrokerageUserRankPageReqVO pageReqVO); + + /** + * 获得下级分销统计分页 + * + * @param pageReqVO 分页查询 + * @param userId ç”¨æˆ·ç¼–å· + * @return 下级分销统计分页 + */ + PageResult getBrokerageUserChildSummaryPage(AppBrokerageUserChildSummaryPageReqVO pageReqVO, Long userId); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageUserServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageUserServiceImpl.java new file mode 100644 index 0000000..7bfca21 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageUserServiceImpl.java @@ -0,0 +1,385 @@ +package com.tashow.cloud.trade.service.brokerage; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.user.BrokerageUserCreateReqVO; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.user.BrokerageUserPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserChildSummaryRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByUserCountRespVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankPageReqVO; +import com.tashow.cloud.trade.convert.brokerage.BrokerageUserConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; +import com.tashow.cloud.trade.dal.mysql.brokerage.BrokerageUserMapper; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; +import com.tashow.cloud.trade.service.config.TradeConfigService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMapByFilter; + +/** + * 分销用户 Service 实现类 + * + * @author owen + */ +@Service +@Validated +public class BrokerageUserServiceImpl implements BrokerageUserService { + + @Resource + private BrokerageUserMapper brokerageUserMapper; + + @Resource + private TradeConfigService tradeConfigService; + + @Resource + private MemberUserApi memberUserApi; + + @Override + public BrokerageUserDO getBrokerageUser(Long id) { + return brokerageUserMapper.selectById(id); + } + + @Override + public PageResult getBrokerageUserPage(BrokerageUserPageReqVO pageReqVO) { + List childIds = getChildUserIdsByLevel(pageReqVO.getBindUserId(), pageReqVO.getLevel()); + // 有â€ç»‘定用户编å·â€œæŸ¥è¯¢æ¡ä»¶æ—¶ï¼Œæ²¡æœ‰æŸ¥åˆ°ä¸‹çº§ä¼šå‘˜ï¼Œç›´æŽ¥è¿”回空 + if (pageReqVO.getBindUserId() != null && CollUtil.isEmpty(childIds)) { + return PageResult.empty(); + } + return brokerageUserMapper.selectPage(pageReqVO, childIds); + } + + @Override + public void updateBrokerageUserId(Long id, Long bindUserId) { + // 校验存在 + BrokerageUserDO brokerageUser = validateBrokerageUserExists(id); + // 绑定关系未å‘生å˜åŒ– + if (Objects.equals(brokerageUser.getBindUserId(), bindUserId)) { + return; + } + + // 情况一:清除推广员 + if (bindUserId == null) { + // 清除推广员 + brokerageUserMapper.updateBindUserIdAndBindUserTimeToNull(id); + return; + } + + // 情况二:修改推广员 + validateCanBindUser(brokerageUser, bindUserId); + brokerageUserMapper.updateById(fillBindUserData(bindUserId, new BrokerageUserDO().setId(id))); + } + + @Override + public void updateBrokerageUserEnabled(Long id, Boolean enabled) { + // 校验存在 + validateBrokerageUserExists(id); + if (BooleanUtil.isTrue(enabled)) { + // 开通推广资格 + brokerageUserMapper.updateById(new BrokerageUserDO().setId(id) + .setBrokerageEnabled(true).setBrokerageTime(LocalDateTime.now())); + } else { + // å–æ¶ˆæŽ¨å¹¿èµ„æ ¼ + brokerageUserMapper.updateEnabledFalseAndBrokerageTimeToNull(id); + } + } + + private BrokerageUserDO validateBrokerageUserExists(Long id) { + BrokerageUserDO brokerageUserDO = brokerageUserMapper.selectById(id); + if (brokerageUserDO == null) { + throw exception(BROKERAGE_USER_NOT_EXISTS); + } + return brokerageUserDO; + } + + @Override + public BrokerageUserDO getBindBrokerageUser(Long id) { + return Optional.ofNullable(id) + .map(this::getBrokerageUser) + .map(BrokerageUserDO::getBindUserId) + .map(this::getBrokerageUser) + .orElse(null); + } + + @Override + public BrokerageUserDO getOrCreateBrokerageUser(Long id) { + BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(id); + // 特殊:人人分销的情况下,如果分销人为空则创建分销人 + if (brokerageUser == null && ObjUtil.equal(BrokerageEnabledConditionEnum.ALL.getCondition(), + tradeConfigService.getTradeConfig().getBrokerageEnabledCondition())) { + brokerageUser = new BrokerageUserDO().setId(id).setBrokerageEnabled(true).setBrokeragePrice(0) + .setBrokerageTime(LocalDateTime.now()).setFrozenPrice(0); + brokerageUserMapper.insert(brokerageUser); + } + return brokerageUser; + } + + @Override + public boolean updateUserPrice(Long id, Integer price) { + if (price > 0) { + brokerageUserMapper.updatePriceIncr(id, price); + } else if (price < 0) { + return brokerageUserMapper.updatePriceDecr(id, price) > 0; + } + return true; + } + + @Override + public void updateUserFrozenPrice(Long id, Integer frozenPrice) { + if (frozenPrice > 0) { + brokerageUserMapper.updateFrozenPriceIncr(id, frozenPrice); + } else if (frozenPrice < 0) { + brokerageUserMapper.updateFrozenPriceDecr(id, frozenPrice); + } + } + + @Override + public void updateFrozenPriceDecrAndPriceIncr(Long id, Integer frozenPrice) { + Assert.isTrue(frozenPrice < 0); + int updateRows = brokerageUserMapper.updateFrozenPriceDecrAndPriceIncr(id, frozenPrice); + if (updateRows == 0) { + throw exception(BROKERAGE_USER_FROZEN_PRICE_NOT_ENOUGH); + } + } + + @Override + public Long getBrokerageUserCountByBindUserId(Long bindUserId, Integer level) { + List childIds = getChildUserIdsByLevel(bindUserId, level); + return (long) CollUtil.size(childIds); + } + + @Override + public boolean bindBrokerageUser(Long userId, Long bindUserId) { + // 1. 获得分销用户 + boolean isNewBrokerageUser = false; + BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId); + if (brokerageUser == null) { // 分销用户ä¸å­˜åœ¨çš„æƒ…况:1. 新注册;2. æ—§æ•°æ®ï¼›3. 分销功能关闭åŽåˆæ‰“å¼€ + isNewBrokerageUser = true; + brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setBrokeragePrice(0).setFrozenPrice(0); + } + + // 2.1 校验是å¦èƒ½ç»‘定用户 + boolean validated = isUserCanBind(brokerageUser); + if (!validated) { + return false; + } + // 2.3 校验能å¦ç»‘定 + validateCanBindUser(brokerageUser, bindUserId); + // 2.3 绑定用户 + if (isNewBrokerageUser) { + Integer enabledCondition = tradeConfigService.getTradeConfig().getBrokerageEnabledCondition(); + if (BrokerageEnabledConditionEnum.ALL.getCondition().equals(enabledCondition)) { // 人人分销:用户默认就有分销资格 + brokerageUser.setBrokerageEnabled(true).setBrokerageTime(LocalDateTime.now()); + } else { + brokerageUser.setBrokerageEnabled(false).setBrokerageTime(LocalDateTime.now()); + } + brokerageUserMapper.insert(fillBindUserData(bindUserId, brokerageUser)); + } else { + brokerageUserMapper.updateById(fillBindUserData(bindUserId, new BrokerageUserDO().setId(userId))); + } + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createBrokerageUser(BrokerageUserCreateReqVO createReqVO) { + // 1.1 校验分销用户是å¦å·²å­˜åœ¨ + BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(createReqVO.getUserId()); + if (brokerageUser != null) { + throw exception(BROKERAGE_CREATE_USER_EXISTS); + } + // 1.2 校验是å¦èƒ½ç»‘定用户 + brokerageUser = BeanUtils.toBean(createReqVO, BrokerageUserDO.class).setId(createReqVO.getUserId()) + .setBrokerageTime(LocalDateTime.now()); + validateCanBindUser(brokerageUser, createReqVO.getBindUserId()); + + // 2. 创建分销人 + brokerageUserMapper.insert(brokerageUser); + return brokerageUser.getId(); + } + + /** + * 补全绑定用户的字段 + * + * @param bindUserId ç»‘å®šçš„ç”¨æˆ·ç¼–å· + * @param brokerageUser update 对象 + * @return 补全åŽçš„ update 对象 + */ + private BrokerageUserDO fillBindUserData(Long bindUserId, BrokerageUserDO brokerageUser) { + return brokerageUser.setBindUserId(bindUserId).setBindUserTime(LocalDateTime.now()); + } + + @Override + public Boolean getUserBrokerageEnabled(Long userId) { + // 全局分销功能是å¦å¼€å¯ + TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig(); + if (tradeConfig == null || BooleanUtil.isFalse(tradeConfig.getBrokerageEnabled())) { + return false; + } + + // ç”¨æˆ·æ˜¯å¦æœ‰åˆ†é”€èµ„æ ¼ + return Optional.ofNullable(getBrokerageUser(userId)) + .map(BrokerageUserDO::getBrokerageEnabled) + .orElse(false); + } + + @Override + public PageResult getBrokerageUserRankPageByUserCount(AppBrokerageUserRankPageReqVO pageReqVO) { + IPage pageResult = brokerageUserMapper.selectCountPageGroupByBindUserId(MyBatisUtils.buildPage(pageReqVO), + ArrayUtil.get(pageReqVO.getTimes(), 0), ArrayUtil.get(pageReqVO.getTimes(), 1)); + return new PageResult<>(pageResult.getRecords(), pageResult.getTotal()); + } + + @Override + public PageResult getBrokerageUserChildSummaryPage(AppBrokerageUserChildSummaryPageReqVO pageReqVO, Long userId) { + // 1.1 查询下级用户编å·åˆ—表 + List childIds = getChildUserIdsByLevel(userId, pageReqVO.getLevel()); + if (CollUtil.isEmpty(childIds)) { + return PageResult.empty(); + } + // 1.2 æ ¹æ®æ˜µç§°è¿‡æ»¤ä¸‹çº§ç”¨æˆ· + List users = memberUserApi.getUserList(childIds).getCheckedData(); + Map userMap = convertMapByFilter(users, + user -> StrUtil.contains(user.getNickname(), pageReqVO.getNickname()), + MemberUserRespDTO::getId); + if (CollUtil.isEmpty(userMap)) { + return PageResult.empty(); + } + + // 2. 分页查询 + IPage pageResult = brokerageUserMapper.selectSummaryPageByUserId( + MyBatisUtils.buildPage(pageReqVO), BrokerageRecordBizTypeEnum.ORDER.getType(), + BrokerageRecordStatusEnum.SETTLEMENT.getStatus(), userMap.keySet(), pageReqVO.getSortingField() + ); + + // 3. 拼接数æ®å¹¶è¿”回 + BrokerageUserConvert.INSTANCE.copyTo(pageResult.getRecords(), userMap); + return new PageResult<>(pageResult.getRecords(), pageResult.getTotal()); + } + + private boolean isUserCanBind(BrokerageUserDO user) { + // 校验分销功能是å¦å¯ç”¨ + TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig(); + if (tradeConfig == null || !BooleanUtil.isTrue(tradeConfig.getBrokerageEnabled())) { + return false; + } + + // æ ¡éªŒåˆ†é”€å…³ç³»ç»‘å®šæ¨¡å¼ + if (BrokerageBindModeEnum.REGISTER.getMode().equals(tradeConfig.getBrokerageBindMode())) { + // 判断是å¦ä¸ºæ–°ç”¨æˆ·ï¼šæ³¨å†Œæ—¶é—´åœ¨ 30 秒内的,都算新用户 + if (!isNewRegisterUser(user.getId())) { + throw exception(BROKERAGE_BIND_MODE_REGISTER); // åªæœ‰åœ¨æ³¨å†Œæ—¶å¯ä»¥ç»‘定 + } + } else if (BrokerageBindModeEnum.ANYTIME.getMode().equals(tradeConfig.getBrokerageBindMode())) { + if (user.getBindUserId() != null) { + throw exception(BROKERAGE_BIND_OVERRIDE); // 已绑定了推广人 + } + } + return true; + } + + /** + * 判断是å¦ä¸ºæ–°ç”¨æˆ· + *

+ * 标准:注册时间在 30 秒内的,都算新用户 + *

+ * 疑问:为什么通过这样的方å¼å®žçŽ°ï¼Ÿ + * 回答:因为注册在 member 模å—,希望它和 trade 模å—解耦,所以åªèƒ½ç”¨è¿™ç§çº¦å®šçš„逻辑。 + * + * @param userId ç”¨æˆ·ç¼–å· + * @return æ˜¯å¦æ–°ç”¨æˆ· + */ + private boolean isNewRegisterUser(Long userId) { + MemberUserRespDTO user = memberUserApi.getUser(userId).getCheckedData(); + return user != null && LocalDateTimeUtils.afterNow(user.getCreateTime().plusSeconds(30)); + } + + private void validateCanBindUser(BrokerageUserDO user, Long bindUserId) { + // 1.1 校验推广人是å¦å­˜åœ¨ + MemberUserRespDTO bindUserInfo = memberUserApi.getUser(bindUserId).getCheckedData(); + if (bindUserInfo == null) { + throw exception(BROKERAGE_USER_NOT_EXISTS); + } + // 1.2 校验è¦ç»‘定的用户有无推广资格 + BrokerageUserDO bindUser = getOrCreateBrokerageUser(bindUserId); + if (bindUser == null || BooleanUtil.isFalse(bindUser.getBrokerageEnabled())) { + throw exception(BROKERAGE_BIND_USER_NOT_ENABLED); + } + + // 2. 校验绑定自己 + if (Objects.equals(user.getId(), bindUserId)) { + throw exception(BROKERAGE_BIND_SELF); + } + + // 3. 下级ä¸èƒ½ç»‘定自己的上级 + for (int i = 0; i <= Short.MAX_VALUE; i++) { + if (Objects.equals(bindUser.getBindUserId(), user.getId())) { + throw exception(BROKERAGE_BIND_LOOP); + } + bindUser = getBrokerageUser(bindUser.getBindUserId()); + // 找到根节点,结æŸå¾ªçޝ + if (bindUser == null || bindUser.getBindUserId() == null) { + break; + } + } + } + + /** + * æ ¹æ®ç»‘定用户编å·ï¼ŒèŽ·å¾—ä¸‹çº§ç”¨æˆ·ç¼–å·åˆ—表 + * + * @param bindUserId ç»‘å®šç”¨æˆ·ç¼–å· + * @param level 下级用户的层级。 + * 如果 level 为空,则查询 1+2 两个层级 + * @return 下级用户编å·åˆ—表 + */ + private List getChildUserIdsByLevel(Long bindUserId, Integer level) { + if (bindUserId == null) { + return Collections.emptyList(); + } + // 先查第 1 级 + List bindUserIds = brokerageUserMapper.selectIdListByBindUserIdIn(Collections.singleton(bindUserId)); + if (CollUtil.isEmpty(bindUserIds)) { + return Collections.emptyList(); + } + + // 情况一:level 为空,查询所有级别 + if (level == null) { + // 冿Ÿ¥ç¬¬ 2 级,并åˆå¹¶ç»“æžœ + bindUserIds.addAll(brokerageUserMapper.selectIdListByBindUserIdIn(bindUserIds)); + return bindUserIds; + } + // 情况二:level 为 1ï¼ŒåªæŸ¥è¯¢ç¬¬ 1 级 + if (level == 1) { + return bindUserIds; + } + // 情况三:level 为 1ï¼ŒåªæŸ¥è¯¢ç¬¬ 2 级 + if (level == 2) { + return brokerageUserMapper.selectIdListByBindUserIdIn(bindUserIds); + } + throw exception(BROKERAGE_USER_LEVEL_NOT_SUPPORT); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageWithdrawService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageWithdrawService.java new file mode 100644 index 0000000..9e0ab63 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageWithdrawService.java @@ -0,0 +1,91 @@ +package com.tashow.cloud.trade.service.brokerage; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 佣金æçް Service æŽ¥å£ + * + * @author èŠ‹é“æºç  + */ +public interface BrokerageWithdrawService { + + /** + * ã€ç®¡ç†å‘˜ã€‘审核佣金æçް + * + * @param id ä½£é‡‘ç¼–å· + * @param status å®¡æ ¸çŠ¶æ€ + * @param auditReason 驳回原因 + * @param userIp æ“作 IP + */ + void auditBrokerageWithdraw(Long id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp); + + /** + * 获得佣金æçް + * + * @param id ç¼–å· + * @return 佣金æçް + */ + BrokerageWithdrawDO getBrokerageWithdraw(Long id); + + /** + * 获得佣金æçŽ°åˆ†é¡µ + * + * @param pageReqVO 分页查询 + * @return 佣金æçŽ°åˆ†é¡µ + */ + PageResult getBrokerageWithdrawPage(BrokerageWithdrawPageReqVO pageReqVO); + + /** + * ã€ä¼šå‘˜ã€‘创建佣金æçް + * + * @param userId ä¼šå‘˜ç”¨æˆ·ç¼–å· + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return 佣金æçŽ°ç¼–å· + */ + Long createBrokerageWithdraw(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO); + + /** + * ã€API】更新佣金æçŽ°çš„è½¬è´¦ç»“æžœ + * + * ç›®å‰ç”¨äºŽæ”¯ä»˜å›žè°ƒï¼Œæ ‡è®°æçŽ°è½¬è´¦ç»“æžœ + * + * @param id æçŽ°ç¼–å· + * @param payTransferId 转账订å•ç¼–å· + */ + void updateBrokerageWithdrawTransferred(Long id, Long payTransferId); + + /** + * 按照 userId,汇总æ¯ä¸ªç”¨æˆ·çš„æçް + * + * @param userIds ç”¨æˆ·ç¼–å· + * @param status æçŽ°çŠ¶æ€ + * @return 用户æçŽ°æ±‡æ€» List + */ + List getWithdrawSummaryListByUserId(Collection userIds, + Collection status); + + /** + * 按照 userId,汇总æ¯ä¸ªç”¨æˆ·çš„æçް + * + * @param userIds ç”¨æˆ·ç¼–å· + * @param status æçŽ°çŠ¶æ€ + * @return 用户æçŽ°æ±‡æ€» Map + */ + default Map getWithdrawSummaryMapByUserId(Set userIds, + Collection status) { + return convertMap(getWithdrawSummaryListByUserId(userIds, status), BrokerageWithdrawSummaryRespBO::getUserId); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageWithdrawServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageWithdrawServiceImpl.java new file mode 100644 index 0000000..520692b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/BrokerageWithdrawServiceImpl.java @@ -0,0 +1,250 @@ +package com.tashow.cloud.trade.service.brokerage; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO; +import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi; +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletAddBalanceReqDTO; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum; +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; +import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; +import cn.iocoder.yudao.module.system.api.social.SocialUserApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import com.tashow.cloud.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; +import com.tashow.cloud.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; +import com.tashow.cloud.trade.convert.brokerage.BrokerageWithdrawConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; +import com.tashow.cloud.trade.dal.mysql.brokerage.BrokerageWithdrawMapper; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; +import com.tashow.cloud.trade.framework.order.config.TradeOrderProperties; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; +import com.tashow.cloud.trade.service.config.TradeConfigService; +import jakarta.annotation.Resource; +import jakarta.validation.Validator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; + +/** + * 佣金æçް Service 实现类 + * + * @author èŠ‹é“æºç  + */ +@Service +@Validated +@Slf4j +public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { + + @Resource + private BrokerageWithdrawMapper brokerageWithdrawMapper; + + @Resource + private BrokerageRecordService brokerageRecordService; + @Resource + private TradeConfigService tradeConfigService; + + @Resource + private NotifyMessageSendApi notifyMessageSendApi; + @Resource + private PayTransferApi payTransferApi; + @Resource + private SocialUserApi socialUserApi; + @Resource + private PayWalletApi payWalletApi; + + @Resource + private Validator validator; + + @Resource + private TradeOrderProperties tradeOrderProperties; + + @Override + @Transactional(rollbackFor = Exception.class) + public void auditBrokerageWithdraw(Long id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp) { + // 1.1 校验存在 + BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id); + // 1.2 校验状æ€ä¸ºå®¡æ ¸ä¸­ + if (ObjectUtil.notEqual(BrokerageWithdrawStatusEnum.AUDITING.getStatus(), withdraw.getStatus())) { + throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING); + } + + // 2. æ›´æ–°çŠ¶æ€ + int rows = brokerageWithdrawMapper.updateByIdAndStatus(id, BrokerageWithdrawStatusEnum.AUDITING.getStatus(), + new BrokerageWithdrawDO().setStatus(status.getStatus()).setAuditReason(auditReason).setAuditTime(LocalDateTime.now())); + if (rows == 0) { + throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING); + } + + // 3.1 审批通过的åŽç»­å¤„ç† + if (BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.equals(status)) { + auditBrokerageWithdrawSuccess(withdraw); + // 3.2 审批ä¸é€šè¿‡çš„åŽç»­å¤„ç† + } else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) { + brokerageRecordService.addBrokerage(withdraw.getUserId(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT, + String.valueOf(withdraw.getId()), withdraw.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT.getTitle()); + } else { + throw new IllegalArgumentException("䏿”¯æŒçš„æçŽ°çŠ¶æ€ï¼š" + status); + } + } + + private void auditBrokerageWithdrawSuccess(BrokerageWithdrawDO withdraw) { + // 1.1 钱包 + if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) { + payWalletApi.addWalletBalance(new PayWalletAddBalanceReqDTO() + .setUserId(withdraw.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue()) + .setBizType(PayWalletBizTypeEnum.BROKERAGE_WITHDRAW.getType()).setBizId(withdraw.getId().toString()) + .setPrice(withdraw.getPrice())).checkError(); + // 1.2 微信 API + } else if (BrokerageWithdrawTypeEnum.WECHAT_API.getType().equals(withdraw.getType())) { + // TODO @luchi:这里,è¦åŠ ä¸ªè½¬è´¦å•å·çš„记录;å¦å¤–,调用 API 转账,是立马æˆåŠŸï¼Œè¿˜æ˜¯æœ‰å»¶è¿Ÿçš„å“ˆï¼Ÿ + Long payTransferId = createPayTransfer(withdraw); + // 1.3 剩余类型,都是手动打款,所以ä¸å¤„ç† + } else { + // TODO å¯ä¼˜åŒ–:未æ¥å¯ä»¥è€ƒè™‘,接入支付å®ã€é“¶è”ç­‰ API 转账,实现自动打款 + log.info("[auditBrokerageWithdrawSuccess][withdraw({}) 类型({}) 手动打款,无需处ç†]", withdraw.getId(), withdraw.getType()); + } + + // 2. éžæ”¯ä»˜ API,则直接体现æˆåŠŸ + if (!BrokerageWithdrawTypeEnum.isApi(withdraw.getType())) { + brokerageWithdrawMapper.updateByIdAndStatus(withdraw.getId(), BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.getStatus(), + new BrokerageWithdrawDO().setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS.getStatus())); + } + } + + private Long createPayTransfer(BrokerageWithdrawDO withdraw) { + // 1.1 获å–微信 openid + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId( + UserTypeEnum.MEMBER.getValue(), withdraw.getUserId(), SocialTypeEnum.WECHAT_MINI_APP.getType()).getCheckedData(); + // TODO @luchiï¼šè¿™é‡Œï¼Œéœ€è¦æ ¡éªŒéžç©ºã€‚如果空的è¯ï¼Œè¦æœ‰ä¸šåŠ¡å¼‚å¸¸å“ˆï¼› + // 1.2 构建请求 + PayTransferCreateReqDTO payTransferCreateReqDTO = new PayTransferCreateReqDTO() + .setAppKey(tradeOrderProperties.getPayAppKey()) + .setChannelCode("wx_lite").setType(PayTransferTypeEnum.WX_BALANCE.getType()) + .setMerchantTransferId(withdraw.getId().toString()) + .setPrice(withdraw.getPrice()) + .setSubject("佣金æçް") + .setOpenid(socialUser.getOpenid()).setUserIp(getClientIP()); + // 2. å‘起请求 + return payTransferApi.createTransfer(payTransferCreateReqDTO).getCheckedData(); + } + + private BrokerageWithdrawDO validateBrokerageWithdrawExists(Long id) { + BrokerageWithdrawDO withdraw = brokerageWithdrawMapper.selectById(id); + if (withdraw == null) { + throw exception(BROKERAGE_WITHDRAW_NOT_EXISTS); + } + return withdraw; + } + + @Override + public BrokerageWithdrawDO getBrokerageWithdraw(Long id) { + return brokerageWithdrawMapper.selectById(id); + } + + @Override + public PageResult getBrokerageWithdrawPage(BrokerageWithdrawPageReqVO pageReqVO) { + return brokerageWithdrawMapper.selectPage(pageReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createBrokerageWithdraw(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO) { + // 1.1 校验æçŽ°é‡‘é¢ + TradeConfigDO tradeConfig = validateWithdrawPrice(createReqVO.getPrice()); + // 1.2 校验æçް傿•° + createReqVO.validate(validator); + + // 2.1 计算手续费 + Integer feePrice = calculateFeePrice(createReqVO.getPrice(), tradeConfig.getBrokerageWithdrawFeePercent()); + // 2.2 创建佣金æçŽ°è®°å½• + BrokerageWithdrawDO withdraw = BrokerageWithdrawConvert.INSTANCE.convert(createReqVO, userId, feePrice); + brokerageWithdrawMapper.insert(withdraw); + + // 3. 创建用户佣金记录 + // 注æ„,佣金是å¦å……足,reduceBrokerage å·²ç»è¿›è¡Œæ ¡éªŒ + brokerageRecordService.reduceBrokerage(userId, BrokerageRecordBizTypeEnum.WITHDRAW, String.valueOf(withdraw.getId()), + createReqVO.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW.getTitle()); + return withdraw.getId(); + } + + /** + * 计算æçŽ°æ‰‹ç»­è´¹ + * + * @param withdrawPrice æçŽ°é‡‘é¢ + * @param percent 手续费百分比 + * @return æçŽ°æ‰‹ç»­è´¹ + */ + private Integer calculateFeePrice(Integer withdrawPrice, Integer percent) { + Integer feePrice = 0; + if (percent != null && percent > 0) { + feePrice = MoneyUtils.calculateRatePrice(withdrawPrice, Double.valueOf(percent)); + } + return feePrice; + } + + /** + * 校验æçް金é¢è¦æ±‚ + * + * @param withdrawPrice æçŽ°é‡‘é¢ + * @return 分销é…ç½® + */ + private TradeConfigDO validateWithdrawPrice(Integer withdrawPrice) { + TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig(); + if (tradeConfig.getBrokerageWithdrawMinPrice() != null && withdrawPrice < tradeConfig.getBrokerageWithdrawMinPrice()) { + throw exception(BROKERAGE_WITHDRAW_MIN_PRICE, MoneyUtils.fenToYuanStr(tradeConfig.getBrokerageWithdrawMinPrice())); + } + return tradeConfig; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateBrokerageWithdrawTransferred(Long id, Long payTransferId) { + BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id); + PayTransferRespDTO transfer = payTransferApi.getTransfer(payTransferId).getCheckedData(); + // TODO @luchi:建议å‚考支付那,å³ä½¿æˆåŠŸçš„æƒ…å†µä¸‹ï¼Œä¹Ÿè¦å„ç§æ ¡éªŒï¼›é‡‘颿˜¯å¦åŒ¹é…ã€è½¬è´¦å•å·æ˜¯å¦åŒ¹é…ã€æ˜¯å¦é‡å¤è°ƒç”¨ï¼› + if (PayTransferStatusEnum.isSuccess(transfer.getStatus())) { + withdraw.setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS.getStatus()); + // TODO @luchi:å‘é€ç«™å†…ä¿¡ + } else if (PayTransferStatusEnum.isPendingStatus(transfer.getStatus())) { + // TODO @luchiï¼šè¿™é‡Œï¼Œæ˜¯ä¸æ˜¯ä¸ç”¨æ›´æ–°å“ˆï¼Ÿ + withdraw.setStatus(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.getStatus()); + } else { + withdraw.setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_FAIL.getStatus()); + // 3.2 驳回时需è¦é€€è¿˜ç”¨æˆ·ä½£é‡‘ + brokerageRecordService.addBrokerage(withdraw.getUserId(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT, + String.valueOf(withdraw.getId()), withdraw.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT.getTitle()); + } + brokerageWithdrawMapper.updateById(withdraw); + } + + @Override + public List getWithdrawSummaryListByUserId(Collection userIds, + Collection statuses) { + if (CollUtil.isEmpty(userIds) || CollUtil.isEmpty(statuses)) { + return Collections.emptyList(); + } + return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, + convertSet(statuses, BrokerageWithdrawStatusEnum::getStatus)); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/BrokerageAddReqBO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/BrokerageAddReqBO.java new file mode 100644 index 0000000..a7b78f4 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/BrokerageAddReqBO.java @@ -0,0 +1,53 @@ +package com.tashow.cloud.trade.service.brokerage.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * 佣金 增加 Request BO + * + * @author owen + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BrokerageAddReqBO { + + /** + * ä¸šåŠ¡ç¼–å· + */ + @NotBlank(message = "业务编å·ä¸èƒ½ä¸ºç©º") + private String bizId; + /** + * 佣金基数 + */ + @NotNull(message = "佣金基数ä¸èƒ½ä¸ºç©º") + private Integer basePrice; + /** + * 一级佣金(固定) + */ + @NotNull(message = "一级佣金(固定)ä¸èƒ½ä¸ºç©º") + private Integer firstFixedPrice; + /** + * 二级佣金(固定) + */ + private Integer secondFixedPrice; + + /** + * æ¥æºç”¨æˆ·ç¼–å· + */ + @NotNull(message = "æ¥æºç”¨æˆ·ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long sourceUserId; + + /** + * 佣金记录标题 + */ + @NotEmpty(message = "佣金记录标题ä¸èƒ½ä¸ºç©º") + private String title; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/BrokerageWithdrawSummaryRespBO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/BrokerageWithdrawSummaryRespBO.java new file mode 100644 index 0000000..2356a06 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/BrokerageWithdrawSummaryRespBO.java @@ -0,0 +1,31 @@ +package com.tashow.cloud.trade.service.brokerage.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 佣金æçްåˆè®¡ BO + * + * @author owen + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BrokerageWithdrawSummaryRespBO { + + /** + * ç”¨æˆ·ç¼–å· + */ + private Long userId; + + /** + * æçŽ°æ¬¡æ•° + */ + private Integer count; + /** + * æçŽ°é‡‘é¢ + */ + private Integer price; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/UserBrokerageSummaryRespBO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/UserBrokerageSummaryRespBO.java new file mode 100644 index 0000000..b313b92 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/brokerage/bo/UserBrokerageSummaryRespBO.java @@ -0,0 +1,30 @@ +package com.tashow.cloud.trade.service.brokerage.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 用户佣金åˆè®¡ BO + * + * @author owen + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserBrokerageSummaryRespBO { + + /** + * ç”¨æˆ·ç¼–å· + */ + private Long userId; + /** + * æŽ¨å¹¿æ•°é‡ + */ + private Integer count; + /** + * ä½£é‡‘æ€»é¢ + */ + private Integer price; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/cart/CartService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/cart/CartService.java new file mode 100644 index 0000000..a1ff188 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/cart/CartService.java @@ -0,0 +1,87 @@ +package com.tashow.cloud.trade.service.cart; + +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.*; +import com.tashow.cloud.trade.controller.app.cart.vo.*; +import com.tashow.cloud.trade.dal.dataobject.cart.CartDO; + +import jakarta.validation.Valid; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * 购物车 Service æŽ¥å£ + * + * @author èŠ‹é“æºç  + */ +public interface CartService { + + /** + * 添加商å“到购物车 + * + * @param userId ç”¨æˆ·ç¼–å· + * @param addReqVO æ·»åŠ ä¿¡æ¯ + * @return è´­ç‰©é¡¹çš„ç¼–å· + */ + Long addCart(Long userId, @Valid AppCartAddReqVO addReqVO); + + /** + * æ›´æ–°è´­ç‰©è½¦å•†å“æ•°é‡ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param updateCountReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateCartCount(Long userId, AppCartUpdateCountReqVO updateCountReqVO); + + /** + * æ›´æ–°è´­ç‰©è½¦é€‰ä¸­çŠ¶æ€ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param updateSelectedReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateCartSelected(Long userId, @Valid AppCartUpdateSelectedReqVO updateSelectedReqVO); + + /** + * é‡ç½®è´­ç‰©è½¦å•†å“ + * + * 使用场景:在一个购物车项对应的商å“失效(例如说 SPU 被下架),å¯ä»¥é‡æ–°é€‰æ‹©å¯¹åº”çš„ SKU + * + * @param userId ç”¨æˆ·ç¼–å· + * @param updateReqVO é‡ç½®ä¿¡æ¯ + */ + void resetCart(Long userId, AppCartResetReqVO updateReqVO); + + /** + * åˆ é™¤è´­ç‰©è½¦å•†å“ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param ids è´­ç‰©é¡¹çš„ç¼–å· + */ + void deleteCart(Long userId, Collection ids); + + /** + * æŸ¥è¯¢ç”¨æˆ·åœ¨è´­ç‰©è½¦ä¸­çš„å•†å“æ•°é‡ + * + * @param userId ç”¨æˆ·ç¼–å· + * @return 商哿•°é‡ + */ + Integer getCartCount(Long userId); + + /** + * 查询用户的购物车列表 + * + * @param userId ç”¨æˆ·ç¼–å· + * @return 购物车列表 + */ + AppCartListRespVO getCartList(Long userId); + + /** + * 查询用户的购物车列表 + * + * @param userId ç”¨æˆ·ç¼–å· + * @param ids è´­ç‰©é¡¹çš„ç¼–å· + * @return 购物车列表 + */ + List getCartList(Long userId, Set ids); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/cart/CartServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/cart/CartServiceImpl.java new file mode 100644 index 0000000..2fa30a3 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/cart/CartServiceImpl.java @@ -0,0 +1,197 @@ +package com.tashow.cloud.trade.service.cart; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +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.app.cart.vo.*; +import com.tashow.cloud.trade.controller.app.cart.vo.*; +import com.tashow.cloud.trade.convert.cart.TradeCartConvert; +import com.tashow.cloud.trade.dal.dataobject.cart.CartDO; +import com.tashow.cloud.trade.dal.mysql.cart.CartMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_NOT_FOUND; +import static java.util.Collections.emptyList; + +/** + * 购物车 Service 实现类 + * + * // TODO 芋艿:未æ¥ä¼˜åŒ–:购物车的价格计算,支æŒè¥é”€ä¿¡æ¯ï¼›ç›®å‰ä¸æ”¯æŒçš„原因,å‰ç«¯ç•Œé¢éœ€è¦å‰ç«¯ pr 支æŒä¸‹ï¼›ä¾‹å¦‚说:会员价格; + * + * @author èŠ‹é“æºç  + */ +@Service +@Validated +public class CartServiceImpl implements CartService { + + @Resource + private CartMapper cartMapper; + + @Resource + private ProductSpuApi productSpuApi; + @Resource + private ProductSkuApi productSkuApi; + + @Override + public Long addCart(Long userId, AppCartAddReqVO addReqVO) { + // 查询 TradeCartDO + CartDO cart = cartMapper.selectByUserIdAndSkuId(userId, addReqVO.getSkuId()); + // 校验 SKU + Integer count = addReqVO.getCount(); + ProductSkuRespDTO sku = checkProductSku(addReqVO.getSkuId(), count); + + // æƒ…å†µä¸€ï¼šå­˜åœ¨ï¼Œåˆ™è¿›è¡Œæ•°é‡æ›´æ–° + if (cart != null) { + cartMapper.updateById(new CartDO().setId(cart.getId()).setSelected(true) + .setCount(cart.getCount() + count)); + return cart.getId(); + // 情况二:ä¸å­˜åœ¨ï¼Œåˆ™è¿›è¡Œæ’å…¥ + } else { + cart = new CartDO().setUserId(userId).setSelected(true) + .setSpuId(sku.getSpuId()).setSkuId(sku.getId()).setCount(count); + cartMapper.insert(cart); + } + return cart.getId(); + } + + @Override + public void updateCartCount(Long userId, AppCartUpdateCountReqVO updateReqVO) { + // 校验 TradeCartDO 存在 + CartDO cart = cartMapper.selectById(updateReqVO.getId(), userId); + if (cart == null) { + throw exception(CARD_ITEM_NOT_FOUND); + } + // æ ¡éªŒå•†å“ SKU + checkProductSku(cart.getSkuId(), updateReqVO.getCount()); + + // æ›´æ–°æ•°é‡ + cartMapper.updateById(new CartDO().setId(cart.getId()) + .setCount(updateReqVO.getCount())); + } + + @Override + public void updateCartSelected(Long userId, AppCartUpdateSelectedReqVO updateSelectedReqVO) { + cartMapper.updateByIds(updateSelectedReqVO.getIds(), userId, + new CartDO().setSelected(updateSelectedReqVO.getSelected())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resetCart(Long userId, AppCartResetReqVO resetReqVO) { + // 第一步:删除原本的购物项 + CartDO oldCart = cartMapper.selectById(resetReqVO.getId(), userId); + if (oldCart == null) { + throw exception(CARD_ITEM_NOT_FOUND); + } + cartMapper.deleteById(oldCart.getId()); + + // 第二步:添加新的购物项 + CartDO newCart = cartMapper.selectByUserIdAndSkuId(userId, resetReqVO.getSkuId()); + if (newCart != null) { + updateCartCount(userId, new AppCartUpdateCountReqVO() + .setId(newCart.getId()).setCount(resetReqVO.getCount())); + } else { + addCart(userId, new AppCartAddReqVO().setSkuId(resetReqVO.getSkuId()) + .setCount(resetReqVO.getCount())); + } + } + + /** + * è´­ç‰©è½¦åˆ é™¤å•†å“ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param ids å•†å“ SKU ç¼–å·çš„æ•°ç»„ + */ + @Override + public void deleteCart(Long userId, Collection ids) { + // 查询 TradeCartDO 列表 + List carts = cartMapper.selectListByIds(ids, userId); + if (CollUtil.isEmpty(carts)) { + return; + } + + // æ‰¹é‡æ ‡è®°åˆ é™¤ + cartMapper.deleteByIds(convertSet(carts, CartDO::getId)); + } + + @Override + public Integer getCartCount(Long userId) { + // TODO 芋艿:需è¦ç®—上 selected + return cartMapper.selectSumByUserId(userId); + } + + @Override + public AppCartListRespVO getCartList(Long userId) { + // èŽ·å¾—è´­ç‰©è½¦çš„å•†å“ + List carts = cartMapper.selectListByUserId(userId); + carts.sort(Comparator.comparing(CartDO::getId).reversed()); + // 如果未空,则返回空结果 + if (CollUtil.isEmpty(carts)) { + return new AppCartListRespVO().setValidList(emptyList()) + .setInvalidList(emptyList()); + } + + // 查询 SPUã€SKU 列表 + List spus = productSpuApi.getSpuList(convertSet(carts, CartDO::getSpuId)).getCheckedData(); + List skus = productSkuApi.getSkuList(convertSet(carts, CartDO::getSkuId)).getCheckedData(); + + // 如果 SPU 被删除,则删除购物车对应的商å“。延迟删除 + // ä¸ºä»€ä¹ˆä¸æ˜¯ SKU 被删除呢?因为 SKU 被删除时,还å¯ä»¥é€šè¿‡ SPU 选择其它 SKU + deleteCartIfSpuDeleted(carts, spus); + + // æ‹¼æŽ¥æ•°æ® + return TradeCartConvert.INSTANCE.convertList(carts, spus, skus); + } + + @Override + public List getCartList(Long userId, Set ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return cartMapper.selectListByUserId(userId, ids); + } + + private void deleteCartIfSpuDeleted(List carts, List spus) { + // 如果 SPU 被删除,则删除购物车对应的商å“。延迟删除 + carts.removeIf(cart -> { + if (spus.stream().noneMatch(spu -> spu.getId().equals(cart.getSpuId()))) { + cartMapper.deleteById(cart.getId()); + return true; + } + return false; + }); + } + + /** + * æ ¡éªŒå•†å“ SKU 是å¦åˆæ³• + * 1. 是å¦å­˜åœ¨ + * 2. 是å¦ä¸‹æž¶ + * 3. 库存ä¸è¶³ + * + * @param skuId å•†å“ SKU ç¼–å· + * @param count 商哿•°é‡ + * @return å•†å“ SKU + */ + private ProductSkuRespDTO checkProductSku(Long skuId, Integer count) { + ProductSkuRespDTO sku = productSkuApi.getSku(skuId).getCheckedData(); + if (sku == null) { + throw exception(SKU_NOT_EXISTS); + } + if (count > sku.getStock()) { + throw exception(SKU_STOCK_NOT_ENOUGH); + } + return sku; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/config/TradeConfigService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/config/TradeConfigService.java new file mode 100644 index 0000000..a2b53c2 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/config/TradeConfigService.java @@ -0,0 +1,29 @@ +package com.tashow.cloud.trade.service.config; + +import com.tashow.cloud.trade.controller.admin.config.vo.TradeConfigSaveReqVO; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; + +import jakarta.validation.Valid; + +/** + * 交易中心é…ç½® Service æŽ¥å£ + * + * @author owen + */ +public interface TradeConfigService { + + /** + * 更新交易中心é…ç½® + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void saveTradeConfig(@Valid TradeConfigSaveReqVO updateReqVO); + + /** + * 获得交易中心é…ç½® + * + * @return 交易中心é…ç½® + */ + TradeConfigDO getTradeConfig(); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/config/TradeConfigServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/config/TradeConfigServiceImpl.java new file mode 100644 index 0000000..b9e2951 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/config/TradeConfigServiceImpl.java @@ -0,0 +1,44 @@ +package com.tashow.cloud.trade.service.config; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import com.tashow.cloud.trade.controller.admin.config.vo.TradeConfigSaveReqVO; +import com.tashow.cloud.trade.convert.config.TradeConfigConvert; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; +import com.tashow.cloud.trade.dal.mysql.config.TradeConfigMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + * 交易中心é…ç½® Service 实现类 + * + * @author owen + */ +@Service +@Validated +public class TradeConfigServiceImpl implements TradeConfigService { + + @Resource + private TradeConfigMapper tradeConfigMapper; + + @Override + public void saveTradeConfig(TradeConfigSaveReqVO saveReqVO) { + // 存在,则进行更新 + TradeConfigDO dbConfig = getTradeConfig(); + if (dbConfig != null) { + tradeConfigMapper.updateById(TradeConfigConvert.INSTANCE.convert(saveReqVO).setId(dbConfig.getId())); + return; + } + // ä¸å­˜åœ¨ï¼Œåˆ™è¿›è¡Œæ’å…¥ + tradeConfigMapper.insert(TradeConfigConvert.INSTANCE.convert(saveReqVO)); + } + + @Override + public TradeConfigDO getTradeConfig() { + List list = tradeConfigMapper.selectList(); + return CollectionUtils.getFirst(list); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressService.java new file mode 100644 index 0000000..aded2b3 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressService.java @@ -0,0 +1,82 @@ +package com.tashow.cloud.trade.service.delivery; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressCreateReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressExportReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressPageReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressUpdateReqVO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; + +import jakarta.validation.Valid; +import java.util.List; + +/** + * å¿«é€’å…¬å¸ Service æŽ¥å£ + * + * @author jason + */ +public interface DeliveryExpressService { + + /** + * åˆ›å»ºå¿«é€’å…¬å¸ + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createDeliveryExpress(@Valid DeliveryExpressCreateReqVO createReqVO); + + /** + * æ›´æ–°å¿«é€’å…¬å¸ + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateDeliveryExpress(@Valid DeliveryExpressUpdateReqVO updateReqVO); + + /** + * åˆ é™¤å¿«é€’å…¬å¸ + * + * @param id ç¼–å· + */ + void deleteDeliveryExpress(Long id); + + /** + * èŽ·å¾—å¿«é€’å…¬å¸ + * + * @param id ç¼–å· + * @return å¿«é€’å…¬å¸ + */ + DeliveryExpressDO getDeliveryExpress(Long id); + + /** + * æ ¡éªŒå¿«é€’å…¬å¸æ˜¯å¦åˆæ³• + * + * @param id ç¼–å· + * @return å¿«é€’å…¬å¸ + */ + DeliveryExpressDO validateDeliveryExpress(Long id); + + /** + * 获得快递公å¸åˆ†é¡µ + * + * @param pageReqVO 分页查询 + * @return 快递公å¸åˆ†é¡µ + */ + PageResult getDeliveryExpressPage(DeliveryExpressPageReqVO pageReqVO); + + /** + * 获得快递公å¸åˆ—表, 用于 Excel 导出 + * + * @param exportReqVO 查询æ¡ä»¶ + * @return 快递公å¸åˆ—表 + */ + List getDeliveryExpressList(DeliveryExpressExportReqVO exportReqVO); + + /** + * èŽ·å–æŒ‡å®šçжæ€çš„快递公å¸åˆ—表 + * + * @param status çŠ¶æ€ + * @return 快递公å¸åˆ—表 + */ + List getDeliveryExpressListByStatus(Integer status); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressServiceImpl.java new file mode 100644 index 0000000..9eb7e37 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressServiceImpl.java @@ -0,0 +1,113 @@ +package com.tashow.cloud.trade.service.delivery; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressCreateReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressExportReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressPageReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.express.DeliveryExpressUpdateReqVO; +import com.tashow.cloud.trade.convert.delivery.DeliveryExpressConvert; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.tashow.cloud.trade.dal.mysql.delivery.DeliveryExpressMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; + +/** + * å¿«é€’å…¬å¸ Service 实现类 + * + * @author jason + */ +@Service +@Validated +public class DeliveryExpressServiceImpl implements DeliveryExpressService { + + @Resource + private DeliveryExpressMapper deliveryExpressMapper; + + @Override + public Long createDeliveryExpress(DeliveryExpressCreateReqVO createReqVO) { + //æ ¡éªŒç¼–ç æ˜¯å¦å”¯ä¸€ + validateExpressCodeUnique(createReqVO.getCode(), null); + // æ’å…¥ + DeliveryExpressDO deliveryExpress = DeliveryExpressConvert.INSTANCE.convert(createReqVO); + deliveryExpressMapper.insert(deliveryExpress); + // 返回 + return deliveryExpress.getId(); + } + + @Override + public void updateDeliveryExpress(DeliveryExpressUpdateReqVO updateReqVO) { + // 校验存在 + validateDeliveryExpressExists(updateReqVO.getId()); + //æ ¡éªŒç¼–ç æ˜¯å¦å”¯ä¸€ + validateExpressCodeUnique(updateReqVO.getCode(), updateReqVO.getId()); + // æ›´æ–° + DeliveryExpressDO updateObj = DeliveryExpressConvert.INSTANCE.convert(updateReqVO); + deliveryExpressMapper.updateById(updateObj); + } + + @Override + public void deleteDeliveryExpress(Long id) { + // 校验存在 + validateDeliveryExpressExists(id); + // 删除 + deliveryExpressMapper.deleteById(id); + } + + private void validateExpressCodeUnique(String code, Long id) { + DeliveryExpressDO express = deliveryExpressMapper.selectByCode(code); + if (express == null) { + return; + } + // 如果 id 为空,说明ä¸ç”¨æ¯”较是å¦ä¸ºç›¸åŒ id çš„å¿«é€’å…¬å¸ + if (id == null) { + throw exception(EXPRESS_CODE_DUPLICATE); + } + if (!express.getId().equals(id)) { + throw exception(EXPRESS_CODE_DUPLICATE); + } + } + private void validateDeliveryExpressExists(Long id) { + if (deliveryExpressMapper.selectById(id) == null) { + throw exception(EXPRESS_NOT_EXISTS); + } + } + + @Override + public DeliveryExpressDO getDeliveryExpress(Long id) { + return deliveryExpressMapper.selectById(id); + } + + @Override + public DeliveryExpressDO validateDeliveryExpress(Long id) { + DeliveryExpressDO deliveryExpress = deliveryExpressMapper.selectById(id); + if (deliveryExpress == null) { + throw exception(EXPRESS_NOT_EXISTS); + } + if (deliveryExpress.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { + throw exception(EXPRESS_STATUS_NOT_ENABLE); + } + return deliveryExpress; + } + + @Override + public PageResult getDeliveryExpressPage(DeliveryExpressPageReqVO pageReqVO) { + return deliveryExpressMapper.selectPage(pageReqVO); + } + + @Override + public List getDeliveryExpressList(DeliveryExpressExportReqVO exportReqVO) { + return deliveryExpressMapper.selectList(exportReqVO); + } + + @Override + public List getDeliveryExpressListByStatus(Integer status) { + return deliveryExpressMapper.selectListByStatus(status); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressTemplateService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressTemplateService.java new file mode 100644 index 0000000..1a8de58 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressTemplateService.java @@ -0,0 +1,95 @@ +package com.tashow.cloud.trade.service.delivery; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateCreateReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateDetailRespVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; +import com.tashow.cloud.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; + +import jakarta.validation.Valid; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 快递è¿è´¹æ¨¡æ¿ Service æŽ¥å£ + * + * @author jason + */ +public interface DeliveryExpressTemplateService { + + /** + * 创建快递è¿è´¹æ¨¡æ¿ + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createDeliveryExpressTemplate(@Valid DeliveryExpressTemplateCreateReqVO createReqVO); + + /** + * 更新快递è¿è´¹æ¨¡æ¿ + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateDeliveryExpressTemplate(@Valid DeliveryExpressTemplateUpdateReqVO updateReqVO); + + /** + * 删除快递è¿è´¹æ¨¡æ¿ + * + * @param id ç¼–å· + */ + void deleteDeliveryExpressTemplate(Long id); + + /** + * 获得快递è¿è´¹æ¨¡æ¿ + * + * @param id ç¼–å· + * @return 快递è¿è´¹æ¨¡æ¿è¯¦æƒ… + */ + DeliveryExpressTemplateDetailRespVO getDeliveryExpressTemplate(Long id); + + /** + * 获得快递è¿è´¹æ¨¡æ¿åˆ—表 + * + * @param ids ç¼–å· + * @return 快递è¿è´¹æ¨¡æ¿åˆ—表 + */ + List getDeliveryExpressTemplateList(Collection ids); + + /** + * 获得快递è¿è´¹æ¨¡æ¿åˆ—表 + * + * @return 快递è¿è´¹æ¨¡æ¿åˆ—表 + */ + List getDeliveryExpressTemplateList(); + + /** + * 获得快递è¿è´¹æ¨¡æ¿åˆ†é¡µ + * + * @param pageReqVO 分页查询 + * @return 快递è¿è´¹æ¨¡æ¿åˆ†é¡µ + */ + PageResult getDeliveryExpressTemplatePage(DeliveryExpressTemplatePageReqVO pageReqVO); + + /** + * 校验快递è¿è´¹æ¨¡æ¿ + * + * 如果校验ä¸é€šè¿‡ï¼ŒæŠ›å‡º {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 异常 + * + * @param templateId 模æ¿ç¼–å· + * @return 快递è¿è´¹æ¨¡æ¿ + */ + DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); + + /** + * 基于è¿è´¹æ¨¡æ¿ç¼–å·æ•°ç»„和收件人地å€åŒºåŸŸç¼–å·ï¼ŒèŽ·å–匹é…è¿è´¹æ¨¡æ¿ + * + * @param ids ç¼–å·åˆ—表 + * @param areaId åŒºåŸŸç¼–å· + * @return Map (templateId -> è¿è´¹æ¨¡æ¿è®¾ç½®) + */ + Map getExpressTemplateMapByIdsAndArea(Collection ids, Integer areaId); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java new file mode 100644 index 0000000..bd92771 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java @@ -0,0 +1,219 @@ +package com.tashow.cloud.trade.service.delivery; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.*; +import com.tashow.cloud.trade.controller.admin.delivery.vo.expresstemplate.*; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; +import com.tashow.cloud.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper; +import com.tashow.cloud.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper; +import com.tashow.cloud.trade.dal.mysql.delivery.DeliveryExpressTemplateMapper; +import com.tashow.cloud.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static com.tashow.cloud.trade.convert.delivery.DeliveryExpressTemplateConvert.INSTANCE; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_TEMPLATE_NAME_DUPLICATE; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_TEMPLATE_NOT_EXISTS; + +/** + * 快递è¿è´¹æ¨¡æ¿ Service 实现类 + * + * @author jason + */ +@Service +@Validated +public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTemplateService { + + @Resource + private DeliveryExpressTemplateMapper expressTemplateMapper; + @Resource + private DeliveryExpressTemplateChargeMapper expressTemplateChargeMapper; + @Resource + private DeliveryExpressTemplateFreeMapper expressTemplateFreeMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createDeliveryExpressTemplate(DeliveryExpressTemplateCreateReqVO createReqVO) { + // 校验模æ¿å是å¦å”¯ä¸€ + validateTemplateNameUnique(createReqVO.getName(), null); + + // æ’å…¥ + DeliveryExpressTemplateDO template = INSTANCE.convert(createReqVO); + expressTemplateMapper.insert(template); + // æ’å…¥è¿è´¹æ¨¡æ¿è®¡è´¹è¡¨ + if (CollUtil.isNotEmpty(createReqVO.getCharges())) { + expressTemplateChargeMapper.insertBatch( + INSTANCE.convertTemplateChargeList(template.getId(), createReqVO.getChargeMode(), createReqVO.getCharges()) + ); + } + // æ’å…¥è¿è´¹æ¨¡æ¿åŒ…邮表 + if (CollUtil.isNotEmpty(createReqVO.getFrees())) { + expressTemplateFreeMapper.insertBatch( + INSTANCE.convertTemplateFreeList(template.getId(), createReqVO.getFrees()) + ); + } + return template.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateDeliveryExpressTemplate(DeliveryExpressTemplateUpdateReqVO updateReqVO) { + // 校验存在 + validateDeliveryExpressTemplateExists(updateReqVO.getId()); + // 校验模æ¿å是å¦å”¯ä¸€ + validateTemplateNameUnique(updateReqVO.getName(), updateReqVO.getId()); + + // æ›´æ–°è¿è´¹ä»Žè¡¨ + updateExpressTemplateCharge(updateReqVO.getId(), updateReqVO.getChargeMode(), updateReqVO.getCharges()); + // 更新包邮从表 + updateExpressTemplateFree(updateReqVO.getId(), updateReqVO.getFrees()); + // 更新模æ¿ä¸»è¡¨ + DeliveryExpressTemplateDO updateObj = INSTANCE.convert(updateReqVO); + expressTemplateMapper.updateById(updateObj); + } + + private void updateExpressTemplateFree(Long templateId, List frees) { + // ç¬¬ä¸€æ­¥ï¼Œå¯¹æ¯”æ–°è€æ•°æ®ï¼ŒèŽ·å¾—æ·»åŠ ã€ä¿®æ”¹ã€åˆ é™¤çš„列表 + List oldList = expressTemplateFreeMapper.selectListByTemplateId(templateId); + List newList = INSTANCE.convertTemplateFreeList(templateId, frees); + List> diffList = CollectionUtils.diffList(oldList, newList, + (oldVal, newVal) -> ObjectUtil.equal(oldVal.getId(), newVal.getId())); + + // ç¬¬äºŒæ­¥ï¼Œæ‰¹é‡æ·»åŠ ã€ä¿®æ”¹ã€åˆ é™¤ + if (CollUtil.isNotEmpty(diffList.get(0))) { + expressTemplateFreeMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + expressTemplateFreeMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + expressTemplateFreeMapper.deleteBatchIds(convertList(diffList.get(2), DeliveryExpressTemplateFreeDO::getId)); + } + } + + private void updateExpressTemplateCharge(Long templateId, Integer chargeMode, List charges) { + // ç¬¬ä¸€æ­¥ï¼Œå¯¹æ¯”æ–°è€æ•°æ®ï¼ŒèŽ·å¾—æ·»åŠ ã€ä¿®æ”¹ã€åˆ é™¤çš„列表 + List oldList = expressTemplateChargeMapper.selectListByTemplateId(templateId); + List newList = INSTANCE.convertTemplateChargeList(templateId, chargeMode, charges); + List> diffList = diffList(oldList, newList, (oldVal, newVal) -> { + boolean same = ObjectUtil.equal(oldVal.getId(), newVal.getId()); + if (same) { + newVal.setChargeMode(chargeMode); // æ›´æ–°ä¸‹æ”¶è´¹æ¨¡å¼ + } + return same; + }); + + // ç¬¬äºŒæ­¥ï¼Œæ‰¹é‡æ·»åŠ ã€ä¿®æ”¹ã€åˆ é™¤ + if (CollUtil.isNotEmpty(diffList.get(0))) { + expressTemplateChargeMapper.insertBatch(diffList.get(0)); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + expressTemplateChargeMapper.updateBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + expressTemplateChargeMapper.deleteBatchIds(convertList(diffList.get(2), DeliveryExpressTemplateChargeDO::getId)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteDeliveryExpressTemplate(Long id) { + // 校验存在 + validateDeliveryExpressTemplateExists(id); + + // 删除主表 + expressTemplateMapper.deleteById(id); + // 删除è¿è´¹ä»Žè¡¨ + expressTemplateChargeMapper.deleteByTemplateId(id); + // 删除包邮从表 + expressTemplateFreeMapper.deleteByTemplateId(id); + } + + /** + * 校验è¿è´¹æ¨¡æ¿å是å¦å”¯ä¸€ + * + * @param name 模æ¿åç§° + * @param id è¿è´¹æ¨¡æ¿ç¼–å·,å¯ä»¥ä¸º null + */ + private void validateTemplateNameUnique(String name, Long id) { + DeliveryExpressTemplateDO template = expressTemplateMapper.selectByName(name); + if (template == null) { + return; + } + // 如果 id 为空 + if (id == null) { + throw exception(EXPRESS_TEMPLATE_NAME_DUPLICATE); + } + if (!template.getId().equals(id)) { + throw exception(EXPRESS_TEMPLATE_NAME_DUPLICATE); + } + } + + private void validateDeliveryExpressTemplateExists(Long id) { + if (expressTemplateMapper.selectById(id) == null) { + throw exception(EXPRESS_TEMPLATE_NOT_EXISTS); + } + } + + @Override + public DeliveryExpressTemplateDetailRespVO getDeliveryExpressTemplate(Long id) { + List chargeList = expressTemplateChargeMapper.selectListByTemplateId(id); + List freeList = expressTemplateFreeMapper.selectListByTemplateId(id); + DeliveryExpressTemplateDO template = expressTemplateMapper.selectById(id); + return INSTANCE.convert(template, chargeList, freeList); + } + + @Override + public List getDeliveryExpressTemplateList(Collection ids) { + return expressTemplateMapper.selectBatchIds(ids); + } + + @Override + public List getDeliveryExpressTemplateList() { + return expressTemplateMapper.selectList(); + } + + @Override + public PageResult getDeliveryExpressTemplatePage(DeliveryExpressTemplatePageReqVO pageReqVO) { + return expressTemplateMapper.selectPage(pageReqVO); + } + + @Override + public DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId) { + DeliveryExpressTemplateDO template = expressTemplateMapper.selectById(templateId); + if (template == null) { + throw exception(EXPRESS_TEMPLATE_NOT_EXISTS); + } + return template; + } + + @Override + public Map getExpressTemplateMapByIdsAndArea(Collection ids, Integer areaId) { + Assert.notNull(areaId, "åŒºåŸŸç¼–å· {} ä¸èƒ½ä¸ºç©º", areaId); + // 查询 template 数组 + if (CollUtil.isEmpty(ids)) { + return Collections.emptyMap(); + } + List templateList = expressTemplateMapper.selectBatchIds(ids); + // 查询 templateCharge 数组 + List chargeList = expressTemplateChargeMapper.selectByTemplateIds(ids); + // 查询 templateFree 数组 + List freeList = expressTemplateFreeMapper.selectListByTemplateIds(ids); + + // 组åˆè¿è´¹æ¨¡æ¿é…ç½® RespBO + return INSTANCE.convertMap(areaId, templateList, chargeList, freeList); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryPickUpStoreService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryPickUpStoreService.java new file mode 100644 index 0000000..5c0f29e --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryPickUpStoreService.java @@ -0,0 +1,82 @@ +package com.tashow.cloud.trade.service.delivery; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpBindReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreCreateReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStorePageReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreUpdateReqVO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * 自æé—¨åº— Service æŽ¥å£ + * + * @author jason + */ +public interface DeliveryPickUpStoreService { + + /** + * 创建自æé—¨åº— + * + * @param createReqVO åˆ›å»ºä¿¡æ¯ + * @return ç¼–å· + */ + Long createDeliveryPickUpStore(@Valid DeliveryPickUpStoreCreateReqVO createReqVO); + + /** + * 更新自æé—¨åº— + * + * @param updateReqVO æ›´æ–°ä¿¡æ¯ + */ + void updateDeliveryPickUpStore(@Valid DeliveryPickUpStoreUpdateReqVO updateReqVO); + + /** + * 删除自æé—¨åº— + * + * @param id ç¼–å· + */ + void deleteDeliveryPickUpStore(Long id); + + /** + * 获得自æé—¨åº— + * + * @param id ç¼–å· + * @return 自æé—¨åº— + */ + DeliveryPickUpStoreDO getDeliveryPickUpStore(Long id); + + /** + * 获得自æé—¨åº—列表 + * + * @param ids ç¼–å· + * @return 自æé—¨åº—列表 + */ + List getDeliveryPickUpStoreList(Collection ids); + + /** + * 获得指定状æ€çš„自æé—¨åº—列表 + * + * @param status çŠ¶æ€ + * @return 自æé—¨åº—列表 + */ + List getDeliveryPickUpStoreListByStatus(Integer status); + + /** + * 获得自æé—¨åº—分页 + * + * @param pageReqVO 分页查询 + * @return 自æé—¨åº—分页 + */ + PageResult getDeliveryPickUpStorePage(DeliveryPickUpStorePageReqVO pageReqVO); + + /** + * 绑定自æåº—员 + * + * @param bindReqVO ç»‘å®šæ•°æ® + */ + void bindDeliveryPickUpStore(DeliveryPickUpBindReqVO bindReqVO); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryPickUpStoreServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryPickUpStoreServiceImpl.java new file mode 100644 index 0000000..91637d5 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/DeliveryPickUpStoreServiceImpl.java @@ -0,0 +1,103 @@ +package com.tashow.cloud.trade.service.delivery; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpBindReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreCreateReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStorePageReqVO; +import com.tashow.cloud.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreUpdateReqVO; +import com.tashow.cloud.trade.convert.delivery.DeliveryPickUpStoreConvert; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; +import com.tashow.cloud.trade.dal.mysql.delivery.DeliveryPickUpStoreMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PICK_UP_STORE_NOT_EXISTS; + +/** + * 自æé—¨åº— Service 实现类 + * + * @author jason + */ +@Service +@Validated +public class DeliveryPickUpStoreServiceImpl implements DeliveryPickUpStoreService { + + @Resource + private DeliveryPickUpStoreMapper deliveryPickUpStoreMapper; + + @Resource + private AdminUserApi adminUserApi; + + @Override + public Long createDeliveryPickUpStore(DeliveryPickUpStoreCreateReqVO createReqVO) { + // æ’å…¥ + DeliveryPickUpStoreDO deliveryPickUpStore = DeliveryPickUpStoreConvert.INSTANCE.convert(createReqVO); + deliveryPickUpStoreMapper.insert(deliveryPickUpStore); + // 返回 + return deliveryPickUpStore.getId(); + } + + @Override + public void updateDeliveryPickUpStore(DeliveryPickUpStoreUpdateReqVO updateReqVO) { + // 校验存在 + validateDeliveryPickUpStoreExists(updateReqVO.getId()); + // æ›´æ–° + DeliveryPickUpStoreDO updateObj = DeliveryPickUpStoreConvert.INSTANCE.convert(updateReqVO); + deliveryPickUpStoreMapper.updateById(updateObj); + } + + @Override + public void deleteDeliveryPickUpStore(Long id) { + // 校验存在 + validateDeliveryPickUpStoreExists(id); + // 删除 + deliveryPickUpStoreMapper.deleteById(id); + } + + private void validateDeliveryPickUpStoreExists(Long id) { + DeliveryPickUpStoreDO deliveryPickUpStore = deliveryPickUpStoreMapper.selectById(id); + if (deliveryPickUpStore == null) { + throw exception(PICK_UP_STORE_NOT_EXISTS); + } + } + + @Override + public DeliveryPickUpStoreDO getDeliveryPickUpStore(Long id) { + return deliveryPickUpStoreMapper.selectById(id); + } + + @Override + public List getDeliveryPickUpStoreList(Collection ids) { + return deliveryPickUpStoreMapper.selectBatchIds(ids); + } + + @Override + public List getDeliveryPickUpStoreListByStatus(Integer status) { + return deliveryPickUpStoreMapper.selectListByStatus(status); + } + + @Override + public PageResult getDeliveryPickUpStorePage(DeliveryPickUpStorePageReqVO pageReqVO) { + return deliveryPickUpStoreMapper.selectPage(pageReqVO); + } + + @Override + public void bindDeliveryPickUpStore(DeliveryPickUpBindReqVO bindReqVO) { + // 1.1 校验门店存在 + validateDeliveryPickUpStoreExists(bindReqVO.getId()); + // 1.2 校验用户存在 + adminUserApi.validateUserList(bindReqVO.getVerifyUserIds()).checkError(); + + // 2. æ›´æ–° + DeliveryPickUpStoreDO updateObj = BeanUtils.toBean(bindReqVO, DeliveryPickUpStoreDO.class); + deliveryPickUpStoreMapper.updateById(updateObj); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/bo/DeliveryExpressTemplateRespBO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/bo/DeliveryExpressTemplateRespBO.java new file mode 100644 index 0000000..2156556 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/delivery/bo/DeliveryExpressTemplateRespBO.java @@ -0,0 +1,80 @@ +package com.tashow.cloud.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; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/TradeMessageService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/TradeMessageService.java new file mode 100644 index 0000000..b9d6d3a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/TradeMessageService.java @@ -0,0 +1,19 @@ +package com.tashow.cloud.trade.service.message; + +import com.tashow.cloud.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO; + +/** + * Trade æ¶ˆæ¯ service æŽ¥å£ + * + * @author HUIHUI + */ +public interface TradeMessageService { + + /** + * 订å•å‘è´§æ—¶å‘é€é€šçŸ¥ + * + * @param reqBO å‘逿¶ˆæ¯ + */ + void sendMessageWhenDeliveryOrder(TradeOrderMessageWhenDeliveryOrderReqBO reqBO); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/TradeMessageServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/TradeMessageServiceImpl.java new file mode 100644 index 0000000..75f26dc --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/TradeMessageServiceImpl.java @@ -0,0 +1,44 @@ +package com.tashow.cloud.trade.service.message; + +import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; +import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; +import cn.iocoder.yudao.module.trade.enums.MessageTemplateConstants; +import com.tashow.cloud.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.HashMap; +import java.util.Map; + +/** + * Trade æ¶ˆæ¯ service 实现类 + * + * @author HUIHUI + */ +@Service +@Validated +public class TradeMessageServiceImpl implements TradeMessageService { + + @Resource + private NotifyMessageSendApi notifyMessageSendApi; + + @Override + public void sendMessageWhenDeliveryOrder(TradeOrderMessageWhenDeliveryOrderReqBO reqBO) { + if (true) { + return; + } + // 1ã€æž„é€ æ¶ˆæ¯ + Map msgMap = new HashMap<>(2); + msgMap.put("orderId", reqBO.getOrderId()); + msgMap.put("deliveryMessage", reqBO.getMessage()); + // TODO 芋艿:看下模版 + // 2ã€å‘é€ç«™å†…ä¿¡ + notifyMessageSendApi.sendSingleMessageToMember( + new NotifySendSingleToUserReqDTO() + .setUserId(reqBO.getUserId()) + .setTemplateCode(MessageTemplateConstants.SMS_ORDER_DELIVERY) + .setTemplateParams(msgMap)).checkError(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/bo/TradeOrderMessageWhenDeliveryOrderReqBO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/bo/TradeOrderMessageWhenDeliveryOrderReqBO.java new file mode 100644 index 0000000..c1c76e1 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/message/bo/TradeOrderMessageWhenDeliveryOrderReqBO.java @@ -0,0 +1,32 @@ +package com.tashow.cloud.trade.service.message.bo; + +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * 订å•å‘货时通知创建 Req BO + * + * @author HUIHUI + */ +@Data +public class TradeOrderMessageWhenDeliveryOrderReqBO { + + /** + * 订å•ç¼–å· + */ + @NotNull(message = "订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long orderId; + /** + * ç”¨æˆ·ç¼–å· + */ + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + /** + * æ¶ˆæ¯ + */ + @NotEmpty(message = "å‘逿¶ˆæ¯ä¸èƒ½ä¸ºç©º") + private String message; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderLogService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderLogService.java new file mode 100644 index 0000000..2d4bfdc --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderLogService.java @@ -0,0 +1,35 @@ +package com.tashow.cloud.trade.service.order; + +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderLogDO; +import com.tashow.cloud.trade.service.order.bo.TradeOrderLogCreateReqBO; +import org.springframework.scheduling.annotation.Async; + +import java.util.List; + +/** + * äº¤æ˜“ä¸‹å•æ—¥å¿— Service æŽ¥å£ + * + * @author é™ˆè³ + * @since 2023/7/6 15:44 + */ +public interface TradeOrderLogService { + + /** + * åˆ›å»ºäº¤æ˜“ä¸‹å•æ—¥å¿— + * + * @param logDTO 日志记录 + * @author é™ˆè³ + * @since 2023/7/6 15:45 + */ + @Async + void createOrderLog(TradeOrderLogCreateReqBO logDTO); + + /** + * èŽ·å¾—äº¤æ˜“è®¢å•æ—¥å¿—列表 + * + * @param orderId 订å•ç¼–å· + * @return äº¤æ˜“è®¢å•æ—¥å¿—列表 + */ + List getOrderLogListByOrderId(Long orderId); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderLogServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderLogServiceImpl.java new file mode 100644 index 0000000..2d34e09 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderLogServiceImpl.java @@ -0,0 +1,34 @@ +package com.tashow.cloud.trade.service.order; + +import com.tashow.cloud.trade.convert.order.TradeOrderLogConvert; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderLogDO; +import com.tashow.cloud.trade.dal.mysql.order.TradeOrderLogMapper; +import com.tashow.cloud.trade.service.order.bo.TradeOrderLogCreateReqBO; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + * äº¤æ˜“ä¸‹å•æ—¥å¿— Service 实现类 + * + * @author é™ˆè³ + * @since 2023/7/6 15:44 + */ +@Service +public class TradeOrderLogServiceImpl implements TradeOrderLogService { + + @Resource + private TradeOrderLogMapper tradeOrderLogMapper; + + @Override + public void createOrderLog(TradeOrderLogCreateReqBO createReqBO) { + tradeOrderLogMapper.insert(TradeOrderLogConvert.INSTANCE.convert(createReqBO)); + } + + @Override + public List getOrderLogListByOrderId(Long orderId) { + return tradeOrderLogMapper.selectListByOrderId(orderId); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderQueryService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderQueryService.java new file mode 100644 index 0000000..40708c2 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderQueryService.java @@ -0,0 +1,160 @@ +package com.tashow.cloud.trade.service.order; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderPageReqVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderSummaryRespVO; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderPageReqVO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; + +import java.util.Collection; +import java.util.List; + +import static java.util.Collections.singleton; + +/** + * 交易订å•ã€è¯»ã€‘ Service æŽ¥å£ + * + * @author èŠ‹é“æºç  + */ +public interface TradeOrderQueryService { + + // =================== Order =================== + + /** + * 获得指定编å·çš„äº¤æ˜“è®¢å• + * + * @param id 交易订å•ç¼–å· + * @return äº¤æ˜“è®¢å• + */ + TradeOrderDO getOrder(Long id); + + /** + * èŽ·å¾—æŒ‡å®šç”¨æˆ·ï¼ŒæŒ‡å®šçš„äº¤æ˜“è®¢å• + * + * @param userId ç”¨æˆ·ç¼–å· + * @param id 交易订å•ç¼–å· + * @return äº¤æ˜“è®¢å• + */ + TradeOrderDO getOrder(Long userId, Long id); + + /** + * 获得指定用户,指定活动,指定状æ€çš„äº¤æ˜“è®¢å• + * + * @param userId ç”¨æˆ·ç¼–å· + * @param combinationActivityId æ´»åŠ¨ç¼–å· + * @param status 订å•çŠ¶æ€ + * @return äº¤æ˜“è®¢å• + */ + TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status); + + /** + * 获得订å•列表 + * + * @param ids 订å•ç¼–å·æ•°ç»„ + * @return 订å•列表 + */ + List getOrderList(Collection ids); + + /** + * ã€ç®¡ç†å‘˜ã€‘获得交易订å•分页 + * + * @param reqVO 分页请求 + * @return äº¤æ˜“è®¢å• + */ + PageResult getOrderPage(TradeOrderPageReqVO reqVO); + + /** + * 获得订å•统计 + * + * @param reqVO è¯·æ±‚å‚æ•° + * @return 订å•统计 + */ + TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO); + + /** + * ã€ä¼šå‘˜ã€‘获得交易订å•分页 + * + * @param userId ç”¨æˆ·ç¼–å· + * @param reqVO 分页请求 + * @return äº¤æ˜“è®¢å• + */ + PageResult getOrderPage(Long userId, AppTradeOrderPageReqVO reqVO); + + /** + * ã€ä¼šå‘˜ã€‘èŽ·å¾—äº¤æ˜“è®¢å•æ•°é‡ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param status 订å•状æ€ã€‚如果为空,则ä¸è¿›è¡Œç­›é€‰ + * @param commonStatus 评价状æ€ã€‚如果为空,则ä¸è¿›è¡Œç­›é€‰ + * @return è®¢å•æ•°é‡ + */ + Long getOrderCount(Long userId, Integer status, Boolean commonStatus); + + /** + * ã€å‰å°ã€‘获得订å•的物æµè½¨è¿¹ + * + * @param id 订å•ç¼–å· + * @param userId ç”¨æˆ·ç¼–å· + * @return 物æµè½¨è¿¹æ•°ç»„ + */ + List getExpressTrackList(Long id, Long userId); + + /** + * ã€åŽå°ã€‘获得订å•的物æµè½¨è¿¹ + * + * @param id 订å•ç¼–å· + * @return 物æµè½¨è¿¹æ•°ç»„ + */ + List getExpressTrackList(Long id); + + /** + * ã€ä¼šå‘˜ã€‘åœ¨æŒ‡å®šæ´»åŠ¨ä¸‹ï¼Œç”¨æˆ·è´­ä¹°çš„å•†å“æ•°é‡ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param activityId æ´»åŠ¨ç¼–å· + * @param type 订å•类型 + * @return æ´»åŠ¨å•†å“æ•°é‡ + */ + int getActivityProductCount(Long userId, Long activityId, TradeOrderTypeEnum type); + + // =================== Order Item =================== + + /** + * 获得指定用户,指定的交易订å•项 + * + * @param userId ç”¨æˆ·ç¼–å· + * @param itemId 交易订å•é¡¹ç¼–å· + * @return 交易订å•项 + */ + TradeOrderItemDO getOrderItem(Long userId, Long itemId); + + /** + * 获得交易订å•项 + * + * @param id 交易订å•é¡¹ç¼–å· itemId + * @return 交易订å•项 + */ + TradeOrderItemDO getOrderItem(Long id); + + /** + * æ ¹æ®äº¤æ˜“订å•ç¼–å·ï¼ŒæŸ¥è¯¢äº¤æ˜“订å•项 + * + * @param orderId 交易订å•ç¼–å· + * @return 交易订å•项数组 + */ + default List getOrderItemListByOrderId(Long orderId) { + return getOrderItemListByOrderId(singleton(orderId)); + } + + /** + * æ ¹æ®äº¤æ˜“订å•ç¼–å·æ•°ç»„,查询交易订å•项 + * + * @param orderIds 交易订å•ç¼–å·æ•°ç»„ + * @return 交易订å•项数组 + */ + List getOrderItemListByOrderId(Collection orderIds); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderQueryServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderQueryServiceImpl.java new file mode 100644 index 0000000..411bbc6 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderQueryServiceImpl.java @@ -0,0 +1,260 @@ +package com.tashow.cloud.trade.service.order; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderPageReqVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderSummaryRespVO; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderPageReqVO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.tashow.cloud.trade.dal.mysql.order.TradeOrderItemMapper; +import com.tashow.cloud.trade.dal.mysql.order.TradeOrderMapper; +import com.tashow.cloud.trade.dal.redis.RedisKeyConstants; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.framework.delivery.core.client.ExpressClientFactory; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; +import com.tashow.cloud.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; +import com.tashow.cloud.trade.service.delivery.DeliveryExpressService; +import jakarta.annotation.Resource; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_NOT_EXISTS; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_NOT_FOUND; + +/** + * 交易订å•ã€è¯»ã€‘ Service 实现类 + * + * @author èŠ‹é“æºç  + */ +@Service +public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { + + @Resource + private ExpressClientFactory expressClientFactory; + + @Resource + private TradeOrderMapper tradeOrderMapper; + @Resource + private TradeOrderItemMapper tradeOrderItemMapper; + + @Resource + private DeliveryExpressService deliveryExpressService; + + @Resource + private MemberUserApi memberUserApi; + + // =================== Order =================== + + @Override + public TradeOrderDO getOrder(Long id) { + return tradeOrderMapper.selectById(id); + } + + @Override + public TradeOrderDO getOrder(Long userId, Long id) { + TradeOrderDO order = tradeOrderMapper.selectById(id); + if (order != null + && ObjectUtil.notEqual(order.getUserId(), userId)) { + return null; + } + return order; + } + + @Override + public TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status) { + return tradeOrderMapper.selectByUserIdAndCombinationActivityIdAndStatus(userId, combinationActivityId, status); + } + + @Override + public List getOrderList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return tradeOrderMapper.selectBatchIds(ids); + } + + @Override + public PageResult getOrderPage(TradeOrderPageReqVO reqVO) { + // æ ¹æ®ç”¨æˆ·æŸ¥è¯¢æ¡ä»¶æž„建用户编å·åˆ—表 + Set userIds = buildQueryConditionUserIds(reqVO); + if (userIds == null) { // æ²¡æŸ¥è¯¢åˆ°ç”¨æˆ·ï¼Œè¯´æ˜Žè‚¯å®šä¹Ÿæ²¡ä»–çš„è®¢å• + return PageResult.empty(); + } + + // 分页查询 + return tradeOrderMapper.selectPage(reqVO, userIds); + } + + private Set buildQueryConditionUserIds(TradeOrderPageReqVO reqVO) { + // 获得 userId 相关的查询 + Set userIds = new HashSet<>(); + if (StrUtil.isNotEmpty(reqVO.getUserMobile())) { + MemberUserRespDTO user = memberUserApi.getUserByMobile(reqVO.getUserMobile()).getCheckedData(); + if (user == null) { // æ²¡æŸ¥è¯¢åˆ°ç”¨æˆ·ï¼Œè¯´æ˜Žè‚¯å®šä¹Ÿæ²¡ä»–çš„è®¢å• + return null; + } + userIds.add(user.getId()); + } + if (StrUtil.isNotEmpty(reqVO.getUserNickname())) { + List users = memberUserApi.getUserListByNickname(reqVO.getUserNickname()).getCheckedData(); + if (CollUtil.isEmpty(users)) { // æ²¡æŸ¥è¯¢åˆ°ç”¨æˆ·ï¼Œè¯´æ˜Žè‚¯å®šä¹Ÿæ²¡ä»–çš„è®¢å• + return null; + } + userIds.addAll(convertSet(users, MemberUserRespDTO::getId)); + } + return userIds; + } + + @Override + public TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO) { + // æ ¹æ®ç”¨æˆ·æŸ¥è¯¢æ¡ä»¶æž„建用户编å·åˆ—表 + Set userIds = buildQueryConditionUserIds(reqVO); + if (userIds == null) { // æ²¡æŸ¥è¯¢åˆ°ç”¨æˆ·ï¼Œè¯´æ˜Žè‚¯å®šä¹Ÿæ²¡ä»–çš„è®¢å• + return new TradeOrderSummaryRespVO(); + } + // 查询æ¯ä¸ªå”®åŽçжæ€å¯¹åº”的数é‡ã€é‡‘é¢ + List> list = tradeOrderMapper.selectOrderSummaryGroupByRefundStatus(reqVO, userIds); + + TradeOrderSummaryRespVO vo = new TradeOrderSummaryRespVO().setAfterSaleCount(0L).setAfterSalePrice(0L); + for (Map map : list) { + Long count = MapUtil.getLong(map, "count", 0L); + Long price = MapUtil.getLong(map, "price", 0L); + // 未退款的计入订å•,部分退款ã€å…¨éƒ¨é€€æ¬¾è®¡å…¥å”®åŽ + if (TradeOrderRefundStatusEnum.NONE.getStatus().equals(MapUtil.getInt(map, "refundStatus"))) { + vo.setOrderCount(count).setOrderPayPrice(price); + } else { + vo.setAfterSaleCount(vo.getAfterSaleCount() + count).setAfterSalePrice(vo.getAfterSalePrice() + price); + } + } + return vo; + } + + @Override + public PageResult getOrderPage(Long userId, AppTradeOrderPageReqVO reqVO) { + return tradeOrderMapper.selectPage(reqVO, userId); + } + + @Override + public Long getOrderCount(Long userId, Integer status, Boolean commentStatus) { + return tradeOrderMapper.selectCountByUserIdAndStatus(userId, status, commentStatus); + } + + @Override + public List getExpressTrackList(Long id, Long userId) { + // æŸ¥è¯¢è®¢å• + TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + // æŸ¥è¯¢ç‰©æµ + return getExpressTrackList(order); + } + + @Override + public List getExpressTrackList(Long id) { + // æŸ¥è¯¢è®¢å• + TradeOrderDO order = tradeOrderMapper.selectById(id); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + // æŸ¥è¯¢ç‰©æµ + return getExpressTrackList(order); + } + + @Override + public int getActivityProductCount(Long userId, Long activityId, TradeOrderTypeEnum type) { + // 获得订å•列表 + List orders = tradeOrderMapper.selectListByUserIdAndActivityId(userId, activityId, type); + orders.removeIf(order -> TradeOrderStatusEnum.isCanceled(order.getStatus())); // 过滤掉ã€å·²å–æ¶ˆã€‘çš„è®¢å• + if (CollUtil.isEmpty(orders)) { + return 0; + } + // 获得订å•项列表 + return tradeOrderItemMapper.selectProductSumByOrderId(convertSet(orders, TradeOrderDO::getId)); + } + + /** + * 获得订å•的物æµè½¨è¿¹ + * + * @param order è®¢å• + * @return 物æµè½¨è¿¹ + */ + private List getExpressTrackList(TradeOrderDO order) { + if (order.getLogisticsId() == null) { + return Collections.emptyList(); + } + // 查询物æµå…¬å¸ + DeliveryExpressDO express = deliveryExpressService.getDeliveryExpress(order.getLogisticsId()); + if (express == null) { + throw exception(EXPRESS_NOT_EXISTS); + } + // 查询物æµè½¨è¿¹ + return getSelf().getExpressTrackList(express.getCode(), order.getLogisticsNo(), order.getReceiverMobile()); + } + + /** + * 查询物æµè½¨è¿¹ + *

+ * ç¼“å­˜çš„ç›®çš„ï¼šè€ƒè™‘åŠæ—¶æ€§è¦æ±‚ä¸é«˜ï¼Œä½†æ˜¯æ¯æ¬¡è°ƒç”¨éœ€è¦é’± + * + * @param code 快递公å¸ç¼–ç  + * @param logisticsNo å‘货快递å•å· + * @param receiverMobile æ”¶ã€å¯„件人的电è¯å·ç  + * @return 物æµè½¨è¿¹ + */ + @Cacheable(cacheNames = RedisKeyConstants.EXPRESS_TRACK, key = "#code + '-' + #logisticsNo + '-' + #receiverMobile", + unless = "#result == null") + public List getExpressTrackList(String code, String logisticsNo, String receiverMobile) { + return expressClientFactory.getDefaultExpressClient().getExpressTrackList(new ExpressTrackQueryReqDTO() + .setExpressCode(code).setLogisticsNo(logisticsNo).setPhone(receiverMobile)); + } + + // =================== Order Item =================== + + @Override + public TradeOrderItemDO getOrderItem(Long userId, Long itemId) { + TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(itemId); + if (orderItem != null + && ObjectUtil.notEqual(orderItem.getUserId(), userId)) { + return null; + } + return orderItem; + } + + @Override + public TradeOrderItemDO getOrderItem(Long id) { + return tradeOrderItemMapper.selectById(id); + } + + @Override + public List getOrderItemListByOrderId(Collection orderIds) { + if (CollUtil.isEmpty(orderIds)) { + return Collections.emptyList(); + } + return tradeOrderItemMapper.selectListByOrderId(orderIds); + } + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private TradeOrderQueryServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderUpdateService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderUpdateService.java new file mode 100644 index 0000000..98c0118 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderUpdateService.java @@ -0,0 +1,221 @@ +package com.tashow.cloud.trade.service.order; + +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderRemarkReqVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO; +import com.tashow.cloud.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import jakarta.validation.constraints.NotNull; + +import java.util.List; + +/** + * 交易订å•ã€å†™ã€‘Service æŽ¥å£ + * + * @author LeeYan9 + * @since 2022-08-26 + */ +public interface TradeOrderUpdateService { + + // =================== Order =================== + + /** + * 获得订å•ç»“ç®—ä¿¡æ¯ + * + * @param userId 登录用户 + * @param settlementReqVO 订å•结算请求 + * @return 订å•结算结果 + */ + AppTradeOrderSettlementRespVO settlementOrder(Long userId, AppTradeOrderSettlementReqVO settlementReqVO); + + /** + * ã€ä¼šå‘˜ã€‘åˆ›å»ºäº¤æ˜“è®¢å• + * + * @param userId 登录用户 + * @param createReqVO 创建交易订å•请求模型 + * @return 交易订å•çš„ + */ + TradeOrderDO createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO); + + /** + * 更新交易订å•已支付 + * + * @param id 交易订å•ç¼–å· + * @param payOrderId 支付订å•ç¼–å· + */ + void updateOrderPaid(Long id, Long payOrderId); + + /** + * åŒæ­¥è®¢å•çš„æ”¯ä»˜çŠ¶æ€ + * + * 1. Quietly 表示,å³ä½¿åŒæ­¥å¤±è´¥ï¼Œä¹Ÿä¸ä¼šæŠ›å‡ºå¼‚常 + * 2. ä»€ä¹ˆæ—¶å€™å›žå‡ºçŽ°å¼‚å¸¸ï¼Ÿå› ä¸ºæ˜¯ä¸»åŠ¨åŒæ­¥ï¼Œå¯èƒ½å’Œæ”¯ä»˜æ¨¡å—的回调通知 {@link #updateOrderPaid(Long, Long)} 存在并å‘冲çªï¼Œå¯¼è‡´æŠ›å‡ºå¼‚常 + * + * @param id 订å•ç¼–å· + * @param payOrderId 支付订å•ç¼–å· + */ + void syncOrderPayStatusQuietly(Long id, Long payOrderId); + + /** + * ã€ç®¡ç†å‘˜ã€‘å‘è´§äº¤æ˜“è®¢å• + * + * @param deliveryReqVO å‘货请求 + */ + void deliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO); + + /** + * ã€ä¼šå‘˜ã€‘æ”¶è´§äº¤æ˜“è®¢å• + * + * @param userId ç”¨æˆ·ç¼–å· + * @param id 订å•ç¼–å· + */ + void receiveOrderByMember(Long userId, Long id); + + /** + * ã€ç³»ç»Ÿã€‘è‡ªåŠ¨æ”¶è´§äº¤æ˜“è®¢å• + * + * @return æ”¶è´§æ•°é‡ + */ + int receiveOrderBySystem(); + + /** + * ã€ä¼šå‘˜ã€‘å–æ¶ˆäº¤æ˜“è®¢å• + * + * @param userId ç”¨æˆ·ç¼–å· + * @param id 订å•ç¼–å· + */ + void cancelOrderByMember(Long userId, Long id); + + /** + * ã€ç³»ç»Ÿã€‘è‡ªåŠ¨å–æ¶ˆè®¢å• + * + * @return å–æ¶ˆæ•°é‡ + */ + int cancelOrderBySystem(); + + /** + * ã€ä¼šå‘˜ã€‘åˆ é™¤è®¢å• + * + * @param userId ç”¨æˆ·ç¼–å· + * @param id 订å•ç¼–å· + */ + void deleteOrder(Long userId, Long id); + + /** + * ã€ç®¡ç†å‘˜ã€‘交易订å•备注 + * + * @param reqVO 请求 + */ + void updateOrderRemark(TradeOrderRemarkReqVO reqVO); + + /** + * ã€ç®¡ç†å‘˜ã€‘调整价格 + * + * @param reqVO 请求 + */ + void updateOrderPrice(TradeOrderUpdatePriceReqVO reqVO); + + /** + * ã€ç®¡ç†å‘˜ã€‘è°ƒæ•´åœ°å€ + * + * @param reqVO 请求 + */ + void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO); + + /** + * ã€ç®¡ç†å‘˜ã€‘æ ¸é”€è®¢å• + * + * @param userId 管ç†å‘˜ç¼–å· + * @param id 订å•ç¼–å· + */ + void pickUpOrderByAdmin(Long userId, Long id); + + /** + * ã€ç®¡ç†å‘˜ã€‘æ ¸é”€è®¢å• + * + * @param userId 管ç†å‘˜ç¼–å· + * @param pickUpVerifyCode è‡ªææ ¸é”€ç  + */ + void pickUpOrderByAdmin(Long userId, String pickUpVerifyCode); + + /** + * ã€ç®¡ç†å‘˜ã€‘æ ¹æ®è‡ªææ ¸é”€ç ï¼ŒæŸ¥è¯¢è®¢å• + * + * @param pickUpVerifyCode è‡ªææ ¸é”€ç  + */ + TradeOrderDO getByPickUpVerifyCode(String pickUpVerifyCode); + + // =================== Order Item =================== + + /** + * 当售åŽç”³è¯·åŽï¼Œæ›´æ–°äº¤æ˜“订å•项的售åŽçŠ¶æ€ + * + * @param id 交易订å•é¡¹ç¼–å· + * @param afterSaleId å”®åŽå•ç¼–å· + */ + void updateOrderItemWhenAfterSaleCreate(@NotNull Long id, @NotNull Long afterSaleId); + + /** + * 当售åŽå®ŒæˆåŽï¼Œæ›´æ–°äº¤æ˜“订å•项的售åŽçŠ¶æ€ + * + * @param id 交易订å•é¡¹ç¼–å· + * @param refundPrice é€€æ¬¾é‡‘é¢ + */ + void updateOrderItemWhenAfterSaleSuccess(@NotNull Long id, @NotNull Integer refundPrice); + + /** + * 当售åŽå–æ¶ˆï¼ˆç”¨æˆ·å–æ¶ˆã€ç®¡ç†å‘˜é©³å›žã€ç®¡ç†å‘˜æ‹’ç»æ”¶è´§ï¼‰åŽï¼Œæ›´æ–°äº¤æ˜“订å•项的售åŽçŠ¶æ€ + * + * @param id 交易订å•é¡¹ç¼–å· + */ + void updateOrderItemWhenAfterSaleCancel(@NotNull Long id); + + /** + * ã€ä¼šå‘˜ã€‘创建订å•项的评论 + * + * @param userId ç”¨æˆ·ç¼–å· + * @param createReqVO 创建请求 + * @return 得到评价 id + */ + Long createOrderItemCommentByMember(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO); + + /** + * ã€ç³»ç»Ÿã€‘创建订å•项的评论 + * + * @return è¢«è¯„è®ºçš„è®¢å•æ•° + */ + int createOrderItemCommentBySystem(); + + /** + * 更新拼团相关信æ¯åˆ°è®¢å• + * + * @param orderId 订å•ç¼–å· + * @param activityId æ‹¼å›¢æ´»åŠ¨ç¼–å· + * @param combinationRecordId æ‹¼å›¢è®°å½•ç¼–å· + * @param headId å›¢é•¿ç¼–å· + */ + void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId); + + /** + * å–æ¶ˆæ”¯ä»˜è®¢å• + * + * @param userId ç”¨æˆ·ç¼–å· + * @param orderId 订å•ç¼–å· + * @param cancelType å–æ¶ˆç±»åž‹ + */ + void cancelPaidOrder(Long userId, Long orderId, Integer cancelType); + + /** + * 更新下å•èµ é€çš„优惠券编å·åˆ°è®¢å• + * + * @param userId ç”¨æˆ·ç¼–å· + * @param orderId 订å•ç¼–å· + * @param giveCouponIds èµ é€çš„优惠券编å·åˆ—表 + */ + void updateOrderGiveCouponIds(Long userId, Long orderId, List giveCouponIds); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderUpdateServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderUpdateServiceImpl.java new file mode 100644 index 0000000..2c5e43f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/TradeOrderUpdateServiceImpl.java @@ -0,0 +1,991 @@ +package com.tashow.cloud.trade.service.order; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.module.member.api.address.MemberAddressApi; +import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO; +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; +import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; +import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderRemarkReqVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO; +import com.tashow.cloud.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO; +import com.tashow.cloud.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; +import com.tashow.cloud.trade.convert.order.TradeOrderConvert; +import com.tashow.cloud.trade.dal.dataobject.cart.CartDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryExpressDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.tashow.cloud.trade.dal.mysql.order.TradeOrderItemMapper; +import com.tashow.cloud.trade.dal.mysql.order.TradeOrderMapper; +import com.tashow.cloud.trade.dal.redis.no.TradeNoRedisDAO; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; +import com.tashow.cloud.trade.framework.order.config.TradeOrderProperties; +import com.tashow.cloud.trade.framework.order.core.annotations.TradeOrderLog; +import com.tashow.cloud.trade.framework.order.core.utils.TradeOrderLogUtils; +import com.tashow.cloud.trade.service.cart.CartService; +import com.tashow.cloud.trade.service.delivery.DeliveryExpressService; +import com.tashow.cloud.trade.service.delivery.DeliveryPickUpStoreService; +import com.tashow.cloud.trade.service.message.TradeMessageService; +import com.tashow.cloud.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO; +import com.tashow.cloud.trade.service.order.handler.TradeOrderHandler; +import com.tashow.cloud.trade.service.price.TradePriceService; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import com.tashow.cloud.trade.service.price.calculator.TradePriceCalculatorHelper; +import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotNull; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +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.framework.common.util.date.LocalDateTimeUtils.minusTime; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getTerminal; +import static cn.iocoder.yudao.module.trade.enums.MessageTemplateConstants.WXA_ORDER_DELIVERY; + +/** + * 交易订å•ã€å†™ã€‘Service 实现类 + * + * @author LeeYan9 + * @since 2022-08-26 + */ +@Service +@Slf4j +public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { + + @Resource + private TradeOrderMapper tradeOrderMapper; + @Resource + private TradeOrderItemMapper tradeOrderItemMapper; + @Resource + private TradeNoRedisDAO tradeNoRedisDAO; + + @Resource + private List tradeOrderHandlers; + + @Resource + private CartService cartService; + @Resource + private TradePriceService tradePriceService; + @Resource + private DeliveryExpressService deliveryExpressService; + @Resource + private TradeMessageService tradeMessageService; + @Resource + private DeliveryPickUpStoreService pickUpStoreService; + + @Resource + private PayOrderApi payOrderApi; + @Resource + private MemberAddressApi addressApi; + @Resource + private ProductCommentApi productCommentApi; + @Resource + public SocialClientApi socialClientApi; + @Resource + public PayRefundApi payRefundApi; + + @Resource + private TradeOrderProperties tradeOrderProperties; + + // =================== Order =================== + + @Override + public AppTradeOrderSettlementRespVO settlementOrder(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) { + // 1. èŽ·å¾—æ”¶è´§åœ°å€ + MemberAddressRespDTO address = getAddress(userId, settlementReqVO.getAddressId()); + if (address != null) { + settlementReqVO.setAddressId(address.getId()); + } + + // 2. 计算价格 + TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, settlementReqVO); + + // 3. 拼接返回 + return TradeOrderConvert.INSTANCE.convert(calculateRespBO, address); + } + + /** + * èŽ·å¾—ç”¨æˆ·åœ°å€ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param addressId 地å€ç¼–å· + * @return åœ°å€ + */ + private MemberAddressRespDTO getAddress(Long userId, Long addressId) { + if (addressId != null) { + return addressApi.getAddress(addressId, userId).getCheckedData(); + } + return addressApi.getDefaultAddress(userId).getCheckedData(); + } + + /** + * 计算订å•ä»·æ ¼ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param settlementReqVO ç»“ç®—ä¿¡æ¯ + * @return 订å•ä»·æ ¼ + */ + private TradePriceCalculateRespBO calculatePrice(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) { + // 1. 如果æ¥è‡ªè´­ç‰©è½¦ï¼Œåˆ™èŽ·å¾—è´­ç‰©è½¦çš„å•†å“ + List cartList = cartService.getCartList(userId, + convertSet(settlementReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId)); + + // 2. 计算价格 + TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList); + calculateReqBO.getItems().forEach(item -> Assert.isTrue(item.getSelected(), // 防御性编程,ä¿è¯éƒ½æ˜¯é€‰ä¸­çš„ + "商å“({}) 未设置为选中", item.getSkuId())); + return tradePriceService.calculateOrderPrice(calculateReqBO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE) + public TradeOrderDO createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO) { + // 1.1 价格计算 + TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO); + // 1.2 æž„å»ºè®¢å• + TradeOrderDO order = buildTradeOrder(userId, createReqVO, calculateRespBO); + List orderItems = buildTradeOrderItems(order, calculateRespBO); + + // 2. 订å•创建å‰çš„逻辑 + tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems)); + + // 3. ä¿å­˜è®¢å• + tradeOrderMapper.insert(order); + orderItems.forEach(orderItem -> orderItem.setOrderId(order.getId())); + tradeOrderItemMapper.insertBatch(orderItems); + + // 4. 订å•创建åŽçš„逻辑 + afterCreateTradeOrder(order, orderItems, createReqVO); + return order; + } + + private TradeOrderDO buildTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, + TradePriceCalculateRespBO calculateRespBO) { + TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, createReqVO, calculateRespBO); + order.setType(calculateRespBO.getType()); + order.setNo(tradeNoRedisDAO.generate(TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX)); + order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus()); + order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); + order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum)); + order.setUserIp(getClientIP()).setTerminal(getTerminal()); + // 使用 + èµ é€ä¼˜æƒ åˆ¸ + order.setGiveCouponTemplateCounts(calculateRespBO.getGiveCouponTemplateCounts()); + // 支付 + é€€æ¬¾ä¿¡æ¯ + order.setAdjustPrice(0).setPayStatus(false); + order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0); + // 物æµä¿¡æ¯ + order.setDeliveryType(createReqVO.getDeliveryType()); + if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType())) { + MemberAddressRespDTO address = addressApi.getAddress(createReqVO.getAddressId(), userId).getCheckedData(); + Assert.notNull(address, "地å€({}) ä¸èƒ½ä¸ºç©º", createReqVO.getAddressId()); // 价格计算时,已ç»è®¡ç®— + order.setReceiverName(address.getName()).setReceiverMobile(address.getMobile()) + .setReceiverAreaId(address.getAreaId()).setReceiverDetailAddress(address.getDetailAddress()); + } else if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.PICK_UP.getType())) { + order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile()); + order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // éšæœºä¸€ä¸ªæ ¸é”€ç ï¼Œé•¿åº¦ä¸º 8 ä½ + } + return order; + } + + private List buildTradeOrderItems(TradeOrderDO tradeOrderDO, + TradePriceCalculateRespBO calculateRespBO) { + return TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO); + } + + /** + * 订å•创建åŽï¼Œæ‰§è¡ŒåŽç½®é€»è¾‘ + *

+ * 例如说:优惠劵的扣å‡ã€ç§¯åˆ†çš„æ‰£å‡ã€æ”¯ä»˜å•的创建等等 + * + * @param order è®¢å• + * @param orderItems 订å•项 + * @param createReqVO 创建订å•请求 + */ + private void afterCreateTradeOrder(TradeOrderDO order, List orderItems, + AppTradeOrderCreateReqVO createReqVO) { + // 1. 执行订å•创建åŽç½®å¤„ç†å™¨ + tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems)); + + // 2. åˆ é™¤è´­ç‰©è½¦å•†å“ + Set cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId); + if (CollUtil.isNotEmpty(cartIds)) { + cartService.deleteCart(order.getUserId(), cartIds); + } + + // 3. 生æˆé¢„支付 + // ç‰¹æ®Šæƒ…å†µï¼šç§¯åˆ†å…‘æ¢æ—¶ï¼Œå¯èƒ½æ”¯ä»˜é‡‘é¢ä¸ºé›¶ + if (order.getPayPrice() > 0) { + createPayOrder(order, orderItems); + } + + // 4. æ’å…¥è®¢å•æ—¥å¿— + TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus()); + + // TODO @LeeYan9: 是å¯ä»¥æ€è€ƒä¸‹, 订å•çš„è¥é”€ä¼˜æƒ è®°å½•, 应该记录在哪里, 微信讨论起æ¥! + } + + private void createPayOrder(TradeOrderDO order, List orderItems) { + // 创建支付å•,用于åŽç»­çš„æ”¯ä»˜ + PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert( + order, orderItems, tradeOrderProperties); + Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO).getCheckedData(); + + // 更新到交易å•上 + tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setPayOrderId(payOrderId)); + order.setPayOrderId(payOrderId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_PAY) + public void updateOrderPaid(Long id, Long payOrderId) { + // 1.1 æ ¡éªŒè®¢å•æ˜¯å¦å­˜åœ¨ + TradeOrderDO order = validateOrderExists(id); + // 1.2 校验订å•已支付 + if (!TradeOrderStatusEnum.isUnpaid(order.getStatus()) || order.getPayStatus()) { + // 特殊:如果订å•已支付,且支付å•å·ç›¸åŒï¼Œç›´æŽ¥è¿”回,说明é‡å¤å›žè°ƒ + if (ObjectUtil.equals(order.getPayOrderId(), payOrderId)) { + log.warn("[updateOrderPaid][order({}) 已支付,且支付å•å·ç›¸åŒ({}),直接返回]", order, payOrderId); + return; + } + log.error("[updateOrderPaid][order({}) 支付å•ä¸åŒ¹é…({}),请进行处ç†ï¼order æ•°æ®æ˜¯ï¼š{}]", + id, payOrderId, JsonUtils.toJsonString(order)); + throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR); + } + + // 2. 校验支付订å•çš„åˆæ³•性 + PayOrderRespDTO payOrder = validatePayOrderPaid(order, payOrderId); + + // 3. æ›´æ–° TradeOrderDO 状æ€ä¸ºå·²æ”¯ä»˜ï¼Œç­‰å¾…å‘è´§ + int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(), + new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayStatus(true) + .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode())); + if (updateCount == 0) { + throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); + } + + // 4. 执行 TradeOrderHandler çš„åŽç½®å¤„ç† + List orderItems = tradeOrderItemMapper.selectListByOrderId(id); + tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems)); + + // 5. è®°å½•è®¢å•æ—¥å¿— + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus()); + TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue()); + } + + @Override + public void syncOrderPayStatusQuietly(Long id, Long payOrderId) { + PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId).getCheckedData(); + if (payOrder == null) { + return; + } + if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) { + return; + } + try { + getSelf().updateOrderPaid(id, payOrderId); + } catch (Throwable e) { + log.warn("[syncOrderPayStatusQuietly][id({}) payOrderId({}) åŒæ­¥æ”¯ä»˜çжæ€å¤±è´¥]", id, payOrderId, e); + } + } + + /** + * 校验支付订å•çš„åˆæ³•性 + * + * @param order äº¤æ˜“è®¢å• + * @param payOrderId 支付订å•ç¼–å· + * @return æ”¯ä»˜è®¢å• + */ + private PayOrderRespDTO validatePayOrderPaid(TradeOrderDO order, Long payOrderId) { + // 1. æ ¡éªŒæ”¯ä»˜å•æ˜¯å¦å­˜åœ¨ + PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId).getCheckedData(); + if (payOrder == null) { + log.error("[validatePayOrderPaid][order({}) payOrder({}) ä¸å­˜åœ¨ï¼Œè¯·è¿›è¡Œå¤„ç†ï¼]", order.getId(), payOrderId); + throw exception(ORDER_NOT_FOUND); + } + + // 2.1 校验支付å•已支付 + if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) { + log.error("[validatePayOrderPaid][order({}) payOrder({}) 未支付,请进行处ç†ï¼payOrder æ•°æ®æ˜¯ï¼š{}]", + order.getId(), payOrderId, JsonUtils.toJsonString(payOrder)); + throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS); + } + // 2.2 校验支付金é¢ä¸€è‡´ + if (ObjectUtil.notEqual(payOrder.getPrice(), order.getPayPrice())) { + log.error("[validatePayOrderPaid][order({}) payOrder({}) 支付金é¢ä¸åŒ¹é…,请进行处ç†ï¼order æ•°æ®æ˜¯ï¼š{},payOrder æ•°æ®æ˜¯ï¼š{}]", + order.getId(), payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder)); + throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH); + } + // 2.2 校验支付订å•匹é…(二次) + if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), order.getId().toString())) { + log.error("[validatePayOrderPaid][order({}) 支付å•ä¸åŒ¹é…({}),请进行处ç†ï¼payOrder æ•°æ®æ˜¯ï¼š{}]", + order.getId(), payOrderId, JsonUtils.toJsonString(payOrder)); + throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR); + } + return payOrder; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_DELIVERY) + public void deliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO) { + // 1.1 校验并获得交易订å•(å¯å‘货) + TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId()); + // 1.2 校验 deliveryType 是å¦ä¸ºå¿«é€’,是快递æ‰å¯ä»¥å‘è´§ + if (ObjectUtil.notEqual(order.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType())) { + throw exception(ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS); + } + + // 2. 更新订å•为已å‘è´§ + TradeOrderDO updateOrderObj = new TradeOrderDO(); + // 2.1 快递å‘è´§ + DeliveryExpressDO express = null; + if (ObjectUtil.notEqual(deliveryReqVO.getLogisticsId(), TradeOrderDO.LOGISTICS_ID_NULL)) { + express = deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId()); + updateOrderObj.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo()); + } else { + // 2.2 无需å‘è´§ + updateOrderObj.setLogisticsId(0L).setLogisticsNo(""); + } + // 执行更新 + updateOrderObj.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now()); + int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), updateOrderObj); + if (updateCount == 0) { + throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED); + } + + // 3. è®°å½•è®¢å•æ—¥å¿— + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus(), + MapUtil.builder().put("deliveryName", express != null ? express.getName() : "") + .put("logisticsNo", express != null ? deliveryReqVO.getLogisticsNo() : "").build()); + + // 4.1 å‘é€ç«™å†…ä¿¡ + tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO() + .setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null)); + // 4.2 å‘é€è®¢é˜…æ¶ˆæ¯ + getSelf().sendDeliveryOrderMessage(order, deliveryReqVO); + } + + @Async + public void sendDeliveryOrderMessage(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) { + // 构建并å‘逿¨¡ç‰ˆæ¶ˆæ¯ + Long orderId = order.getId(); + socialClientApi.sendWxaSubscribeMessage(new SocialWxaSubscribeMessageSendReqDTO() + .setUserId(order.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue()) + .setTemplateTitle(WXA_ORDER_DELIVERY) + .setPage("pages/order/detail?id=" + orderId) // 订å•详情页 + .addMessage("character_string3", String.valueOf(orderId)) // 订å•ç¼–å· + .addMessage("phrase6", TradeOrderStatusEnum.DELIVERED.getName()) // 订å•çŠ¶æ€ + .addMessage("date4", LocalDateTimeUtil.formatNormal(LocalDateTime.now()))// å‘è´§æ—¶é—´ + .addMessage("character_string5", StrUtil.blankToDefault(deliveryReqVO.getLogisticsNo(), "-")) // 快递å•å· + .addMessage("thing9", order.getReceiverDetailAddress())).checkError(); // æ”¶è´§åœ°å€ + } + + /** + * æ ¡éªŒäº¤æ˜“è®¢å•æ»¡è¶³è¢«å‘è´§çš„æ¡ä»¶ + *

+ * 1. äº¤æ˜“è®¢å•æœªå‘è´§ + * + * @param id 交易订å•ç¼–å· + * @return äº¤æ˜“è®¢å• + */ + private TradeOrderDO validateOrderDeliverable(Long id) { + TradeOrderDO order = validateOrderExists(id); + // 1. æ ¡éªŒè®¢å•æ˜¯å¦æœªå‘è´§ + if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) { + throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE); + } + + // 2. 执行 TradeOrderHandler å‰ç½®å¤„ç† + tradeOrderHandlers.forEach(handler -> handler.beforeDeliveryOrder(order)); + return order; + } + + @NotNull + private TradeOrderDO validateOrderExists(Long id) { + // æ ¡éªŒè®¢å•æ˜¯å¦å­˜åœ¨ + TradeOrderDO order = tradeOrderMapper.selectById(id); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + return order; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_RECEIVE) + public void receiveOrderByMember(Long userId, Long id) { + // 校验并获得交易订å•ï¼ˆå¯æ”¶è´§ï¼‰ + TradeOrderDO order = validateOrderReceivable(userId, id); + + // æ”¶è´§è®¢å• + receiveOrder0(order); + } + + @Override + public int receiveOrderBySystem() { + // 1. æŸ¥è¯¢è¿‡æœŸçš„å¾…æ”¯ä»˜è®¢å• + LocalDateTime expireTime = minusTime(tradeOrderProperties.getReceiveExpireTime()); + List orders = tradeOrderMapper.selectListByStatusAndDeliveryTimeLt( + TradeOrderStatusEnum.DELIVERED.getStatus(), expireTime); + if (CollUtil.isEmpty(orders)) { + return 0; + } + + // 2. é历执行,é€ä¸ªå–消 + int count = 0; + for (TradeOrderDO order : orders) { + try { + getSelf().receiveOrderBySystem(order); + count++; + } catch (Throwable e) { + log.error("[receiveOrderBySystem][order({}) 自动收货订å•异常]", order.getId(), e); + } + } + return count; + } + + /** + * 自动收货å•ä¸ªè®¢å• + * + * @param order è®¢å• + */ + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_RECEIVE) + public void receiveOrderBySystem(TradeOrderDO order) { + receiveOrder0(order); + } + + /** + * 收货订å•的核心实现 + * + * @param order è®¢å• + */ + private void receiveOrder0(TradeOrderDO order) { + // æ›´æ–° TradeOrderDO 状æ€ä¸ºå·²å®Œæˆ + int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), + new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(LocalDateTime.now())); + if (updateCount == 0) { + throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED); + } + + // æ’å…¥è®¢å•æ—¥å¿— + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus()); + } + + /** + * æ ¡éªŒäº¤æ˜“è®¢å•æ»¡è¶³å¯å”®è´§çš„æ¡ä»¶ + *

+ * 1. 交易订å•å¾…æ”¶è´§ + * + * @param userId ç”¨æˆ·ç¼–å· + * @param id 交易订å•ç¼–å· + * @return äº¤æ˜“è®¢å• + */ + private TradeOrderDO validateOrderReceivable(Long userId, Long id) { + // æ ¡éªŒè®¢å•æ˜¯å¦å­˜åœ¨ + TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + // æ ¡éªŒè®¢å•æ˜¯å¦æ˜¯å¾…æ”¶è´§çŠ¶æ€ + if (!TradeOrderStatusEnum.isDelivered(order.getStatus())) { + throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED); + } + return order; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL) + public void cancelOrderByMember(Long userId, Long id) { + // 1.1 校验存在 + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + // 1.2 æ ¡éªŒçŠ¶æ€ + if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) { + throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID); + } + + // 2. å–æ¶ˆè®¢å• + cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL); + } + + @Override + public int cancelOrderBySystem() { + // 1. æŸ¥è¯¢è¿‡æœŸçš„å¾…æ”¯ä»˜è®¢å• + LocalDateTime expireTime = minusTime(tradeOrderProperties.getPayExpireTime()); + List orders = tradeOrderMapper.selectListByStatusAndCreateTimeLt( + TradeOrderStatusEnum.UNPAID.getStatus(), expireTime); + if (CollUtil.isEmpty(orders)) { + return 0; + } + + // 2. é历执行,é€ä¸ªå–消 + int count = 0; + for (TradeOrderDO order : orders) { + try { + getSelf().cancelOrderBySystem(order); + count++; + } catch (Throwable e) { + log.error("[cancelOrderBySystem][order({}) 过期订å•异常]", order.getId(), e); + } + } + return count; + } + + /** + * è‡ªåŠ¨å–æ¶ˆå•ä¸ªè®¢å• + * + * @param order è®¢å• + */ + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL) + public void cancelOrderBySystem(TradeOrderDO order) { + cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT); + } + + /** + * å–æ¶ˆè®¢å•的核心实现 + * + * @param order è®¢å• + * @param cancelType å–æ¶ˆç±»åž‹ + */ + private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) { + // 1. æ›´æ–° TradeOrderDO 状æ€ä¸ºå·²å–消 + int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), + new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus()) + .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now())); + if (updateCount == 0) { + throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID); + } + + // 2. 执行 TradeOrderHandler çš„åŽç½®å¤„ç† + List orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId()); + tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems)); + + // 3. å¢žåŠ è®¢å•æ—¥å¿— + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus()); + } + + /** + * 如果金é¢å…¨éƒ¨è¢«é€€æ¬¾ï¼Œåˆ™å–æ¶ˆè®¢å• + * 如果还有未被退款的金é¢ï¼Œåˆ™æ— éœ€å–æ¶ˆè®¢å• + * + * @param order è®¢å• + * @param refundPrice é€€æ¬¾é‡‘é¢ + */ + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_CANCEL_AFTER_SALE) + public void cancelOrderByAfterSale(TradeOrderDO order, Integer refundPrice) { + // 1. æ›´æ–°è®¢å• + if (refundPrice < order.getPayPrice()) { + return; + } + tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()) + .setStatus(TradeOrderStatusEnum.CANCELED.getStatus()) + .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now())); + + // 2. 执行 TradeOrderHandler çš„åŽç½®å¤„ç† + List orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId()); + tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE) + public void deleteOrder(Long userId, Long id) { + // 1.1 校验存在 + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + // 1.2 æ ¡éªŒçŠ¶æ€ + if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus())) { + throw exception(ORDER_DELETE_FAIL_STATUS_NOT_CANCEL); + } + // 2. åˆ é™¤è®¢å• + tradeOrderMapper.deleteById(id); + + // 3. 记录日志 + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus()); + } + + @Override + public void updateOrderRemark(TradeOrderRemarkReqVO reqVO) { + // æ ¡éªŒå¹¶èŽ·å¾—äº¤æ˜“è®¢å• + validateOrderExists(reqVO.getId()); + + // æ›´æ–° + TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(reqVO); + tradeOrderMapper.updateById(order); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_PRICE) + public void updateOrderPrice(TradeOrderUpdatePriceReqVO reqVO) { + // 1.1 æ ¡éªŒäº¤æ˜“è®¢å• + TradeOrderDO order = validateOrderExists(reqVO.getId()); + if (order.getPayStatus()) { + throw exception(ORDER_UPDATE_PRICE_FAIL_PAID); + } + // 1.2 æ ¡éªŒè°ƒä»·é‡‘é¢æ˜¯å¦å˜åŒ– + if (order.getAdjustPrice() > 0) { + throw exception(ORDER_UPDATE_PRICE_FAIL_ALREADY); + } + // 1.3 支付价格ä¸èƒ½ä¸º 0 + int newPayPrice = order.getPayPrice() + reqVO.getAdjustPrice(); + if (newPayPrice <= 0) { + throw exception(ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR); + } + + // 2. æ›´æ–°è®¢å• + tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()) + .setAdjustPrice(reqVO.getAdjustPrice() + order.getAdjustPrice()).setPayPrice(newPayPrice)); + + // 3. æ›´æ–° TradeOrderItem,需è¦åš adjustPrice 的分摊 + List orderOrderItems = tradeOrderItemMapper.selectListByOrderId(order.getId()); + List dividePrices = TradePriceCalculatorHelper.dividePrice2(orderOrderItems, reqVO.getAdjustPrice()); + List updateItems = new ArrayList<>(); + for (int i = 0; i < orderOrderItems.size(); i++) { + TradeOrderItemDO item = orderOrderItems.get(i); + updateItems.add(new TradeOrderItemDO().setId(item.getId()) + .setAdjustPrice(item.getAdjustPrice() + dividePrices.get(i)) + .setPayPrice(item.getPayPrice() + dividePrices.get(i))); + } + tradeOrderItemMapper.updateBatch(updateItems); + + // 4. æ›´æ–°æ”¯ä»˜è®¢å• + payOrderApi.updatePayOrderPrice(order.getPayOrderId(), newPayPrice).checkError(); + + // 5. è®°å½•è®¢å•æ—¥å¿— + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus(), + MapUtil.builder().put("oldPayPrice", MoneyUtils.fenToYuanStr(order.getPayPrice())) + .put("adjustPrice", MoneyUtils.fenToYuanStr(reqVO.getAdjustPrice())) + .put("newPayPrice", MoneyUtils.fenToYuanStr(newPayPrice)).build()); + } + + @Override + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_ADDRESS) + public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) { + // æ ¡éªŒäº¤æ˜“è®¢å• + TradeOrderDO order = validateOrderExists(reqVO.getId()); + // åªæœ‰å¾…å‘货状æ€ï¼Œæ‰å¯ä»¥ä¿®æ”¹è®¢å•收货地å€ï¼› + if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())) { + throw exception(ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED); + } + + // æ›´æ–° + tradeOrderMapper.updateById(TradeOrderConvert.INSTANCE.convert(reqVO)); + + // è®°å½•è®¢å•æ—¥å¿— + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus()); + } + + @Override + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE) + public void pickUpOrderByAdmin(Long userId, Long id) { + getSelf().pickUpOrder(userId, tradeOrderMapper.selectById(id)); + } + + @Override + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE) + public void pickUpOrderByAdmin(Long userId, String pickUpVerifyCode) { + getSelf().pickUpOrder(userId, tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode)); + } + + @Override + public TradeOrderDO getByPickUpVerifyCode(String pickUpVerifyCode) { + return tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode); + } + + @Transactional(rollbackFor = Exception.class) + public void pickUpOrder(Long userId, TradeOrderDO order) { + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + if (ObjUtil.notEqual(DeliveryTypeEnum.PICK_UP.getType(), order.getDeliveryType())) { + throw exception(ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP); + } + DeliveryPickUpStoreDO deliveryPickUpStore = pickUpStoreService.getDeliveryPickUpStore(order.getPickUpStoreId()); + if (deliveryPickUpStore == null + || !CollUtil.contains(deliveryPickUpStore.getVerifyUserIds(), userId)) { + throw exception(ORDER_PICK_UP_FAIL_NOT_VERIFY_USER); + } + + receiveOrder0(order); + } + + // =================== Order Item =================== + + @Override + public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) { + // 更新订å•项 + updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), + TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), afterSaleId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) { + // 1.1 更新订å•项 + updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), + TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null); + // 1.2 执行 TradeOrderHandler çš„åŽç½®å¤„ç† + TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id); + TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId()); + tradeOrderHandlers.forEach(handler -> handler.afterCancelOrderItem(order, orderItem)); + + // 2.1 更新订å•的退款金é¢ã€ç§¯åˆ† + Integer orderRefundPrice = order.getRefundPrice() + refundPrice; + Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint(); + Integer refundStatus = isAllOrderItemAfterSaleSuccess(order.getId()) ? + TradeOrderRefundStatusEnum.ALL.getStatus() // å¦‚æžœéƒ½å”®åŽæˆåŠŸï¼Œåˆ™éœ€è¦å–æ¶ˆè®¢å• + : TradeOrderRefundStatusEnum.PART.getStatus(); + tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()) + .setRefundStatus(refundStatus) + .setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint)); + // 2.2 å¦‚æžœå…¨éƒ¨é€€æ¬¾ï¼Œåˆ™è¿›è¡Œå–æ¶ˆè®¢å• + getSelf().cancelOrderByAfterSale(order, orderRefundPrice); + } + + @Override + public void updateOrderItemWhenAfterSaleCancel(Long id) { + // 更新订å•项 + updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), + TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null); + } + + private void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus, + Long afterSaleId) { + // 更新订å•项 + int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId); + if (updateCount <= 0) { + throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL); + } + + } + + /** + * 判断指定订å•的所有订å•é¡¹ï¼Œæ˜¯ä¸æ˜¯éƒ½å”®åŽæˆåŠŸ + * + * @param id 订å•ç¼–å· + * @return 是å¦éƒ½å”®åŽæˆåŠŸ + */ + private boolean isAllOrderItemAfterSaleSuccess(Long id) { + List orderItems = tradeOrderItemMapper.selectListByOrderId(id); + return orderItems.stream().allMatch(orderItem -> Objects.equals(orderItem.getAfterSaleStatus(), + TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_COMMENT) + public Long createOrderItemCommentByMember(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO) { + // 1.1 先通过订å•项 ID,查询订å•项是å¦å­˜åœ¨ + TradeOrderItemDO orderItem = tradeOrderItemMapper.selectByIdAndUserId(createReqVO.getOrderItemId(), userId); + if (orderItem == null) { + throw exception(ORDER_ITEM_NOT_FOUND); + } + // 1.2 校验订å•ç›¸å…³çŠ¶æ€ + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderItem.getOrderId(), userId); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) { + throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED); + } + if (ObjectUtil.notEqual(order.getCommentStatus(), Boolean.FALSE)) { + throw exception(ORDER_COMMENT_STATUS_NOT_FALSE); + } + + // 2. 创建评价 + Long commentId = createOrderItemComment0(orderItem, createReqVO); + + // 3. 如果订å•项都评论了,则更新订å•è¯„ä»·çŠ¶æ€ + List orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId()); + if (!anyMatch(orderItems, item -> Objects.equals(item.getCommentStatus(), Boolean.FALSE))) { + tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE) + .setFinishTime(LocalDateTime.now())); + // å¢žåŠ è®¢å•æ—¥å¿—。注æ„ï¼šåªæœ‰åœ¨æ‰€æœ‰è®¢å•项都评价åŽï¼Œæ‰ä¼šå¢žåŠ  + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus()); + } + return commentId; + } + + @Override + public int createOrderItemCommentBySystem() { + // 1. æŸ¥è¯¢è¿‡æœŸçš„å¾…æ”¯ä»˜è®¢å• + LocalDateTime expireTime = minusTime(tradeOrderProperties.getCommentExpireTime()); + List orders = tradeOrderMapper.selectListByStatusAndReceiveTimeLt( + TradeOrderStatusEnum.COMPLETED.getStatus(), expireTime, false); + if (CollUtil.isEmpty(orders)) { + return 0; + } + + // 2. é历执行,é€ä¸ªå–消 + int count = 0; + for (TradeOrderDO order : orders) { + try { + getSelf().createOrderItemCommentBySystemBySystem(order); + count++; + } catch (Throwable e) { + log.error("[createOrderItemCommentBySystem][order({}) 过期订å•异常]", order.getId(), e); + } + } + return count; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId) { + tradeOrderMapper.updateById( + new TradeOrderDO().setId(orderId).setCombinationActivityId(activityId) + .setCombinationRecordId(combinationRecordId).setCombinationHeadId(headId)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelPaidOrder(Long userId, Long orderId, Integer cancelType) { + // 1.1 这里校验下 cancelType åªå…许拼团关闭; + if (ObjUtil.notEqual(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getType(), cancelType)) { + return; + } + // 1.2 检验订å•存在 + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + + // 1.3 æ ¡éªŒè®¢å•æ˜¯å¦æ”¯ä»˜ + if (!order.getPayStatus()) { + throw exception(ORDER_CANCEL_PAID_FAIL, "已支付"); + } + // 1.3 æ ¡éªŒè®¢å•æ˜¯å¦æœªé€€æ¬¾ + if (ObjUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) { + throw exception(ORDER_CANCEL_PAID_FAIL, "未退款"); + } + + // 2.1 å–æ¶ˆè®¢å• + cancelOrder0(order, TradeOrderCancelTypeEnum.COMBINATION_CLOSE); + // 2.2 åˆ›å»ºé€€æ¬¾å• + payRefundApi.createRefund(new PayRefundCreateReqDTO() + .setAppKey(tradeOrderProperties.getPayAppKey()) // 支付应用 + .setUserIp(NetUtil.getLocalhostStr()) // 使用本机 IP,因为是æœåС噍å‘起退款的 + .setMerchantOrderId(String.valueOf(order.getId())) // 支付å•å· + .setMerchantRefundId(String.valueOf(order.getId())) + .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice())).checkError(); // ä»·æ ¼ä¿¡æ¯ + } + + @Override + public void updateOrderGiveCouponIds(Long userId, Long orderId, List giveCouponIds) { + // 1. 检验订å•存在 + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + + // 2. 更新订å•èµ é€çš„优惠券编å·åˆ—表 + tradeOrderMapper.updateById(new TradeOrderDO().setId(orderId).setGiveCouponIds(giveCouponIds)); + } + + /** + * 创建å•个订å•的评论 + * + * @param order è®¢å• + */ + @Transactional(rollbackFor = Exception.class) + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_COMMENT) + public void createOrderItemCommentBySystemBySystem(TradeOrderDO order) { + // 1. 查询未评论的订å•项 + List orderItems = tradeOrderItemMapper.selectListByOrderIdAndCommentStatus( + order.getId(), Boolean.FALSE); + if (CollUtil.isEmpty(orderItems)) { + return; + } + + // 2. é€ä¸ªè¯„论 + for (TradeOrderItemDO orderItem : orderItems) { + // 2.1 创建评价 + AppTradeOrderItemCommentCreateReqVO commentCreateReqVO = new AppTradeOrderItemCommentCreateReqVO() + .setOrderItemId(orderItem.getId()).setAnonymous(false).setContent("") + .setBenefitScores(5).setDescriptionScores(5); + createOrderItemComment0(orderItem, commentCreateReqVO); + + // 2.2 更新订å•é¡¹è¯„ä»·çŠ¶æ€ + tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE)); + } + + // 3. 所有订å•项都评论了,则更新订å•è¯„ä»·çŠ¶æ€ + tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE) + .setFinishTime(LocalDateTime.now())); + // å¢žåŠ è®¢å•æ—¥å¿—。注æ„ï¼šåªæœ‰åœ¨æ‰€æœ‰è®¢å•项都评价åŽï¼Œæ‰ä¼šå¢žåŠ  + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus()); + } + + /** + * 创建订å•项的评论的核心实现 + * + * @param orderItem 订å•项 + * @param createReqVO 评论内容 + * @return è¯„è®ºç¼–å· + */ + private Long createOrderItemComment0(TradeOrderItemDO orderItem, AppTradeOrderItemCommentCreateReqVO createReqVO) { + // 1. 创建评价 + ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItem); + Long commentId = productCommentApi.createComment(productCommentCreateReqDTO).getCheckedData(); + + // 2. 更新订å•é¡¹è¯„ä»·çŠ¶æ€ + tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE)); + return commentId; + } + + // =================== è¥é”€ç›¸å…³çš„æ“ä½œ =================== + + /** + * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 + * + * @return 自己 + */ + private TradeOrderUpdateServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/bo/TradeOrderLogCreateReqBO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/bo/TradeOrderLogCreateReqBO.java new file mode 100644 index 0000000..297e55a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/bo/TradeOrderLogCreateReqBO.java @@ -0,0 +1,54 @@ +package com.tashow.cloud.trade.service.order.bo; + +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * è®¢å•æ—¥å¿—的创建 Request BO + * + * @author é™ˆè³ + * @since 2023/7/6 15:27 + */ +@Data +public class TradeOrderLogCreateReqBO { + + /** + * ç”¨æˆ·ç¼–å· + */ + @NotNull(message = "用户编å·ä¸èƒ½ä¸ºç©º") + private Long userId; + /** + * 用户类型 + */ + @NotNull(message = "用户类型ä¸èƒ½ä¸ºç©º") + private Integer userType; + + /** + * 订å•ç¼–å· + */ + @NotNull(message = "订å•ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long orderId; + /** + * æ“作å‰çŠ¶æ€ + */ + private Integer beforeStatus; + /** + * æ“作åŽçŠ¶æ€ + */ + @NotNull(message = "æ“作åŽçš„状æ€ä¸èƒ½ä¸ºç©º") + private Integer afterStatus; + + /** + * æ“作类型 + */ + @NotNull(message = "æ“作类型ä¸èƒ½ä¸ºç©º") + private Integer operateType; + /** + * æ“作明细 + */ + @NotEmpty(message = "æ“作明细ä¸èƒ½ä¸ºç©º") + private String content; + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeBargainOrderHandler.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeBargainOrderHandler.java new file mode 100644 index 0000000..f94a386 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeBargainOrderHandler.java @@ -0,0 +1,78 @@ +package com.tashow.cloud.trade.service.order.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi; +import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + * ç ä»·è®¢å•çš„ {@link TradeOrderHandler} 实现类 + * + * @author HUIHUI + */ +@Component +public class TradeBargainOrderHandler implements TradeOrderHandler { + + @Resource + private BargainActivityApi bargainActivityApi; + @Resource + private BargainRecordApi bargainRecordApi; + + @Override + public void beforeOrderCreate(TradeOrderDO order, List orderItems) { + if (!TradeOrderTypeEnum.isBargain(order.getType())) { + return; + } + // 明确校验一下 + Assert.isTrue(orderItems.size() == 1, "ç ä»·æ—¶ï¼Œåªå…许选择一个商å“"); + + // 扣å‡ç ä»·æ´»åŠ¨çš„åº“å­˜ + bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(), + -orderItems.get(0).getCount()).checkError(); + } + + @Override + public void afterOrderCreate(TradeOrderDO order, List orderItems) { + if (!TradeOrderTypeEnum.isBargain(order.getType())) { + return; + } + // 明确校验一下 + Assert.isTrue(orderItems.size() == 1, "ç ä»·æ—¶ï¼Œåªå…许选择一个商å“"); + + // 记录ç ä»·è®°å½•对应的订å•ç¼–å· + bargainRecordApi.updateBargainRecordOrderId(order.getBargainRecordId(), order.getId()).checkError(); + } + + @Override + public void afterCancelOrder(TradeOrderDO order, List orderItems) { + if (!TradeOrderTypeEnum.isBargain(order.getType())) { + return; + } + // 明确校验一下 + Assert.isTrue(orderItems.size() == 1, "ç ä»·æ—¶ï¼Œåªå…许选择一个商å“"); + + // å”®åŽçš„订å•项,已ç»åœ¨ afterCancelOrderItem 回滚库存,所以这里ä¸éœ€è¦é‡å¤å›žæ»š + orderItems = filterOrderItemListByNoneAfterSale(orderItems); + if (CollUtil.isEmpty(orderItems)) { + return; + } + afterCancelOrderItem(order, orderItems.get(0)); + } + + @Override + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) { + if (!TradeOrderTypeEnum.isBargain(order.getType())) { + return; + } + // æ¢å¤ï¼ˆå¢žåŠ ï¼‰ç ä»·æ´»åŠ¨çš„åº“å­˜ + bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(), orderItem.getCount()).checkError(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeBrokerageOrderHandler.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeBrokerageOrderHandler.java new file mode 100644 index 0000000..915cd4d --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeBrokerageOrderHandler.java @@ -0,0 +1,114 @@ +package com.tashow.cloud.trade.service.order.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import com.tashow.cloud.trade.convert.order.TradeOrderConvert; +import com.tashow.cloud.trade.dal.dataobject.brokerage.BrokerageUserDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; +import com.tashow.cloud.trade.service.brokerage.BrokerageRecordService; +import com.tashow.cloud.trade.service.brokerage.BrokerageUserService; +import com.tashow.cloud.trade.service.brokerage.bo.BrokerageAddReqBO; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 订å•分销的 {@link TradeOrderHandler} 实现类 + * + * @author èŠ‹é“æºç  + */ +@Component +public class TradeBrokerageOrderHandler implements TradeOrderHandler { + + @Resource + private MemberUserApi memberUserApi; + @Resource + private ProductSpuApi productSpuApi; + @Resource + private ProductSkuApi productSkuApi; + + @Resource + private BrokerageRecordService brokerageRecordService; + @Resource + private BrokerageUserService brokerageUserService; + + @Override + public void beforeOrderCreate(TradeOrderDO order, List orderItems) { + // è®¾ç½®è®¢å•æŽ¨å¹¿äºº + BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(order.getUserId()); + if (brokerageUser != null && brokerageUser.getBindUserId() != null) { + order.setBrokerageUserId(brokerageUser.getBindUserId()); + } + } + + @Override + public void afterPayOrder(TradeOrderDO order, List orderItems) { + if (order.getBrokerageUserId() == null) { + return; + } + addBrokerage(order.getUserId(), orderItems); + } + + @Override + public void afterCancelOrder(TradeOrderDO order, List orderItems) { + // 如果是未支付的订å•,ä¸ä¼šäº§ç”Ÿåˆ†é”€ç»“果,所以直接 return + if (!order.getPayStatus()) { + return; + } + if (order.getBrokerageUserId() == null) { + return; + } + + // å”®åŽçš„订å•项,已ç»åœ¨ afterCancelOrderItem 回滚库存,所以这里ä¸éœ€è¦é‡å¤å›žæ»š + orderItems = filterOrderItemListByNoneAfterSale(orderItems); + if (CollUtil.isEmpty(orderItems)) { + return; + } + orderItems.forEach(orderItem -> afterCancelOrderItem(order, orderItem)); + } + + @Override + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) { + if (order.getBrokerageUserId() == null) { + return; + } + brokerageRecordService.cancelBrokerage(BrokerageRecordBizTypeEnum.ORDER, String.valueOf(orderItem.getId())); + } + + /** + * 创建分销记录 + *

+ * ç›®å‰æ˜¯æ”¯ä»˜æˆåŠŸåŽï¼Œå°±ä¼šåˆ›å»ºåˆ†é”€è®°å½•。 + *

+ * 业内还有两ç§åšæ³•ï¼Œå¯ä»¥æ ¹æ®è‡ªå·±çš„业务调整: + * 1. 确认收货åŽï¼Œæ‰åˆ›å»ºåˆ†é”€è®°å½• + * 2. 支付 or 䏋啿ˆåŠŸæ—¶ï¼Œåˆ›å»ºåˆ†é”€è®°å½•ï¼ˆå†»ç»“ï¼‰ï¼Œç¡®è®¤æ”¶è´§è§£å†»æˆ–è€… n 天åŽè§£å†» + * + * @param userId ç”¨æˆ·ç¼–å· + * @param orderItems 订å•项 + */ + protected void addBrokerage(Long userId, List orderItems) { + MemberUserRespDTO user = memberUserApi.getUser(userId).getCheckedData(); + Assert.notNull(user); + ProductSpuRespDTO spu = productSpuApi.getSpu(orderItems.get(0).getSpuId()).getCheckedData(); + Assert.notNull(spu); + ProductSkuRespDTO sku = productSkuApi.getSku(orderItems.get(0).getSkuId()).getCheckedData(); + + // æ¯ä¸€ä¸ªè®¢å•项,都会去生æˆåˆ†é”€è®°å½• + List addList = convertList(orderItems, + item -> TradeOrderConvert.INSTANCE.convert(user, item, spu, sku)); + brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeCombinationOrderHandler.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeCombinationOrderHandler.java new file mode 100644 index 0000000..a83c79a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeCombinationOrderHandler.java @@ -0,0 +1,97 @@ +package com.tashow.cloud.trade.service.order.handler; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO; +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO; +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; +import com.tashow.cloud.trade.convert.order.TradeOrderConvert; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.service.order.TradeOrderQueryService; +import com.tashow.cloud.trade.service.order.TradeOrderUpdateService; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_FAIL_EXIST_UNPAID; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS; + +/** + * 拼团订å•çš„ {@link TradeOrderHandler} 实现类 + * + * @author HUIHUI + */ +@Component +public class TradeCombinationOrderHandler implements TradeOrderHandler { + + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ– + private TradeOrderUpdateService orderUpdateService; + @Resource + private TradeOrderQueryService orderQueryService; + + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ– + private CombinationRecordApi combinationRecordApi; + + @Override + public void beforeOrderCreate(TradeOrderDO order, List orderItems) { + // å¦‚æžœä¸æ˜¯æ‹¼å›¢è®¢å•åˆ™ç»“æŸ + if (!TradeOrderTypeEnum.isCombination(order.getType())) { + return; + } + Assert.isTrue(orderItems.size() == 1, "拼团时,åªå…许选择一个商å“"); + + // 1. æ ¡éªŒæ˜¯å¦æ»¡è¶³æ‹¼å›¢æ´»åŠ¨ç›¸å…³é™åˆ¶ + TradeOrderItemDO item = orderItems.get(0); + combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(), + order.getCombinationHeadId(), item.getSkuId(), item.getCount()).checkError(); + + // 2. 校验该用户是å¦å­˜åœ¨æœªæ”¯ä»˜çš„æ‹¼å›¢æ´»åŠ¨è®¢å•,é¿å…一个拼团å¯ä»¥ä¸‹å¤šä¸ªå•å­äº† + TradeOrderDO activityOrder = orderQueryService.getOrderByUserIdAndStatusAndCombination( + order.getUserId(), order.getCombinationActivityId(), TradeOrderStatusEnum.UNPAID.getStatus()); + if (activityOrder != null) { + throw exception(ORDER_CREATE_FAIL_EXIST_UNPAID); + } + } + + @Override + public void afterPayOrder(TradeOrderDO order, List orderItems) { + // 1.å¦‚æžœä¸æ˜¯æ‹¼å›¢è®¢å•åˆ™ç»“æŸ + if (!TradeOrderTypeEnum.isCombination(order.getType())) { + return; + } + Assert.isTrue(orderItems.size() == 1, "拼团时,åªå…许选择一个商å“"); + + // 2. 创建拼团记录 + TradeOrderItemDO item = orderItems.get(0); + CombinationRecordCreateRespDTO combinationRecord = combinationRecordApi.createCombinationRecord( + TradeOrderConvert.INSTANCE.convert(order, item)).getCheckedData(); + + // 3. 更新拼团相关信æ¯åˆ°è®¢å•ã€‚ä¸ºä»€ä¹ˆå‡ ä¸ªå­—æ®µéƒ½è¦æ›´æ–°ï¼Ÿ + // åŽŸå› æ˜¯ï¼šå¦‚æžœåˆ›å»ºè®¢å•æ—¶è‡ªå·±æ˜¯å›¢é•¿çš„æƒ…况下 combinationHeadId 是为 null 的,设置团长编å·è¿™ä¸ªæ“ä½œæ—¶åœ¨è®¢å•æ˜¯å¦åŽåˆ›å»ºæ‹¼å›¢è®°å½•æ—¶æ‰è®¾ç½®çš„。 + orderUpdateService.updateOrderCombinationInfo(order.getId(), order.getCombinationActivityId(), + combinationRecord.getCombinationRecordId(), combinationRecord.getCombinationHeadId()); + } + + @Override + public void beforeDeliveryOrder(TradeOrderDO order) { + if (!TradeOrderTypeEnum.isCombination(order.getType())) { + return; + } + // æ ¡éªŒè®¢å•æ‹¼å›¢æ˜¯å¦æˆåŠŸ + CombinationRecordRespDTO combinationRecord = combinationRecordApi.getCombinationRecordByOrderId(order.getUserId(), order.getId()).getCheckedData(); + Assert.notNull(combinationRecord, "订å•({})对应的拼团记录ä¸å­˜åœ¨", order.getId()); + if (!CombinationRecordStatusEnum.isSuccess(combinationRecord.getStatus())) { + throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS); + } + } + +} + diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeCouponOrderHandler.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeCouponOrderHandler.java new file mode 100644 index 0000000..3af8668 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeCouponOrderHandler.java @@ -0,0 +1,76 @@ +package com.tashow.cloud.trade.service.order.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.tashow.cloud.trade.service.order.TradeOrderQueryService; +import com.tashow.cloud.trade.service.order.TradeOrderUpdateService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 优惠劵的 {@link TradeOrderHandler} 实现类 + * + * @author èŠ‹é“æºç  + */ +@Component +@Slf4j +public class TradeCouponOrderHandler implements TradeOrderHandler { + + @Resource + @Lazy // 延迟加载,é¿å…循环ä¾èµ– + private TradeOrderUpdateService orderUpdateService; + @Resource + private TradeOrderQueryService orderQueryService; + + @Resource + private CouponApi couponApi; + + @Override + public void afterOrderCreate(TradeOrderDO order, List orderItems) { + if (order.getCouponId() == null || order.getCouponId() <= 0) { + return; + } + // ä¸åœ¨å‰ç½®æ‰£å‡çš„原因,是因为优惠劵è¦è®°å½•使用的订å•å· + couponApi.useCoupon(new CouponUseReqDTO().setId(order.getCouponId()).setUserId(order.getUserId()) + .setOrderId(order.getId())).checkError(); + } + + @Override + public void afterPayOrder(TradeOrderDO order, List orderItems) { + if (CollUtil.isEmpty(order.getGiveCouponTemplateCounts())) { + return; + } + // èµ é€ä¼˜æƒ åˆ¸ + try { + List couponIds = couponApi.takeCouponsByAdmin(order.getGiveCouponTemplateCounts(), order.getUserId()).getCheckedData(); + if (CollUtil.isEmpty(couponIds)) { + return; + } + orderUpdateService.updateOrderGiveCouponIds(order.getUserId(), order.getId(), couponIds); + } catch (Exception e) { + log.error("[afterPayOrder][order({}) èµ é€ä¼˜æƒ åˆ¸({})å¤±è´¥ï¼Œéœ€è¦æ‰‹å·¥è¡¥å¿]", order.getId(), order.getGiveCouponTemplateCounts(), e); + } + } + + @Override + public void afterCancelOrder(TradeOrderDO order, List orderItems) { + // 情况一:退还订å•使用的优惠券 + if (order.getCouponId() != null && order.getCouponId() > 0) { + // 退回优惠劵 + couponApi.returnUsedCoupon(order.getCouponId()); + } + // 情况二:收回赠é€çš„优惠券 + if (CollUtil.isEmpty(order.getGiveCouponIds())) { + return; + } + couponApi.invalidateCouponsByAdmin(order.getGiveCouponIds(), order.getUserId()).checkError(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeMemberPointOrderHandler.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeMemberPointOrderHandler.java new file mode 100644 index 0000000..1dd3b64 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeMemberPointOrderHandler.java @@ -0,0 +1,118 @@ +package com.tashow.cloud.trade.service.order.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.member.api.level.MemberLevelApi; +import cn.iocoder.yudao.module.member.api.point.MemberPointApi; +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; +import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; +import com.tashow.cloud.trade.dal.dataobject.aftersale.AfterSaleDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import com.tashow.cloud.trade.service.aftersale.AfterSaleService; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; + +/** + * 会员积分ã€ç­‰çº§çš„ {@link TradeOrderHandler} 实现类 + * + * @author owen + */ +@Component +public class TradeMemberPointOrderHandler implements TradeOrderHandler { + + @Resource + private MemberPointApi memberPointApi; + @Resource + private MemberLevelApi memberLevelApi; + + @Resource + private AfterSaleService afterSaleService; + + @Override + public void afterOrderCreate(TradeOrderDO order, List orderItems) { + // 扣å‡ç”¨æˆ·ç§¯åˆ†ï¼ˆè®¢å•抵扣)。ä¸åœ¨å‰ç½®æ‰£å‡çš„åŽŸå› ï¼Œæ˜¯å› ä¸ºç§¯åˆ†æ‰£å‡æ—¶ï¼Œéœ€è¦è®°å½•å…³è”业务 + reducePoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE, order.getId()); + } + + @Override + public void afterPayOrder(TradeOrderDO order, List orderItems) { + // 增加用户积分(订å•èµ é€ï¼‰ + addPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, + order.getId()); + + // 增加用户ç»éªŒ + memberLevelApi.addExperience(order.getUserId(), order.getPayPrice(), + MemberExperienceBizTypeEnum.ORDER_GIVE.getType(), String.valueOf(order.getId())).checkError(); + } + + @Override + public void afterCancelOrder(TradeOrderDO order, List orderItems) { + // å”®åŽçš„订å•项,已ç»åœ¨ afterCancelOrderItem 回滚库存,所以这里ä¸éœ€è¦é‡å¤å›žæ»š + orderItems = filterOrderItemListByNoneAfterSale(orderItems); + if (CollUtil.isEmpty(orderItems)) { + return; + } + + // å¢žåŠ ï¼ˆå›žæ»šï¼‰ç”¨æˆ·ç§¯åˆ†ï¼ˆè®¢å•æŠµæ‰£ï¼‰ + Integer usePoint = getSumValue(orderItems, TradeOrderItemDO::getUsePoint, Integer::sum); + addPoint(order.getUserId(), usePoint, MemberPointBizTypeEnum.ORDER_USE_CANCEL, + order.getId()); + + // 如下的返还,需è¦ç»è¿‡æ”¯æŒï¼Œä¹Ÿå°±æ˜¯ç»åކ afterPayOrder æµç¨‹ + if (!order.getPayStatus()) { + return; + } + // 扣å‡ï¼ˆå›žæ»šï¼‰ç§¯åˆ†ï¼ˆè®¢å•èµ é€ï¼‰ + Integer givePoint = getSumValue(orderItems, TradeOrderItemDO::getGivePoint, Integer::sum); + reducePoint(order.getUserId(), givePoint, MemberPointBizTypeEnum.ORDER_GIVE_CANCEL, + order.getId()); + // 扣å‡ï¼ˆå›žæ»šï¼‰ç”¨æˆ·ç»éªŒ + int payPrice = order.getPayPrice() - order.getRefundPrice(); + memberLevelApi.addExperience(order.getUserId(), payPrice, + MemberExperienceBizTypeEnum.ORDER_GIVE_CANCEL.getType(), String.valueOf(order.getId())).checkError(); + } + + @Override + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) { + // å¢žåŠ ï¼ˆå›žæ»šï¼‰ç§¯åˆ†ï¼ˆè®¢å•æŠµæ‰£ï¼‰ + addPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE_CANCEL_ITEM, orderItem.getId()); + // 扣å‡ï¼ˆå›žæ»šï¼‰ç§¯åˆ†ï¼ˆè®¢å•èµ é€ï¼‰ + reducePoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE_CANCEL_ITEM, orderItem.getId()); + + // 扣å‡ï¼ˆå›žæ»šï¼‰ç”¨æˆ·ç»éªŒ + AfterSaleDO afterSale = afterSaleService.getAfterSale(orderItem.getAfterSaleId()); + memberLevelApi.reduceExperience(order.getUserId(), afterSale.getRefundPrice(), + MemberExperienceBizTypeEnum.ORDER_GIVE_CANCEL_ITEM.getType(), String.valueOf(orderItem.getId())).checkError(); + } + + /** + * 添加用户积分 + *

+ * ç›®å‰æ˜¯æ”¯ä»˜æˆåŠŸåŽï¼Œå°±ä¼šåˆ›å»ºç§¯åˆ†è®°å½•。 + *

+ * 业内还有两ç§åšæ³•ï¼Œå¯ä»¥æ ¹æ®è‡ªå·±çš„业务调整: + * 1. 确认收货åŽï¼Œæ‰åˆ›å»ºç§¯åˆ†è®°å½• + * 2. 支付 or 䏋啿ˆåŠŸæ—¶ï¼Œåˆ›å»ºç§¯åˆ†è®°å½•ï¼ˆå†»ç»“ï¼‰ï¼Œç¡®è®¤æ”¶è´§è§£å†»æˆ–è€… n 天åŽè§£å†» + * + * @param userId ç”¨æˆ·ç¼–å· + * @param point å¢žåŠ ç§¯åˆ†æ•°é‡ + * @param bizType ä¸šåŠ¡ç¼–å· + * @param bizId ä¸šåŠ¡ç¼–å· + */ + protected void addPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) { + if (point != null && point > 0) { + memberPointApi.addPoint(userId, point, bizType.getType(), String.valueOf(bizId)); + } + } + + protected void reducePoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) { + if (point != null && point > 0) { + memberPointApi.reducePoint(userId, point, bizType.getType(), String.valueOf(bizId)).checkError(); + } + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeOrderHandler.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeOrderHandler.java new file mode 100644 index 0000000..0fd426a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeOrderHandler.java @@ -0,0 +1,78 @@ +package com.tashow.cloud.trade.service.order.handler; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; + +import java.util.List; + +/** + * è®¢å•æ´»åŠ¨ç‰¹æ®Šé€»è¾‘å¤„ç†å™¨ handler æŽ¥å£ + * æä¾›è®¢å•ç”Ÿå‘½å‘¨æœŸé’©å­æŽ¥å£ï¼›è®¢å•创建å‰ã€è®¢å•创建åŽã€è®¢å•支付åŽã€è®¢å•å–æ¶ˆ + * + * @author HUIHUI + */ +public interface TradeOrderHandler { + + /** + * 订å•åˆ›å»ºå‰ + * + * @param order è®¢å• + * @param orderItems 订å•项 + */ + default void beforeOrderCreate(TradeOrderDO order, List orderItems) {} + + /** + * 订å•åˆ›å»ºåŽ + * + * @param order è®¢å• + * @param orderItems 订å•项 + */ + default void afterOrderCreate(TradeOrderDO order, List orderItems) {} + + /** + * 支付订å•åŽ + * + * @param order è®¢å• + * @param orderItems 订å•项 + */ + default void afterPayOrder(TradeOrderDO order, List orderItems) {} + + /** + * 订å•å–æ¶ˆåŽ + * + * @param order è®¢å• + * @param orderItems 订å•项 + */ + default void afterCancelOrder(TradeOrderDO order, List orderItems) {} + + /** + * 订å•项喿¶ˆåŽ + * + * @param order è®¢å• + * @param orderItem 订å•项 + */ + default void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {} + + /** + * 订å•å‘è´§å‰ + * + * @param order è®¢å• + */ + default void beforeDeliveryOrder(TradeOrderDO order) {} + + // ========== 公用方法 ========== + + /** + * 过滤“未售åŽâ€çš„订å•项列表 + * + * @param orderItems 订å•项列表 + * @return 过滤åŽçš„订å•项列表 + */ + default List filterOrderItemListByNoneAfterSale(List orderItems) { + return CollectionUtils.filterList(orderItems, + item -> TradeOrderItemAfterSaleStatusEnum.isNone(item.getAfterSaleStatus())); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradePointOrderHandler.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradePointOrderHandler.java new file mode 100644 index 0000000..66337d9 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradePointOrderHandler.java @@ -0,0 +1,83 @@ +package com.tashow.cloud.trade.service.order.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.promotion.api.point.PointActivityApi; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS; + +/** + * 积分商城活动订å•çš„ {@link TradeOrderHandler} 实现类 + * + * @author HUIHUI + */ +@Component +public class TradePointOrderHandler implements TradeOrderHandler { + + @Resource + private PointActivityApi pointActivityApi; + @Resource + private MemberUserApi memberUserApi; + + @Override + public void beforeOrderCreate(TradeOrderDO order, List orderItems) { + if (!TradeOrderTypeEnum.isPoint(order.getType())) { + return; + } + // 明确校验一下 + Assert.isTrue(orderItems.size() == 1, "积分商城活动兑æ¢å•†å“å…‘æ¢æ—¶ï¼Œåªå…许选择一个商å“"); + // 校验用户剩余积分是å¦è¶³å¤Ÿå…‘æ¢å•†å“ + MemberUserRespDTO user = memberUserApi.getUser(order.getUserId()).getCheckedData(); + if (user.getPoint() < order.getUsePoint()) { + throw exception(ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS); + } + + // 扣å‡ç§¯åˆ†å•†åŸŽæ´»åŠ¨çš„åº“å­˜ + pointActivityApi.updatePointStockDecr(order.getPointActivityId(), + orderItems.get(0).getSkuId(), orderItems.get(0).getCount()).checkError(); + + // 如果支付金é¢ä¸º 0,则直接设置为已支付 + if (Objects.equals(order.getPayPrice(), 0)) { + order.setPayStatus(true).setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()); + } + } + + @Override + public void afterCancelOrder(TradeOrderDO order, List orderItems) { + if (!TradeOrderTypeEnum.isPoint(order.getType())) { + return; + } + // 明确校验一下 + Assert.isTrue(orderItems.size() == 1, "积分商城活动兑æ¢å•†å“å…‘æ¢æ—¶ï¼Œåªå…许选择一个商å“"); + + // å”®åŽçš„订å•项,已ç»åœ¨ afterCancelOrderItem 回滚库存,所以这里ä¸éœ€è¦é‡å¤å›žæ»š + orderItems = filterOrderItemListByNoneAfterSale(orderItems); + if (CollUtil.isEmpty(orderItems)) { + return; + } + afterCancelOrderItem(order, orderItems.get(0)); + } + + @Override + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) { + if (!TradeOrderTypeEnum.isPoint(order.getType())) { + return; + } + // æ¢å¤ç§¯åˆ†å•†åŸŽæ´»åŠ¨çš„åº“å­˜ + pointActivityApi.updatePointStockIncr(order.getPointActivityId(), + orderItem.getSkuId(), orderItem.getCount()).checkError(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeProductSkuOrderHandler.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeProductSkuOrderHandler.java new file mode 100644 index 0000000..be2ff51 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeProductSkuOrderHandler.java @@ -0,0 +1,46 @@ +package com.tashow.cloud.trade.service.order.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import com.tashow.cloud.trade.convert.order.TradeOrderConvert; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +import static java.util.Collections.singletonList; + +/** + * å•†å“ SKU 库存的 {@link TradeOrderHandler} 实现类 + * + * @author èŠ‹é“æºç  + */ +@Component +public class TradeProductSkuOrderHandler implements TradeOrderHandler { + + @Resource + private ProductSkuApi productSkuApi; + + @Override + public void beforeOrderCreate(TradeOrderDO order, List orderItems) { + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems)).checkError(); + } + + @Override + public void afterCancelOrder(TradeOrderDO order, List orderItems) { + // å”®åŽçš„订å•项,已ç»åœ¨ afterCancelOrderItem 回滚库存,所以这里ä¸éœ€è¦é‡å¤å›žæ»š + orderItems = filterOrderItemListByNoneAfterSale(orderItems); + if (CollUtil.isEmpty(orderItems)) { + return; + } + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems)).checkError(); + } + + @Override + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) { + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(singletonList(orderItem))).checkError(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeSeckillOrderHandler.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeSeckillOrderHandler.java new file mode 100644 index 0000000..7fb0122 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/order/handler/TradeSeckillOrderHandler.java @@ -0,0 +1,64 @@ +package com.tashow.cloud.trade.service.order.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderDO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + * ç§’æ€è®¢å•çš„ {@link TradeOrderHandler} 实现类 + * + * @author HUIHUI + */ +@Component +public class TradeSeckillOrderHandler implements TradeOrderHandler { + + @Resource + private SeckillActivityApi seckillActivityApi; + + @Override + public void beforeOrderCreate(TradeOrderDO order, List orderItems) { + if (!TradeOrderTypeEnum.isSeckill(order.getType())) { + return; + } + // 明确校验一下 + Assert.isTrue(orderItems.size() == 1, "ç§’æ€æ—¶ï¼Œåªå…许选择一个商å“"); + + // 扣å‡ç§’æ€æ´»åŠ¨çš„åº“å­˜ + seckillActivityApi.updateSeckillStockDecr(order.getSeckillActivityId(), + orderItems.get(0).getSkuId(), orderItems.get(0).getCount()).checkError(); + } + + @Override + public void afterCancelOrder(TradeOrderDO order, List orderItems) { + if (!TradeOrderTypeEnum.isSeckill(order.getType())) { + return; + } + // 明确校验一下 + Assert.isTrue(orderItems.size() == 1, "ç§’æ€æ—¶ï¼Œåªå…许选择一个商å“"); + + // å”®åŽçš„订å•项,已ç»åœ¨ afterCancelOrderItem 回滚库存,所以这里ä¸éœ€è¦é‡å¤å›žæ»š + orderItems = filterOrderItemListByNoneAfterSale(orderItems); + if (CollUtil.isEmpty(orderItems)) { + return; + } + afterCancelOrderItem(order, orderItems.get(0)); + } + + @Override + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) { + if (!TradeOrderTypeEnum.isSeckill(order.getType())) { + return; + } + // æ¢å¤ç§’æ€æ´»åŠ¨çš„åº“å­˜ + seckillActivityApi.updateSeckillStockIncr(order.getSeckillActivityId(), + orderItem.getSkuId(), orderItem.getCount()).checkError(); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/TradePriceService.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/TradePriceService.java new file mode 100644 index 0000000..232e479 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/TradePriceService.java @@ -0,0 +1,34 @@ +package com.tashow.cloud.trade.service.price; + +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeProductSettlementRespVO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * 价格计算 Service æŽ¥å£ + * + * @author èŠ‹é“æºç  + */ +public interface TradePriceService { + + /** + * ã€è®¢å•】价格计算 + * + * @param calculateReqDTO è®¡ç®—ä¿¡æ¯ + * @return 计算结果 + */ + TradePriceCalculateRespBO calculateOrderPrice(@Valid TradePriceCalculateReqBO calculateReqDTO); + + /** + * ã€å•†å“】价格计算,用于商å“列表ã€å•†å“详情 + * + * @param userId 用户编å·ï¼Œå…许为空 + * @param spuIds å•†å“ SPU ç¼–å·æ•°ç»„ + * @return 计算结果 + */ + List calculateProductPrice(Long userId, List spuIds); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/TradePriceServiceImpl.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/TradePriceServiceImpl.java new file mode 100644 index 0000000..a876b7c --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/TradePriceServiceImpl.java @@ -0,0 +1,155 @@ +package com.tashow.cloud.trade.service.price; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi; +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO; +import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi; +import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import com.tashow.cloud.trade.controller.app.order.vo.AppTradeProductSettlementRespVO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import com.tashow.cloud.trade.service.price.calculator.TradeDiscountActivityPriceCalculator; +import com.tashow.cloud.trade.service.price.calculator.TradePriceCalculator; +import com.tashow.cloud.trade.service.price.calculator.TradePriceCalculatorHelper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; + +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.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL; + +/** + * 价格计算 Service 实现类 + * + * @author èŠ‹é“æºç  + */ +@Service +@Validated +@Slf4j +public class TradePriceServiceImpl implements TradePriceService { + + @Resource + private ProductSkuApi productSkuApi; + @Resource + private ProductSpuApi productSpuApi; + @Resource + private DiscountActivityApi discountActivityApi; + @Resource + private RewardActivityApi rewardActivityApi; + + @Resource + private List priceCalculators; + + @Resource + private TradeDiscountActivityPriceCalculator discountActivityPriceCalculator; + + @Override + public TradePriceCalculateRespBO calculateOrderPrice(TradePriceCalculateReqBO calculateReqBO) { + // 1.1 èŽ·å¾—å•†å“ SKU 数组 + List skuList = checkSkuList(calculateReqBO); + // 1.2 èŽ·å¾—å•†å“ SPU 数组 + List spuList = checkSpuList(skuList); + + // 2.1 计算价格 + TradePriceCalculateRespBO calculateRespBO = TradePriceCalculatorHelper + .buildCalculateResp(calculateReqBO, spuList, skuList); + priceCalculators.forEach(calculator -> calculator.calculate(calculateReqBO, calculateRespBO)); + // 2.2 如果最终支付金é¢å°äºŽç­‰äºŽ 0,则抛出业务异常 + if (calculateReqBO.getPointActivityId() == null // 积分订å•,å…许支付金é¢ä¸º 0 + && calculateRespBO.getPrice().getPayPrice() <= 0) { + log.error("[calculatePrice][ä»·æ ¼è®¡ç®—ä¸æ­£ç¡®ï¼Œè¯·æ±‚ calculateReqDTO({}),结果 priceCalculate({})]", + calculateReqBO, calculateRespBO); + throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL); + } + return calculateRespBO; + } + + private List checkSkuList(TradePriceCalculateReqBO reqBO) { + // èŽ·å¾—å•†å“ SKU 数组 + Map skuIdCountMap = convertMap(reqBO.getItems(), + TradePriceCalculateReqBO.Item::getSkuId, TradePriceCalculateReqBO.Item::getCount); + List skus = productSkuApi.getSkuList(skuIdCountMap.keySet()).getCheckedData(); + + // æ ¡éªŒå•†å“ SKU + skus.forEach(sku -> { + Integer count = skuIdCountMap.get(sku.getId()); + if (count == null) { + throw exception(SKU_NOT_EXISTS); + } + if (count > sku.getStock()) { + throw exception(SKU_STOCK_NOT_ENOUGH); + } + }); + return skus; + } + + private List checkSpuList(List skuList) { + return productSpuApi.validateSpuList(convertSet(skuList, ProductSkuRespDTO::getSpuId)).getCheckedData(); + } + + @Override + public List calculateProductPrice(Long userId, List spuIds) { + // 1.1 获得 SPU 与 SKU 的映射 + List allSkuList = productSkuApi.getSkuListBySpuId(spuIds).getCheckedData(); + Map> spuIdAndSkuListMap = convertMultiMap(allSkuList, ProductSkuRespDTO::getSpuId); + // 1.2 获得会员等级 + MemberLevelRespDTO level = discountActivityPriceCalculator.getMemberLevel(userId); + // 1.3 èŽ·å¾—é™æ—¶æŠ˜æ‰£æ´»åЍ + Map skuIdAndDiscountMap = convertMap( + discountActivityApi.getMatchDiscountProductListBySkuIds(convertSet(allSkuList, ProductSkuRespDTO::getId)).getCheckedData(), + DiscountProductRespDTO::getSkuId); + // 1.4 获得满å‡é€æ´»åЍ + List rewardActivityMap = rewardActivityApi.getMatchRewardActivityListBySpuIds(spuIds).getCheckedData(); + + // 2. 价格计算 + return convertList(spuIds, spuId -> { + AppTradeProductSettlementRespVO spuVO = new AppTradeProductSettlementRespVO().setSpuId(spuId); + // 2.1 优惠价格 + List skuList = spuIdAndSkuListMap.get(spuId); + List skuVOList = convertList(skuList, sku -> { + AppTradeProductSettlementRespVO.Sku skuVO = new AppTradeProductSettlementRespVO.Sku() + .setId(sku.getId()); + TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem() + .setPayPrice(sku.getPrice()).setCount(1); + // è®¡ç®—é™æ—¶æŠ˜æ‰£çš„优惠价格 + DiscountProductRespDTO discountProduct = skuIdAndDiscountMap.get(sku.getId()); + Integer discountPrice = discountActivityPriceCalculator.calculateActivityPrice(discountProduct, orderItem); + // 计算 VIP ä¼˜æƒ é‡‘é¢ + Integer vipPrice = discountActivityPriceCalculator.calculateVipPrice(level, orderItem); + if (discountPrice <= 0 && vipPrice <= 0) { + return skuVO; + } + // 选择一个大的优惠 + if (discountPrice > vipPrice) { + return skuVO.setPromotionPrice(sku.getPrice() - discountPrice) + .setPromotionType(PromotionTypeEnum.DISCOUNT_ACTIVITY.getType()) + .setPromotionId(discountProduct.getId()).setPromotionEndTime(discountProduct.getActivityEndTime()); + } else { + return skuVO.setPromotionPrice(sku.getPrice() - vipPrice) + .setPromotionType(PromotionTypeEnum.MEMBER_LEVEL.getType()); + } + }); + spuVO.setSkus(skuVOList); + // 2.2 满å‡é€æ´»åЍ + RewardActivityMatchRespDTO rewardActivity = CollUtil.findOne(rewardActivityMap, + activity -> CollUtil.contains(activity.getSpuIds(), spuId)); + spuVO.setRewardActivity(BeanUtils.toBean(rewardActivity, AppTradeProductSettlementRespVO.RewardActivity.class)); + return spuVO; + }); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/bo/TradePriceCalculateReqBO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/bo/TradePriceCalculateReqBO.java new file mode 100644 index 0000000..83643ae --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/bo/TradePriceCalculateReqBO.java @@ -0,0 +1,125 @@ +package com.tashow.cloud.trade.service.price.bo; + +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 价格计算 Request BO + * + * @author yudaoæºç  + */ +@Data +public class TradePriceCalculateReqBO { + + /** + * ç”¨æˆ·ç¼–å· + * + * 对应 MemberUserDO çš„ id ç¼–å· + */ + private Long userId; + + /** + * ä¼˜æƒ åŠµç¼–å· + * + * 对应 CouponDO çš„ id ç¼–å· + */ + private Long couponId; + + /** + * 是å¦ä½¿ç”¨ç§¯åˆ† + */ + @NotNull(message = "是å¦ä½¿ç”¨ç§¯åˆ†ä¸èƒ½ä¸ºç©º") + private Boolean pointStatus; + + /** + * é…逿–¹å¼ + * + * 枚举 {@link DeliveryTypeEnum} + */ + private Integer deliveryType; + /** + * 收货地å€ç¼–å· + * + * 对应 MemberAddressDO çš„ id ç¼–å· + */ + private Long addressId; + /** + * 自æé—¨åº—ç¼–å· + * + * 对应 PickUpStoreDO çš„ id ç¼–å· + */ + private Long pickUpStoreId; + + /** + * å•†å“ SKU 数组 + */ + @NotNull(message = "商哿•°ç»„ä¸èƒ½ä¸ºç©º") + private List items; + + // ========== ç§’æ€æ´»åŠ¨ç›¸å…³å­—æ®µ ========== + /** + * ç§’æ€æ´»åŠ¨ç¼–å· + */ + private Long seckillActivityId; + + // ========== 拼团活动相关字段 ========== + /** + * æ‹¼å›¢æ´»åŠ¨ç¼–å· + */ + private Long combinationActivityId; + + /** + * æ‹¼å›¢å›¢é•¿ç¼–å· + */ + private Long combinationHeadId; + + // ========== ç ä»·æ´»åŠ¨ç›¸å…³å­—æ®µ ========== + /** + * ç ä»·è®°å½•ç¼–å· + */ + private Long bargainRecordId; + + // ========== 积分商城活动相关字段 ========== + /** + * ç§¯åˆ†å•†åŸŽæ´»åŠ¨ç¼–å· + */ + private Long pointActivityId; + + /** + * å•†å“ SKU + */ + @Data + @Valid + public static class Item { + + /** + * SKU ç¼–å· + */ + @NotNull(message = "å•†å“ SKU ç¼–å·ä¸èƒ½ä¸ºç©º") + private Long skuId; + + /** + * SKU æ•°é‡ + */ + @NotNull(message = "å•†å“ SKU æ•°é‡ä¸èƒ½ä¸ºç©º") + @Min(value = 0L, message = "å•†å“ SKU æ•°é‡å¿…须大于等于 0") + private Integer count; + + /** + * è´­ç‰©è½¦é¡¹çš„ç¼–å· + */ + private Long cartId; + + /** + * 是å¦é€‰ä¸­ + */ + @NotNull(message = "是å¦é€‰ä¸­ä¸èƒ½ä¸ºç©º") + private Boolean selected; + + } +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/bo/TradePriceCalculateRespBO.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/bo/TradePriceCalculateRespBO.java new file mode 100644 index 0000000..c33d66b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/bo/TradePriceCalculateRespBO.java @@ -0,0 +1,405 @@ +package com.tashow.cloud.trade.service.price.bo; + +import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * 价格计算 Response BO + * + * 整体设计,å‚考 taobao 的技术文档: + * 1. 订å•ç®¡ç† + * 2. 常用订å•金é¢è¯´æ˜Ž + * + * @author èŠ‹é“æºç  + */ +@Data +public class TradePriceCalculateRespBO { + + /** + * 订å•类型 + * + * 枚举 {@link TradeOrderTypeEnum} + */ + private Integer type; + + /** + * 订å•ä»·æ ¼ + */ + private Price price; + + /** + * 订å•项数组 + */ + private List items; + + /** + * è¥é”€æ´»åŠ¨æ•°ç»„ + * + * åªå¯¹åº” {@link Price#items} 商å“匹é…的活动 + */ + private List promotions; + + /** + * ä½¿ç”¨çš„ä¼˜æƒ åŠµç¼–å· + */ + private Long couponId; + /** + * 用户的优惠劵列表(å¯ç”¨ + ä¸å¯ç”¨ï¼‰ + */ + private List coupons; + + /** + * 会员剩余积分 + */ + private Integer totalPoint; + /** + * 使用的积分 + */ + private Integer usePoint; + + /** + * èµ é€çš„积分 + */ + private Integer givePoint; + + /** + * ç ä»·æ´»åŠ¨ç¼–å· + */ + private Long bargainActivityId; + + /** + * 是å¦åŒ…é‚® + */ + private Boolean freeDelivery; + + /** + * èµ é€çš„优惠劵 + * + * key: ä¼˜æƒ åŠµæ¨¡ç‰ˆç¼–å· + * valueï¼šå¯¹åº”çš„ä¼˜æƒ åˆ¸æ•°é‡ + * + * ç›®çš„ï¼šç”¨äºŽè®¢å•æ”¯ä»˜åŽèµ é€ä¼˜æƒ åˆ¸ + */ + private Map giveCouponTemplateCounts; + + /** + * 订å•ä»·æ ¼ + */ + @Data + public static class Price { + + /** + * 商å“原价(总),å•ä½ï¼šåˆ† + * + * 基于 {@link OrderItem#getPrice()} * {@link OrderItem#getCount()} 求和 + * + * 对应 taobao çš„ trade.total_fee 字段 + */ + private Integer totalPrice; + /** + * 订å•优惠(总),å•ä½ï¼šåˆ† + * + * 对应 taobao çš„ order.discount_fee 字段 + */ + private Integer discountPrice; + /** + * è¿è´¹é‡‘é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer deliveryPrice; + /** + * 优惠劵å‡å…金é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ trade.coupon_fee 字段 + */ + private Integer couponPrice; + /** + * 积分抵扣的金é¢ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ trade.point_fee 字段 + */ + private Integer pointPrice; + /** + * VIP å‡å…金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer vipPrice; + /** + * 最终购买金é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ† + * + * = {@link #totalPrice} + * - {@link #couponPrice} + * - {@link #pointPrice} + * - {@link #discountPrice} + * + {@link #deliveryPrice} + * - {@link #vipPrice} + */ + private Integer payPrice; + + } + + /** + * 订å•å•†å“ SKU + */ + @Data + public static class OrderItem { + + /** + * SPU ç¼–å· + */ + private Long spuId; + /** + * SKU ç¼–å· + */ + private Long skuId; + /** + * è´­ä¹°æ•°é‡ + */ + private Integer count; + /** + * è´­ç‰©è½¦é¡¹çš„ç¼–å· + */ + private Long cartId; + /** + * 是å¦é€‰ä¸­ + */ + private Boolean selected; + + /** + * 商å“原价(å•),å•ä½ï¼šåˆ† + * + * 对应 ProductSkuDO çš„ price 字段 + * 对应 taobao çš„ order.price 字段 + */ + private Integer price; + /** + * 优惠金é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ order.discount_fee 字段 + */ + private Integer discountPrice; + /** + * è¿è´¹é‡‘é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ† + */ + private Integer deliveryPrice; + /** + * 优惠劵å‡å…金é¢ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ trade.coupon_fee 字段 + */ + private Integer couponPrice; + /** + * 积分抵扣的金é¢ï¼Œå•ä½ï¼šåˆ† + * + * 对应 taobao çš„ trade.point_fee 字段 + */ + private Integer pointPrice; + /** + * 使用的积分 + */ + private Integer usePoint; + /** + * VIP å‡å…金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer vipPrice; + /** + * 应付金é¢ï¼ˆæ€»ï¼‰ï¼Œå•ä½ï¼šåˆ† + * + * = {@link #price} * {@link #count} + * - {@link #couponPrice} + * - {@link #pointPrice} + * - {@link #discountPrice} + * + {@link #deliveryPrice} + * - {@link #vipPrice} + */ + private Integer payPrice; + + // ========== å•†å“ SPU ä¿¡æ¯ ========== + /** + * 商å“å + */ + private String spuName; + /** + * 商å“图片 + * + * 优先级:SKU.picUrl > SPU.picUrl + */ + private String picUrl; + /** + * åˆ†ç±»ç¼–å· + */ + private Long categoryId; + + // ========== 物æµç›¸å…³å­—段 ========= + + /** + * é…逿–¹å¼æ•°ç»„ + * + * 对应 DeliveryTypeEnum 枚举 + */ + private List deliveryTypes; + + /** + * 物æµé…置模æ¿ç¼–å· + * + * 对应 TradeDeliveryExpressTemplateDO çš„ id ç¼–å· + */ + private Long deliveryTemplateId; + + // ========== å•†å“ SKU ä¿¡æ¯ ========== + /** + * 商å“é‡é‡ï¼Œå•ä½ï¼škg åƒå…‹ + */ + private Double weight; + /** + * 商å“体积,å•ä½ï¼šm^3 平米 + */ + private Double volume; + + /** + * 商å“属性数组 + */ + private List properties; + + /** + * èµ é€çš„积分 + */ + private Integer givePoint; + + } + + /** + * è¥é”€æ˜Žç»† + */ + @Data + public static class Promotion { + + /** + * è¥é”€ç¼–å· + * + * 例如说:è¥é”€æ´»åŠ¨çš„ç¼–å·ã€ä¼˜æƒ åŠµçš„ç¼–å· + */ + private Long id; + /** + * è¥é”€åå­— + */ + private String name; + /** + * è¥é”€ç±»åž‹ + * + * 枚举 {@link PromotionTypeEnum} + */ + private Integer type; + /** + * 计算时的原价(总),å•ä½ï¼šåˆ† + */ + private Integer totalPrice; + /** + * 计算时的优惠(总),å•ä½ï¼šåˆ† + */ + private Integer discountPrice; + /** + * 匹é…çš„å•†å“ SKU 数组 + */ + private List items; + + // ========== åŒ¹é…æƒ…况 ========== + + /** + * æ˜¯å¦æ»¡è¶³ä¼˜æƒ æ¡ä»¶ + */ + private Boolean match; + /** + * 满足æ¡ä»¶çš„æç¤º + * + * 如果 {@link #match} = true 满足,则æç¤ºâ€œåœ£è¯žä»·:çœ 150.00 元†+ * 如果 {@link #match} = false 䏿»¡è¶³ï¼Œåˆ™æç¤ºâ€œè´­æ»¡ 85 元,å¯å‡ 40 元†+ */ + private String description; + + } + + /** + * è¥é”€åŒ¹é…çš„å•†å“ SKU + */ + @Data + public static class PromotionItem { + + /** + * å•†å“ SKU ç¼–å· + */ + private Long skuId; + /** + * 计算时的原价(总),å•ä½ï¼šåˆ† + */ + private Integer totalPrice; + /** + * 计算时的优惠(总),å•ä½ï¼šåˆ† + */ + private Integer discountPrice; + + } + + /** + * ä¼˜æƒ åŠµä¿¡æ¯ + */ + @Data + public static class Coupon { + + /** + * ä¼˜æƒ åŠµç¼–å· + */ + private Long id; + /** + * 优惠劵å + */ + private String name; + + /** + * 是å¦è®¾ç½®æ»¡å¤šå°‘金é¢å¯ç”¨ï¼Œå•ä½ï¼šåˆ† + */ + private Integer usePrice; + + /** + * 生效开始时间 + */ + private LocalDateTime validStartTime; + /** + * ç”Ÿæ•ˆç»“æŸæ—¶é—´ + */ + private LocalDateTime validEndTime; + + /** + * 优惠类型 + */ + private Integer discountType; + /** + * 折扣百分比 + */ + private Integer discountPercent; + /** + * 优惠金é¢ï¼Œå•ä½ï¼šåˆ† + */ + private Integer discountPrice; + /** + * 折扣上é™ï¼Œå•ä½ï¼šåˆ† + */ + private Integer discountLimitPrice; + + /** + * 是å¦åŒ¹é… + */ + private Boolean match; + /** + * ä¸åŒ¹é…的原因 + */ + private String mismatchReason; + + } + + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeBargainActivityPriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeBargainActivityPriceCalculator.java new file mode 100644 index 0000000..aae3752 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeBargainActivityPriceCalculator.java @@ -0,0 +1,58 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; +import cn.iocoder.yudao.module.promotion.api.bargain.dto.BargainValidateJoinRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +// TODO huihuiï¼šå•æµ‹éœ€è¦è¡¥å…… +/** + * ç ä»·æ´»åŠ¨çš„ {@link TradePriceCalculator} 实现类 + * + * @author èŠ‹é“æºç  + */ +@Component +@Order(TradePriceCalculator.ORDER_BARGAIN_ACTIVITY) +public class TradeBargainActivityPriceCalculator implements TradePriceCalculator { + + @Resource + private BargainRecordApi bargainRecordApi; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 1. 判断订å•类型和是å¦å…·æœ‰æ‹¼å›¢è®°å½•ç¼–å· + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.BARGAIN.getType())) { + return; + } + Assert.isTrue(param.getItems().size() == 1, "ç ä»·æ—¶ï¼Œåªå…许选择一个商å“"); + Assert.isTrue(param.getItems().get(0).getCount() == 1, "ç ä»·æ—¶ï¼Œåªå…许选择一个商å“"); + // 2. 校验是å¦å¯ä»¥å‚与ç ä»· + TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0); + BargainValidateJoinRespDTO bargainActivity = bargainRecordApi.validateJoinBargain( + param.getUserId(), param.getBargainRecordId(), orderItem.getSkuId()).getCheckedData(); + + // 3.1 记录优惠明细 + Integer discountPrice = orderItem.getPayPrice() - bargainActivity.getBargainPrice() * orderItem.getCount(); + // TODO 芋艿:æžç«¯æƒ…况,优惠金é¢ä¸ºè´Ÿæ•°ï¼Œéœ€è¦å¤„ç† + TradePriceCalculatorHelper.addPromotion(result, orderItem, + param.getSeckillActivityId(), bargainActivity.getName(), PromotionTypeEnum.BARGAIN_ACTIVITY.getType(), + StrUtil.format("ç ä»·æ´»åŠ¨ï¼šçœ {} å…ƒ", TradePriceCalculatorHelper.formatPrice(discountPrice)), + discountPrice); + // 3.2 æ›´æ–° SKU ä¼˜æƒ é‡‘é¢ + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice); + TradePriceCalculatorHelper.recountPayPrice(orderItem); + TradePriceCalculatorHelper.recountAllPrice(result); + // 4. 特殊:设置对应的ç ä»·æ´»åŠ¨ç¼–å· + result.setBargainActivityId(bargainActivity.getActivityId()); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeCombinationActivityPriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeCombinationActivityPriceCalculator.java new file mode 100644 index 0000000..432ac7f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeCombinationActivityPriceCalculator.java @@ -0,0 +1,54 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +// TODO @puhui999ï¼šå•æµ‹å¯ä»¥åŽè¡¥ä¸‹ + +/** + * 拼团活动的 {@link TradePriceCalculator} 实现类 + * + * @author HUIHUI + */ +@Component +@Order(TradePriceCalculator.ORDER_COMBINATION_ACTIVITY) +public class TradeCombinationActivityPriceCalculator implements TradePriceCalculator { + + @Resource + private CombinationRecordApi combinationRecordApi; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 1. 判断订å•类型和是å¦å…·æœ‰æ‹¼å›¢æ´»åŠ¨ç¼–å· + if (param.getCombinationActivityId() == null) { + return; + } + Assert.isTrue(param.getItems().size() == 1, "拼团时,åªå…许选择一个商å“"); + // 2. 校验是å¦å¯ä»¥å‚与拼团 + TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0); + CombinationValidateJoinRespDTO combinationActivity = combinationRecordApi.validateJoinCombination( + param.getUserId(), param.getCombinationActivityId(), param.getCombinationHeadId(), + orderItem.getSkuId(), orderItem.getCount()).getCheckedData(); + + // 3.1 记录优惠明细 + Integer discountPrice = orderItem.getPayPrice() - combinationActivity.getCombinationPrice() * orderItem.getCount(); + TradePriceCalculatorHelper.addPromotion(result, orderItem, + param.getCombinationActivityId(), combinationActivity.getName(), PromotionTypeEnum.COMBINATION_ACTIVITY.getType(), + StrUtil.format("æ‹¼å›¢æ´»åŠ¨ï¼šçœ {} å…ƒ", TradePriceCalculatorHelper.formatPrice(discountPrice)), + discountPrice); + // 3.2 æ›´æ–° SKU ä¼˜æƒ é‡‘é¢ + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice); + TradePriceCalculatorHelper.recountPayPrice(orderItem); + TradePriceCalculatorHelper.recountAllPrice(result); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeCouponPriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeCouponPriceCalculator.java new file mode 100644 index 0000000..3c3f745 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeCouponPriceCalculator.java @@ -0,0 +1,162 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import jakarta.annotation.Resource; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.function.Predicate; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_CAN_NOT_USE; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER; + +/** + * 优惠劵的 {@link TradePriceCalculator} 实现类 + * + * @author èŠ‹é“æºç  + */ +@Component +@Order(TradePriceCalculator.ORDER_COUPON) +public class TradeCouponPriceCalculator implements TradePriceCalculator { + + @Resource + private CouponApi couponApi; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // åªæœ‰ã€æ™®é€šã€‘订å•,æ‰å…许使用优惠劵 + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) { + if (param.getCouponId() != null) { + throw exception(PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER); + } + return; + } + + // 1.1 加载用户的优惠劵列表 + List coupons = couponApi.getCouponListByUserId(param.getUserId(), CouponStatusEnum.UNUSED.getStatus()).getCheckedData(); + coupons.removeIf(coupon -> LocalDateTimeUtils.beforeNow(coupon.getValidEndTime())); + // 1.2 计算优惠劵的使用æ¡ä»¶ + result.setCoupons(calculateCoupons(coupons, result)); + + // 2. 校验优惠劵是å¦å¯ç”¨ + if (param.getCouponId() == null) { + return; + } + TradePriceCalculateRespBO.Coupon couponBO = CollUtil.findOne(result.getCoupons(), item -> item.getId().equals(param.getCouponId())); + CouponRespDTO coupon = CollUtil.findOne(coupons, item -> item.getId().equals(param.getCouponId())); + if (couponBO == null || coupon == null) { + throw exception(PRICE_CALCULATE_COUPON_CAN_NOT_USE, "优惠劵ä¸å­˜åœ¨"); + } + if (Boolean.FALSE.equals(couponBO.getMatch())) { + throw exception(PRICE_CALCULATE_COUPON_CAN_NOT_USE, couponBO.getMismatchReason()); + } + + // 3.1 计算å¯ä»¥ä¼˜æƒ çš„é‡‘é¢ + List orderItems = filterMatchCouponOrderItems(result, coupon); + Integer totalPayPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems); + Integer couponPrice = getCouponPrice(coupon, totalPayPrice); + // 3.2 è®¡ç®—åˆ†æ‘Šçš„ä¼˜æƒ é‡‘é¢ + List divideCouponPrices = TradePriceCalculatorHelper.dividePrice(orderItems, couponPrice); + + // 4.1 记录使用的优惠劵 + result.setCouponId(param.getCouponId()); + // 4.2 记录优惠明细 + TradePriceCalculatorHelper.addPromotion(result, orderItems, + param.getCouponId(), couponBO.getName(), PromotionTypeEnum.COUPON.getType(), + StrUtil.format("ä¼˜æƒ åŠµï¼šçœ {} å…ƒ", TradePriceCalculatorHelper.formatPrice(couponPrice)), + divideCouponPrices); + // 4.3 æ›´æ–° SKU ä¼˜æƒ é‡‘é¢ + for (int i = 0; i < orderItems.size(); i++) { + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); + orderItem.setCouponPrice(divideCouponPrices.get(i)); + TradePriceCalculatorHelper.recountPayPrice(orderItem); + } + TradePriceCalculatorHelper.recountAllPrice(result); + } + + /** + * 计算用户的优惠劵列表(å¯ç”¨ + ä¸å¯ç”¨ï¼‰ + * + * @param coupons 优惠劵 + * @param result 计算结果 + * @return 优惠劵列表 + */ + private List calculateCoupons(List coupons, + TradePriceCalculateRespBO result) { + return convertList(coupons, coupon -> { + TradePriceCalculateRespBO.Coupon matchCoupon = BeanUtils.toBean(coupon, TradePriceCalculateRespBO.Coupon.class); + // 1.1 优惠劵未到使用时间 + if (LocalDateTimeUtils.afterNow(coupon.getValidStartTime())) { + return matchCoupon.setMatch(false).setMismatchReason("优惠劵未到使用时间"); + } + // 1.2 优惠劵没有匹é…çš„å•†å“ + List orderItems = filterMatchCouponOrderItems(result, coupon); + if (CollUtil.isEmpty(orderItems)) { + return matchCoupon.setMatch(false).setMismatchReason("优惠劵没有匹é…的商å“"); + } + // 1.3 å·® %1$,.2f å…ƒå¯ç”¨ä¼˜æƒ åе + Integer totalPayPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems); + if (totalPayPrice < coupon.getUsePrice()) { + return matchCoupon.setMatch(false) + .setMismatchReason(String.format("å·® %1$,.2f å…ƒå¯ç”¨ä¼˜æƒ åе", (coupon.getUsePrice() - totalPayPrice) / 100D)); + } + // 1.4 优惠金é¢è¶…过订å•é‡‘é¢ + Integer couponPrice = getCouponPrice(coupon, totalPayPrice); + if (couponPrice >= totalPayPrice) { + return matchCoupon.setMatch(false).setMismatchReason("优惠金é¢è¶…过订å•金é¢"); + } + + // 2. 满足æ¡ä»¶ + return matchCoupon.setMatch(true); + }); + } + + private Integer getCouponPrice(CouponRespDTO coupon, Integer totalPayPrice) { + if (PromotionDiscountTypeEnum.PRICE.getType().equals(coupon.getDiscountType())) { // å‡ä»· + return coupon.getDiscountPrice(); + } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(coupon.getDiscountType())) { // 打折 + int couponPrice = totalPayPrice - (totalPayPrice * coupon.getDiscountPercent() / 100); + return coupon.getDiscountLimitPrice() == null ? couponPrice + : Math.min(couponPrice, coupon.getDiscountLimitPrice()); // ä¼˜æƒ ä¸Šé™ + } + throw new IllegalArgumentException(String.format("优惠劵(%s) çš„ä¼˜æƒ ç±»åž‹ä¸æ­£ç¡®", coupon)); + } + + /** + * 获得优惠劵å¯ä½¿ç”¨çš„订å•项(商å“)列表 + * + * @param result 计算结果 + * @param coupon 优惠劵 + * @return 订å•项(商å“)列表 + */ + private List filterMatchCouponOrderItems(TradePriceCalculateRespBO result, + CouponRespDTO coupon) { + Predicate matchPredicate = TradePriceCalculateRespBO.OrderItem::getSelected; + if (PromotionProductScopeEnum.SPU.getScope().equals(coupon.getProductScope())) { + matchPredicate = matchPredicate // é¢å¤–加如下æ¡ä»¶ + .and(orderItem -> coupon.getProductScopeValues().contains(orderItem.getSpuId())); + } else if (PromotionProductScopeEnum.CATEGORY.getScope().equals(coupon.getProductScope())) { + matchPredicate = matchPredicate // é¢å¤–加如下æ¡ä»¶ + .and(orderItem -> coupon.getProductScopeValues().contains(orderItem.getCategoryId())); + } + return filterList(result.getItems(), matchPredicate); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeDeliveryPriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeDeliveryPriceCalculator.java new file mode 100644 index 0000000..89afbfd --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeDeliveryPriceCalculator.java @@ -0,0 +1,240 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.member.api.address.MemberAddressApi; +import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO; +import com.tashow.cloud.trade.dal.dataobject.config.TradeConfigDO; +import com.tashow.cloud.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; +import com.tashow.cloud.trade.service.config.TradeConfigService; +import com.tashow.cloud.trade.service.delivery.DeliveryExpressTemplateService; +import com.tashow.cloud.trade.service.delivery.DeliveryPickUpStoreService; +import com.tashow.cloud.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +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.*; + +/** + * è¿è´¹çš„ {@link TradePriceCalculator} 实现类 + * + * @author jason + */ +@Component +@Order(TradePriceCalculator.ORDER_DELIVERY) +@Slf4j +public class TradeDeliveryPriceCalculator implements TradePriceCalculator { + + @Resource + private MemberAddressApi addressApi; + + @Resource + private DeliveryPickUpStoreService deliveryPickUpStoreService; + @Resource + private DeliveryExpressTemplateService deliveryExpressTemplateService; + @Resource + private TradeConfigService tradeConfigService; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + if (param.getDeliveryType() == null) { + return; + } + // æ ¡éªŒæ˜¯ä¸æ˜¯å­˜åœ¨å•†å“ä¸èƒ½é—¨åº—自æï¼Œæˆ–者ä¸èƒ½å¿«é€’å‘货的情况。就是说,é…逿–¹å¼ä¸åŒ¹é…哈 + if (CollectionUtils.anyMatch(result.getItems(), item -> !item.getDeliveryTypes().contains(param.getDeliveryType()))) { + throw exception(PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL); + } + + if (DeliveryTypeEnum.PICK_UP.getType().equals(param.getDeliveryType())) { + calculateByPickUp(param); + } else if (DeliveryTypeEnum.EXPRESS.getType().equals(param.getDeliveryType())) { + calculateExpress(param, result); + } + } + + private void calculateByPickUp(TradePriceCalculateReqBO param) { + if (param.getPickUpStoreId() == null) { + // 价格计算时,如果为空就ä¸ç®—~最终下å•,会校验该字段ä¸å…许空 + return; + } + DeliveryPickUpStoreDO pickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(param.getPickUpStoreId()); + if (pickUpStore == null || CommonStatusEnum.DISABLE.getStatus().equals(pickUpStore.getStatus())) { + throw exception(PICK_UP_STORE_NOT_EXISTS); + } + } + + // ========= 快递å‘è´§ ========== + + private void calculateExpress(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 0. 得到收件地å€åŒºåŸŸ + if (param.getAddressId() == null) { + // 价格计算时,如果为空就ä¸ç®—~最终下å•,会校验该字段ä¸å…许空 + return; + } + MemberAddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()).getCheckedData(); + Assert.notNull(address, "收件人({})的地å€ï¼Œä¸èƒ½ä¸ºç©º", param.getUserId()); + + // 情况一:全局包邮 + if (isGlobalExpressFree(result)) { + return; + } + + // 情况二:活动包邮 + if (Boolean.TRUE.equals(result.getFreeDelivery())) { + return; + } + + // 情况三:快递模版 + // 2.1 è¿‡æ»¤å‡ºå·²é€‰ä¸­çš„å•†å“ SKU + List selectedItem = filterList(result.getItems(), OrderItem::getSelected); + Set deliveryTemplateIds = convertSet(selectedItem, OrderItem::getDeliveryTemplateId); + Map expressTemplateMap = + deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(deliveryTemplateIds, address.getAreaId()); + // 2.2 计算é…é€è´¹ç”¨ + if (CollUtil.isEmpty(expressTemplateMap)) { + log.error("[calculate][找ä¸åˆ°å•†å“ templateIds {} areaId{} 对应的è¿è´¹æ¨¡æ¿]", deliveryTemplateIds, address.getAreaId()); + throw exception(PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND); + } + calculateDeliveryPrice(selectedItem, expressTemplateMap, result); + } + + /** + * 是å¦å…¨å±€åŒ…é‚® + * + * @param result 计算结果 + * @return 是å¦åŒ…é‚® + */ + private boolean isGlobalExpressFree(TradePriceCalculateRespBO result) { + TradeConfigDO config = tradeConfigService.getTradeConfig(); + // 情况一:交易中心é…ç½®ä¸å­˜åœ¨é»˜è®¤ä¸åŒ…é‚® + if (config == null) { + return false; + } + // 情况二:开å¯äº†å…¨å±€åŒ…é‚® && æ»¡è¶³åŒ…é‚®é‡‘é¢ + return Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) && + result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice(); + } + + private void calculateDeliveryPrice(List selectedSkus, + Map expressTemplateMap, + TradePriceCalculateRespBO result) { + // 按商å“è¿è´¹æ¨¡æ¿æ¥è®¡ç®—商å“çš„è¿è´¹ï¼šç›¸åŒçš„è¿è´¹æ¨¡æ¿å¯èƒ½å¯¹åº”多æ¡è®¢å•å•†å“ SKU + Map> template2ItemMap = convertMultiMap(selectedSkus, OrderItem::getDeliveryTemplateId); + // 便¬¡è®¡ç®—快递è¿è´¹ + for (Map.Entry> entry : template2ItemMap.entrySet()) { + Long templateId = entry.getKey(); + List orderItems = entry.getValue(); + DeliveryExpressTemplateRespBO templateBO = expressTemplateMap.get(templateId); + if (templateBO == null) { + log.error("[calculateDeliveryPrice][ä¸èƒ½è®¡ç®—快递è¿è´¹ï¼Œæ‰¾ä¸åˆ° templateId({}) 对应的è¿è´¹æ¨¡æ¿é…ç½®]", templateId); + continue; + } + // 1. 优先判断是å¦åŒ…邮。如果包邮ä¸è®¡ç®—快递è¿è´¹ + if (isExpressTemplateFree(orderItems, templateBO.getChargeMode(), templateBO.getFree())) { + continue; + } + // 2. 计算快递è¿è´¹ + calculateExpressFeeByChargeMode(orderItems, templateBO.getChargeMode(), templateBO.getCharge()); + } + TradePriceCalculatorHelper.recountAllPrice(result); + } + + /** + * 按é…逿–¹å¼æ¥è®¡ç®—è¿è´¹ + * + * @param orderItems SKU 商å“项目 + * @param chargeMode é…é€è®¡è´¹æ–¹å¼ + * @param templateCharge 快递è¿è´¹é…ç½® + */ + private void calculateExpressFeeByChargeMode(List orderItems, Integer chargeMode, + DeliveryExpressTemplateRespBO.Charge templateCharge) { + if (templateCharge == null) { + log.error("[calculateExpressFeeByChargeMode][计算快递è¿è´¹æ—¶ï¼Œæ‰¾ä¸åˆ° SKU({}) 对应的è¿è´¹æ¨¡ç‰ˆ]", orderItems); + return; + } + double totalChargeValue = getTotalChargeValue(orderItems, chargeMode); + // 1. 计算 SKU 商å“快递费用 + int deliveryPrice; + if (totalChargeValue <= templateCharge.getStartCount()) { + deliveryPrice = templateCharge.getStartPrice(); + } else { + double remainWeight = totalChargeValue - templateCharge.getStartCount(); + // 剩余é‡é‡/ ç»­ä»¶ = 续件的次数. å‘ä¸Šå–æ•´ + int extraNum = (int) Math.ceil(remainWeight / templateCharge.getExtraCount()); + int extraPrice = templateCharge.getExtraPrice() * extraNum; + deliveryPrice = templateCharge.getStartPrice() + extraPrice; + } + + // 2. 分摊快递费用到 SKU. 退费的时候,å¯èƒ½æŒ‰ç…§ SKU è€ƒè™‘é€€è´¹é‡‘é¢ + int remainPrice = deliveryPrice; + for (int i = 0; i < orderItems.size(); i++) { + TradePriceCalculateRespBO.OrderItem item = orderItems.get(i); + int partPrice; + double chargeValue = getChargeValue(item, chargeMode); + if (i < orderItems.size() - 1) { // å‡ä¸€çš„原因,是因为拆分时,如果按照比例,å¯èƒ½ä¼šå‡ºçް.所以最åŽä¸€ä¸ªï¼Œä½¿ç”¨åå‡ + partPrice = (int) (deliveryPrice * (chargeValue / totalChargeValue)); + remainPrice -= partPrice; + } else { + partPrice = remainPrice; + } + Assert.isTrue(partPrice >= 0, "分摊金é¢å¿…须大于等于 0"); + // 更新快递è¿è´¹ + item.setDeliveryPrice(partPrice); + TradePriceCalculatorHelper.recountPayPrice(item); + } + } + + /** + * 检查是å¦åŒ…é‚® + * + * @param chargeMode é…é€è®¡è´¹æ–¹å¼ + * @param templateFree 包邮é…ç½® + */ + private boolean isExpressTemplateFree(List orderItems, Integer chargeMode, + DeliveryExpressTemplateRespBO.Free templateFree) { + if (templateFree == null) { + return false; + } + double totalChargeValue = getTotalChargeValue(orderItems, chargeMode); + double totalPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems); + return totalChargeValue >= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice(); + } + + private double getTotalChargeValue(List orderItems, Integer chargeMode) { + double total = 0; + for (OrderItem orderItem : orderItems) { + total += getChargeValue(orderItem, chargeMode); + } + return total; + } + + private double getChargeValue(OrderItem orderItem, Integer chargeMode) { + DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode); + switch (chargeModeEnum) { + case COUNT: + return orderItem.getCount(); + case WEIGHT: + return orderItem.getWeight() != null ? orderItem.getWeight() * orderItem.getCount() : 0; + case VOLUME: + return orderItem.getVolume() != null ? orderItem.getVolume() * orderItem.getCount() : 0; + default: + throw new IllegalArgumentException(StrUtil.format("未知的计费模å¼({})", chargeMode)); + } + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java new file mode 100644 index 0000000..affc422 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java @@ -0,0 +1,154 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.module.member.api.level.MemberLevelApi; +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi; +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import jakarta.annotation.Resource; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.calculateRatePrice; +import static com.tashow.cloud.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice; + +/** + * 陿—¶æŠ˜æ‰£çš„ {@link TradePriceCalculator} 实现类 + * + * 由于“会员折扣â€å’Œâ€œé™æ—¶æŠ˜æ‰£â€æ˜¯å†²çªï¼Œéœ€è¦é€‰æ‹©ä¼˜æƒ é‡‘é¢å¤šçš„,所以也放在这里计算 + * + * @author èŠ‹é“æºç  + */ +@Component +@Order(TradePriceCalculator.ORDER_DISCOUNT_ACTIVITY) +public class TradeDiscountActivityPriceCalculator implements TradePriceCalculator { + + @Resource + private DiscountActivityApi discountActivityApi; + @Resource + private MemberLevelApi memberLevelApi; + @Resource + private MemberUserApi memberUserApi; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 0. åªæœ‰ã€æ™®é€šã€‘订å•,æ‰è®¡ç®—该优惠 + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) { + return; + } + + // 1.1 获得 SKU å¯¹åº”çš„é™æ—¶æŠ˜æ‰£æ´»åЍ + List discountProducts = discountActivityApi.getMatchDiscountProductListBySkuIds( + convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId)).getCheckedData(); + Map discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId); + // 1.2 获得会员等级 + MemberLevelRespDTO level = getMemberLevel(param.getUserId()); + + // 2. 计算æ¯ä¸ª SKU çš„ä¼˜æƒ é‡‘é¢ + result.getItems().forEach(orderItem -> { + if (!orderItem.getSelected()) { + return; + } + // 2.1 è®¡ç®—é™æ—¶æŠ˜æ‰£çš„ä¼˜æƒ é‡‘é¢ + DiscountProductRespDTO discountProduct = discountProductMap.get(orderItem.getSkuId()); + Integer discountPrice = calculateActivityPrice(discountProduct, orderItem); + // 2.2 计算 VIP ä¼˜æƒ é‡‘é¢ + Integer vipPrice = calculateVipPrice(level, orderItem); + if (discountPrice <= 0 && vipPrice <= 0) { + return; + } + + // 3. 选择优惠金é¢å¤šçš„ + if (discountPrice > vipPrice) { + TradePriceCalculatorHelper.addPromotion(result, orderItem, + discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(), + StrUtil.format("陿—¶æŠ˜æ‰£ï¼šçœ {} å…ƒ", formatPrice(discountPrice)), + discountPrice); + // æ›´æ–° SKU ä¼˜æƒ é‡‘é¢ + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice); + } else { + assert level != null; + TradePriceCalculatorHelper.addPromotion(result, orderItem, + level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(), + String.format("ä¼šå‘˜ç­‰çº§æŠ˜æ‰£ï¼šçœ %s å…ƒ", formatPrice(vipPrice)), + vipPrice); + // æ›´æ–° SKU çš„ä¼˜æƒ é‡‘é¢ + orderItem.setVipPrice(vipPrice); + } + + // 4. 分摊优惠 + TradePriceCalculatorHelper.recountPayPrice(orderItem); + TradePriceCalculatorHelper.recountAllPrice(result); + }); + } + + /** + * 获得用户的等级 + * + * @param userId ç”¨æˆ·ç¼–å· + * @return 用户等级 + */ + public MemberLevelRespDTO getMemberLevel(Long userId) { + MemberUserRespDTO user = memberUserApi.getUser(userId).getCheckedData(); + if (user == null || user.getLevelId() == null || user.getLevelId() <= 0) { + return null; + } + return memberLevelApi.getMemberLevel(user.getLevelId()).getCheckedData(); + } + + /** + * 计算优惠活动的价格 + * + * @param discount 优惠活动 + * @param orderItem 交易项 + * @return 优惠价格 + */ + public Integer calculateActivityPrice(DiscountProductRespDTO discount, + TradePriceCalculateRespBO.OrderItem orderItem) { + if (discount == null) { + return 0; + } + Integer newPrice = orderItem.getPayPrice(); + if (PromotionDiscountTypeEnum.PRICE.getType().equals(discount.getDiscountType())) { // å‡ä»· + newPrice -= discount.getDiscountPrice() * orderItem.getCount(); + } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discount.getDiscountType())) { // 打折 + newPrice = calculateRatePrice(orderItem.getPayPrice(), discount.getDiscountPercent() / 100.0); + } else { + throw new IllegalArgumentException(String.format("优惠活动的商å“(%s) çš„ä¼˜æƒ ç±»åž‹ä¸æ­£ç¡®", discount)); + } + return orderItem.getPayPrice() - newPrice; + } + + /** + * 计算会员 VIP 的优惠价格 + * + * @param level 会员等级 + * @param orderItem 交易项 + * @return 优惠价格 + */ + public Integer calculateVipPrice(MemberLevelRespDTO level, + TradePriceCalculateRespBO.OrderItem orderItem) { + if (level == null + || CommonStatusEnum.isDisable(level.getStatus()) + || level.getDiscountPercent() == null) { + return 0; + } + Integer newPrice = calculateRatePrice(orderItem.getPayPrice(), level.getDiscountPercent().doubleValue()); + return orderItem.getPayPrice() - newPrice; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointActivityPriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointActivityPriceCalculator.java new file mode 100644 index 0000000..7245896 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointActivityPriceCalculator.java @@ -0,0 +1,94 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.promotion.api.point.PointActivityApi; +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.service.order.TradeOrderQueryService; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT; + +/** + * 积分商城的 {@link TradePriceCalculator} 实现类 + * + * @author owen + */ +@Component +@Order(TradePriceCalculator.ORDER_POINT_ACTIVITY) +@Slf4j +public class TradePointActivityPriceCalculator implements TradePriceCalculator { + + @Resource + private PointActivityApi pointActivityApi; + @Resource + private MemberUserApi memberUserApi; + + @Resource + private TradeOrderQueryService tradeOrderQueryService; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 1.1 判断订å•类型是å¦ä¸ºç§¯åˆ†å•†åŸŽæ´»åЍ + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.POINT.getType())) { + return; + } + // 1.2 åˆå§‹åŒ–积分 + MemberUserRespDTO user = memberUserApi.getUser(param.getUserId()).getCheckedData(); + result.setTotalPoint(user.getPoint()).setUsePoint(0); + + // 1.3 æ ¡éªŒç”¨æˆ·ç§¯åˆ†ä½™é¢ + if (user.getPoint() == null || user.getPoint() <= 0) { + return; + } + + Assert.isTrue(param.getItems().size() == 1, "积分商城兑æ¢å•†å“时,åªå…许选择一个商å“"); + // 2. 校验是å¦å¯ä»¥å‚与积分商城活动 + TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0); + PointValidateJoinRespDTO activity = validateJoinPointActivity( + param.getUserId(), param.getPointActivityId(), + orderItem.getSkuId(), orderItem.getCount()); + + // 3.0 积分兑æ¢å‰ç½®æ ¡éªŒ + Assert.isTrue(activity.getPoint() >= 1, "积分商城商å“å…‘æ¢ç§¯åˆ†å¿…须大于 1"); + // 3.1 记录优惠明细 + int usePoint = activity.getPoint() * orderItem.getCount(); + result.setUsePoint(usePoint); + orderItem.setUsePoint(usePoint); + int discountPrice = orderItem.getPayPrice(); // 情况一:å•ä½¿ç”¨ç§¯åˆ†å…‘æ¢ + if (activity.getPrice() != null && activity.getPrice() > 0) { // 情况二:积分 + é‡‘é¢ + discountPrice = orderItem.getPayPrice() - activity.getPrice() * orderItem.getCount(); + } + TradePriceCalculatorHelper.addPromotion(result, orderItem, + param.getPointActivityId(), "积分商城活动", PromotionTypeEnum.POINT.getType(), + StrUtil.format("ç§¯åˆ†å•†åŸŽæ´»åŠ¨ï¼šçœ {} å…ƒ", TradePriceCalculatorHelper.formatPrice(discountPrice)), + discountPrice); + // 3.2 æ›´æ–° SKU ä¼˜æƒ é‡‘é¢ + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice); + TradePriceCalculatorHelper.recountPayPrice(orderItem); + TradePriceCalculatorHelper.recountAllPrice(result); + } + + private PointValidateJoinRespDTO validateJoinPointActivity(Long userId, Long activityId, Long skuId, Integer count) { + // 1. 校验是å¦å¯ä»¥å‚与积分商城活动 + PointValidateJoinRespDTO pointValidateJoinRespDTO = pointActivityApi.validateJoinPointActivity(activityId, skuId, count).getCheckedData(); + // 2. 校验总é™è´­æ•°é‡ï¼Œç›®å‰åªæœ‰ trade 有具体下å•的数æ®ï¼Œéœ€è¦äº¤ç»™ trade 价格计算使用 + int pointProductCount = tradeOrderQueryService.getActivityProductCount(userId, activityId, TradeOrderTypeEnum.POINT); + if (pointProductCount + count > pointValidateJoinRespDTO.getCount()) { + throw exception(PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT); + } + return pointValidateJoinRespDTO; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointGiveCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointGiveCalculator.java new file mode 100644 index 0000000..8c82296 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointGiveCalculator.java @@ -0,0 +1,63 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.util.BooleanUtil; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.module.member.api.config.MemberConfigApi; +import cn.iocoder.yudao.module.member.api.config.dto.MemberConfigRespDTO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Optional; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; + +/** + * èµ é€ç§¯åˆ†çš„ {@link TradePriceCalculator} 实现类 + * + * @author owen + */ +@Component +@Order(TradePriceCalculator.ORDER_POINT_GIVE) +@Slf4j +public class TradePointGiveCalculator implements TradePriceCalculator { + + @Resource + private MemberConfigApi memberConfigApi; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 1.1 校验积分功能是å¦å¼€å¯ + int givePointPerYuan = Optional.ofNullable(memberConfigApi.getConfig().getCheckedData()) + .filter(config -> BooleanUtil.isTrue(config.getPointTradeDeductEnable())) + .map(MemberConfigRespDTO::getPointTradeGivePoint) + .orElse(0); + if (givePointPerYuan <= 0) { + return; + } + // 1.2 æ ¡éªŒæ”¯ä»˜é‡‘é¢ + if (result.getPrice().getPayPrice() <= 0) { + return; + } + + // 2.1 计算赠é€ç§¯åˆ† + int givePoint = MoneyUtils.calculateRatePriceFloor(result.getPrice().getPayPrice(), (double) givePointPerYuan); + // 2.2 计算分摊的赠é€ç§¯åˆ† + List orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected); + List dividePoints = TradePriceCalculatorHelper.dividePrice(orderItems, givePoint); + + // 3.2 æ›´æ–° SKU èµ é€ç§¯åˆ† + for (int i = 0; i < orderItems.size(); i++) { + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); + // 商å“å¯èƒ½èµ é€äº†ç§¯åˆ†ï¼Œæ‰€ä»¥è¿™é‡Œè¦åŠ ä¸Š + orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i)); + } + // 3.3 更新订å•èµ é€ç§¯åˆ† + TradePriceCalculatorHelper.recountAllGivePoint(result); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointUsePriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointUsePriceCalculator.java new file mode 100644 index 0000000..ea63495 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePointUsePriceCalculator.java @@ -0,0 +1,117 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.member.api.config.MemberConfigApi; +import cn.iocoder.yudao.module.member.api.config.dto.MemberConfigRespDTO; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL; + +/** + * 使用积分的 {@link TradePriceCalculator} 实现类 + * + * @author owen + */ +@Component +@Order(TradePriceCalculator.ORDER_POINT_USE) +@Slf4j +public class TradePointUsePriceCalculator implements TradePriceCalculator { + + @Resource + private MemberConfigApi memberConfigApi; + @Resource + private MemberUserApi memberUserApi; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 判断订å•类型是å¦ä¸ä¸ºç§¯åˆ†å•†åŸŽæ´»åЍ + if (ObjectUtil.equal(result.getType(), TradeOrderTypeEnum.POINT.getType())) { + return; + } + // 0. åˆå§‹åŒ–积分 + MemberUserRespDTO user = memberUserApi.getUser(param.getUserId()).getCheckedData(); + result.setTotalPoint(user.getPoint()).setUsePoint(0); + + // 1.1 校验是å¦ä½¿ç”¨ç§¯åˆ† + if (!BooleanUtil.isTrue(param.getPointStatus())) { + return; + } + // 1.2 校验积分抵扣是å¦å¼€å¯ + MemberConfigRespDTO config = memberConfigApi.getConfig().getCheckedData(); + if (!isDeductPointEnable(config)) { + return; + } + // 1.3 æ ¡éªŒç”¨æˆ·ç§¯åˆ†ä½™é¢ + if (user.getPoint() == null || user.getPoint() <= 0) { + return; + } + + // 2.1 è®¡ç®—ç§¯åˆ†ä¼˜æƒ é‡‘é¢ + int pointPrice = calculatePointPrice(config, user.getPoint(), result); + // 2.2 è®¡ç®—åˆ†æ‘Šçš„ç§¯åˆ†ã€æŠµæ‰£é‡‘é¢ + List orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected); + List dividePointPrices = TradePriceCalculatorHelper.dividePrice(orderItems, pointPrice); + List divideUsePoints = TradePriceCalculatorHelper.dividePrice(orderItems, result.getUsePoint()); + + // 3.1 记录优惠明细 + TradePriceCalculatorHelper.addPromotion(result, orderItems, + param.getUserId(), "积分抵扣", PromotionTypeEnum.POINT.getType(), + StrUtil.format("ç§¯åˆ†æŠµæ‰£ï¼šçœ {} å…ƒ", TradePriceCalculatorHelper.formatPrice(pointPrice)), + dividePointPrices); + // 3.2 æ›´æ–° SKU ä¼˜æƒ é‡‘é¢ + for (int i = 0; i < orderItems.size(); i++) { + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); + orderItem.setPointPrice(dividePointPrices.get(i)); + orderItem.setUsePoint(divideUsePoints.get(i)); + TradePriceCalculatorHelper.recountPayPrice(orderItem); + } + TradePriceCalculatorHelper.recountAllPrice(result); + } + + private boolean isDeductPointEnable(MemberConfigRespDTO config) { + return config != null && + BooleanUtil.isTrue(config.getPointTradeDeductEnable()) && // 积分功能是å¦å¯ç”¨ + config.getPointTradeDeductUnitPrice() != null && config.getPointTradeDeductUnitPrice() > 0; // 有没有é…置:1 积分抵扣多少分 + } + + private Integer calculatePointPrice(MemberConfigRespDTO config, Integer usePoint, TradePriceCalculateRespBO result) { + // æ¯ä¸ªè®¢å•最多å¯ä»¥ä½¿ç”¨çš„ç§¯åˆ†æ•°é‡ + if (config.getPointTradeDeductMaxPrice() != null && config.getPointTradeDeductMaxPrice() > 0) { + usePoint = Math.min(usePoint, config.getPointTradeDeductMaxPrice()); + } + // TODO @疯狂:这里应该是,抵扣到åªå‰©ä¸‹ 0.01ï¼› + // 积分优惠金é¢ï¼ˆåˆ†ï¼‰ + int pointPrice = usePoint * config.getPointTradeDeductUnitPrice(); + if (result.getPrice().getPayPrice() <= pointPrice) { + // ç¦æ­¢ 0 元购 + throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL); + } +// // å…许0 元购!!!:用户积分比较多时,积分å¯ä»¥æŠµæ‰£çš„金é¢è¦å¤§äºŽæ”¯ä»˜é‡‘é¢ï¼Œè¿™æ—¶éœ€è¦æ ¹æ®æ”¯ä»˜é‡‘é¢å推使用多少积分 +// if (result.getPrice().getPayPrice() < pointPrice) { +// pointPrice = result.getPrice().getPayPrice(); +// // åæŽ¨éœ€è¦æ‰£é™¤çš„积分 +// usePoint = NumberUtil.toBigDecimal(pointPrice) +// .divide(NumberUtil.toBigDecimal(config.getPointTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP) +// .intValue(); +// } + // 记录使用的积分 + result.setUsePoint(usePoint); + return pointPrice; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePriceCalculator.java new file mode 100644 index 0000000..e2bfc7d --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePriceCalculator.java @@ -0,0 +1,40 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; + +/** + * ä»·æ ¼è®¡ç®—çš„è®¡ç®—å™¨æŽ¥å£ + * + * 优惠计算顺åºï¼š + * 1. 积分抵现ã€ä¼šå‘˜ä»·ã€ä¼˜æƒ åˆ¸ã€ç²‰ä¸ä¸“äº«ä»·ã€æ»¡å‡é€å“ªä¸ªä¼˜å…ˆè®¡ç®—? + * + * @author èŠ‹é“æºç  + */ +public interface TradePriceCalculator { + + int ORDER_SECKILL_ACTIVITY = 8; + int ORDER_BARGAIN_ACTIVITY = 8; + int ORDER_COMBINATION_ACTIVITY = 8; + int ORDER_POINT_ACTIVITY = 8; + + int ORDER_DISCOUNT_ACTIVITY = 10; + int ORDER_REWARD_ACTIVITY = 20; + int ORDER_COUPON = 30; + int ORDER_POINT_USE = 40; + /** + * 快递è¿è´¹çš„计算 + * + * 放在å„ç§è¥é”€æ´»åЍã€ä¼˜æƒ åеåŽé¢ + */ + int ORDER_DELIVERY = 50; + /** + * èµ é€ç§¯åˆ†ï¼Œæ”¾æœ€åŽ + * + * 放在 {@link #ORDER_DELIVERY} åŽé¢çš„原因,是è¿è´¹ä¹Ÿä¼šäº§ç”Ÿè´¹ç”¨ï¼Œéœ€è¦èµ é€å¯¹åº”积分 + */ + int ORDER_POINT_GIVE = 999; + + void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result); + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePriceCalculatorHelper.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePriceCalculatorHelper.java new file mode 100644 index 0000000..10c5a2b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradePriceCalculatorHelper.java @@ -0,0 +1,345 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import com.tashow.cloud.trade.dal.dataobject.order.TradeOrderItemDO; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; +import static java.util.Collections.singletonList; + +/** + * {@link TradePriceCalculator} 的工具类 + * + * 主è¦å®žçް坹 {@link TradePriceCalculateRespBO} 计算结果的æ“作 + * + * @author èŠ‹é“æºç  + */ +public class TradePriceCalculatorHelper { + + public static TradePriceCalculateRespBO buildCalculateResp(TradePriceCalculateReqBO param, + List spuList, List skuList) { + // 创建 PriceCalculateRespDTO 对象 + TradePriceCalculateRespBO result = new TradePriceCalculateRespBO(); + result.setType(getOrderType(param)).setPromotions(new ArrayList<>()).setGiveCouponTemplateCounts(new LinkedHashMap<>()); + + // 创建它的 OrderItem 属性 + result.setItems(new ArrayList<>(param.getItems().size())); + Map spuMap = convertMap(spuList, ProductSpuRespDTO::getId); + Map skuMap = convertMap(skuList, ProductSkuRespDTO::getId); + param.getItems().forEach(item -> { + ProductSkuRespDTO sku = skuMap.get(item.getSkuId()); + if (sku == null) { + return; + } + ProductSpuRespDTO spu = spuMap.get(sku.getSpuId()); + if (spu == null) { + return; + } + // 商å“项 + TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem(); + result.getItems().add(orderItem); + orderItem.setSpuId(sku.getSpuId()).setSkuId(sku.getId()) + .setCount(item.getCount()).setCartId(item.getCartId()).setSelected(item.getSelected()); + // sku ä»·æ ¼ + orderItem.setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * item.getCount()) + .setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0).setVipPrice(0); + // sku ä¿¡æ¯ + orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties()) + .setWeight(sku.getWeight()).setVolume(sku.getVolume()); + // spu ä¿¡æ¯ + orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId()) + .setDeliveryTypes(spu.getDeliveryTypes()).setDeliveryTemplateId(spu.getDeliveryTemplateId()) + .setGivePoint(spu.getGiveIntegral()).setUsePoint(0); + if (StrUtil.isBlank(orderItem.getPicUrl())) { + orderItem.setPicUrl(spu.getPicUrl()); + } + }); + + // 创建它的 Price 属性 + result.setPrice(new TradePriceCalculateRespBO.Price()); + recountAllPrice(result); + recountAllGivePoint(result); + return result; + } + + /** + * 计算订å•类型 + * + * @param param è®¡ç®—å‚æ•° + * @return 订å•类型 + */ + private static Integer getOrderType(TradePriceCalculateReqBO param) { + if (param.getSeckillActivityId() != null) { + return TradeOrderTypeEnum.SECKILL.getType(); + } + if (param.getCombinationActivityId() != null) { + return TradeOrderTypeEnum.COMBINATION.getType(); + } + if (param.getBargainRecordId() != null) { + return TradeOrderTypeEnum.BARGAIN.getType(); + } + if (param.getPointActivityId() != null) { + return TradeOrderTypeEnum.POINT.getType(); + } + return TradeOrderTypeEnum.NORMAL.getType(); + } + + /** + * 基于订å•é¡¹ï¼Œé‡æ–°è®¡ç®— price 总价 + * + * @param result 计算结果 + */ + public static void recountAllPrice(TradePriceCalculateRespBO result) { + // å…ˆé‡ç½® + TradePriceCalculateRespBO.Price price = result.getPrice(); + price.setTotalPrice(0).setDiscountPrice(0).setDeliveryPrice(0) + .setCouponPrice(0).setPointPrice(0).setVipPrice(0).setPayPrice(0); + // å†åˆè®¡ item + result.getItems().forEach(item -> { + if (!item.getSelected()) { + return; + } + price.setTotalPrice(price.getTotalPrice() + item.getPrice() * item.getCount()); + price.setDiscountPrice(price.getDiscountPrice() + item.getDiscountPrice()); + price.setDeliveryPrice(price.getDeliveryPrice() + item.getDeliveryPrice()); + price.setCouponPrice(price.getCouponPrice() + item.getCouponPrice()); + price.setPointPrice(price.getPointPrice() + item.getPointPrice()); + price.setVipPrice(price.getVipPrice() + item.getVipPrice()); + price.setPayPrice(price.getPayPrice() + item.getPayPrice()); + }); + } + + /** + * 基于订å•é¡¹ï¼Œé‡æ–°è®¡ç®—èµ é€ç§¯åˆ† + * + * @param result 计算结果 + */ + public static void recountAllGivePoint(TradePriceCalculateRespBO result) { + result.setGivePoint(getSumValue(result.getItems(), item -> item.getSelected() ? item.getGivePoint() : 0, Integer::sum)); + } + + /** + * 釿–°è®¡ç®—å•个订å•é¡¹çš„æ”¯ä»˜é‡‘é¢ + * + * @param orderItem 订å•项 + */ + public static void recountPayPrice(TradePriceCalculateRespBO.OrderItem orderItem) { + orderItem.setPayPrice(orderItem.getPrice() * orderItem.getCount() + - orderItem.getDiscountPrice() + + orderItem.getDeliveryPrice() + - orderItem.getCouponPrice() + - orderItem.getPointPrice() + - orderItem.getVipPrice() + ); + } + + /** + * 釿–°è®¡ç®—æ¯ä¸ªè®¢å•é¡¹çš„æ”¯ä»˜é‡‘é¢ + * + * ã€ç›®å‰ä¸»è¦æ˜¯å•测使用】 + * + * @param orderItems 订å•项数组 + */ + public static void recountPayPrice(List orderItems) { + orderItems.forEach(orderItem -> { + if (orderItem.getDiscountPrice() == null) { + orderItem.setDiscountPrice(0); + } + if (orderItem.getDeliveryPrice() == null) { + orderItem.setDeliveryPrice(0); + } + if (orderItem.getCouponPrice() == null) { + orderItem.setCouponPrice(0); + } + if (orderItem.getPointPrice() == null) { + orderItem.setPointPrice(0); + } + if (orderItem.getUsePoint() == null) { + orderItem.setUsePoint(0); + } + if (orderItem.getGivePoint() == null) { + orderItem.setGivePoint(0); + } + if (orderItem.getVipPrice() == null) { + orderItem.setVipPrice(0); + } + recountPayPrice(orderItem); + }); + } + + /** + * 计算已选中的订å•é¡¹ï¼Œæ€»æ”¯ä»˜é‡‘é¢ + * + * @param orderItems 订å•项数组 + * @return æ€»æ”¯ä»˜é‡‘é¢ + */ + public static Integer calculateTotalPayPrice(List orderItems) { + return getSumValue(orderItems, + orderItem -> orderItem.getSelected() ? orderItem.getPayPrice() : 0, // 未选中的情况下,ä¸è®¡ç®—æ”¯ä»˜é‡‘é¢ + Integer::sum); + } + + /** + * 计算已选中的订å•é¡¹ï¼Œæ€»å•†å“æ•° + * + * @param orderItems 订å•项数组 + * @return æ€»å•†å“æ•° + */ + public static Integer calculateTotalCount(List orderItems) { + return getSumValue(orderItems, + orderItem -> orderItem.getSelected() ? orderItem.getCount() : 0, // 未选中的情况下,ä¸è®¡ç®—æ•°é‡ + Integer::sum); + } + + /** + * 按照支付金é¢ï¼Œè¿”回æ¯ä¸ªè®¢å•é¡¹çš„åˆ†æ‘Šé‡‘é¢æ•°ç»„ + * + * 实际上 price ä¸ä»…ä»…å¯ä»¥ä¼ é€’的是金é¢ï¼Œä¹Ÿå¯ä»¥æ˜¯ç§¯åˆ†ã€‚å› ä¸ºå®ƒçš„å®žçŽ°é€»è¾‘ï¼Œå°±æ˜¯æ ¹æ® payPrice åšåˆ†æ‘Šè€Œå·² + * + * @param orderItems 订å•项数组 + * @param price é‡‘é¢ + * @return åˆ†æ‘Šé‡‘é¢æ•°ç»„,和传入的 orderItems 一一对应 + */ + public static List dividePrice(List orderItems, Integer price) { + Integer total = calculateTotalPayPrice(orderItems); + assert total != null; + // é历æ¯ä¸€ä¸ªï¼Œè¿›è¡Œåˆ†æ‘Š + List prices = new ArrayList<>(orderItems.size()); + int remainPrice = price; + for (int i = 0; i < orderItems.size(); i++) { + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); + // 1. 如果是未选中,则分摊为 0 + if (!orderItem.getSelected()) { + prices.add(0); + continue; + } + // 2. 如果选中,则按照百分比,进行分摊 + int partPrice; + if (i < orderItems.size() - 1) { // å‡ä¸€çš„原因,是因为拆分时,如果按照比例,å¯èƒ½ä¼šå‡ºçް.所以最åŽä¸€ä¸ªï¼Œä½¿ç”¨åå‡ + partPrice = (int) (price * (1.0D * orderItem.getPayPrice() / total)); + remainPrice -= partPrice; + } else { + partPrice = remainPrice; + } + Assert.isTrue(partPrice >= 0, "分摊金é¢å¿…须大于等于 0"); + prices.add(partPrice); + } + return prices; + } + + /** + * 计算订å•调价价格分摊 + * + * å’Œ {@link #dividePrice(List, Integer)} é€»è¾‘ä¸€è‡´ï¼Œåªæ˜¯ä¼ å…¥çš„æ˜¯ TradeOrderItemDO 对象 + * + * @param items 订å•项 + * @param price è®¢å•æ”¯ä»˜é‡‘é¢ + * @return åˆ†æ‘Šé‡‘é¢æ•°ç»„,和传入的 orderItems 一一对应 + */ + public static List dividePrice2(List items, Integer price) { + Integer total = getSumValue(items, TradeOrderItemDO::getPayPrice, Integer::sum); + assert total != null; + // é历æ¯ä¸€ä¸ªï¼Œè¿›è¡Œåˆ†æ‘Š + List prices = new ArrayList<>(items.size()); + int remainPrice = price; + for (int i = 0; i < items.size(); i++) { + TradeOrderItemDO orderItem = items.get(i); + int partPrice; + if (i < items.size() - 1) { // å‡ä¸€çš„原因,是因为拆分时,如果按照比例,å¯èƒ½ä¼šå‡ºçް.所以最åŽä¸€ä¸ªï¼Œä½¿ç”¨åå‡ + partPrice = (int) (price * (1.0D * orderItem.getPrice() / total)); + remainPrice -= partPrice; + } else { + partPrice = remainPrice; + } + prices.add(partPrice); + } + return prices; + } + + /** + * 添加ã€åŒ¹é…】å•个 OrderItem çš„è¥é”€æ˜Žç»† + * + * @param result 价格计算结果 + * @param orderItem å•个订å•å•†å“ SKU + * @param id è¥é”€ç¼–å· + * @param name è¥é”€åå­— + * @param description 满足æ¡ä»¶çš„æç¤º + * @param type è¥é”€ç±»åž‹ + * @param discountPrice å•个订å•å•†å“ SKU 的优惠价格(总) + */ + public static void addPromotion(TradePriceCalculateRespBO result, TradePriceCalculateRespBO.OrderItem orderItem, + Long id, String name, Integer type, String description, Integer discountPrice) { + addPromotion(result, singletonList(orderItem), id, name, type, description, singletonList(discountPrice)); + } + + /** + * 添加ã€åŒ¹é…】多个 OrderItem çš„è¥é”€æ˜Žç»† + * + * @param result 价格计算结果 + * @param orderItems 多个订å•å•†å“ SKU + * @param id è¥é”€ç¼–å· + * @param name è¥é”€åå­— + * @param description 满足æ¡ä»¶çš„æç¤º + * @param type è¥é”€ç±»åž‹ + * @param discountPrices 多个订å•å•†å“ SKU 的优惠价格(总),和 orderItems 一一对应 + */ + public static void addPromotion(TradePriceCalculateRespBO result, List orderItems, + Long id, String name, Integer type, String description, List discountPrices) { + // 创建è¥é”€æ˜Žç»† Item + List promotionItems = new ArrayList<>(discountPrices.size()); + for (int i = 0; i < orderItems.size(); i++) { + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); + promotionItems.add(new TradePriceCalculateRespBO.PromotionItem().setSkuId(orderItem.getSkuId()) + .setTotalPrice(orderItem.getPayPrice()).setDiscountPrice(discountPrices.get(i))); + } + // 创建è¥é”€æ˜Žç»† + TradePriceCalculateRespBO.Promotion promotion = new TradePriceCalculateRespBO.Promotion() + .setId(id).setName(name).setType(type) + .setTotalPrice(calculateTotalPayPrice(orderItems)) + .setDiscountPrice(getSumValue(discountPrices, value -> value, Integer::sum)) + .setItems(promotionItems).setMatch(true).setDescription(description); + result.getPromotions().add(promotion); + } + + /** + * 添加ã€ä¸åŒ¹é…】多个 OrderItem çš„è¥é”€æ˜Žç»† + * + * @param result 价格计算结果 + * @param orderItems 多个订å•å•†å“ SKU + * @param id è¥é”€ç¼–å· + * @param name è¥é”€åå­— + * @param description 满足æ¡ä»¶çš„æç¤º + * @param type è¥é”€ç±»åž‹ + */ + public static void addNotMatchPromotion(TradePriceCalculateRespBO result, List orderItems, + Long id, String name, Integer type, String description) { + // 创建è¥é”€æ˜Žç»† Item + List promotionItems = CollectionUtils.convertList(orderItems, + orderItem -> new TradePriceCalculateRespBO.PromotionItem().setSkuId(orderItem.getSkuId()) + .setTotalPrice(orderItem.getPayPrice()).setDiscountPrice(0)); + // 创建è¥é”€æ˜Žç»† + TradePriceCalculateRespBO.Promotion promotion = new TradePriceCalculateRespBO.Promotion() + .setId(id).setName(name).setType(type) + .setTotalPrice(calculateTotalPayPrice(orderItems)) + .setDiscountPrice(0) + .setItems(promotionItems).setMatch(false).setDescription(description); + result.getPromotions().add(promotion); + } + + public static String formatPrice(Integer price) { + return String.format("%.2f", price / 100d); + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java new file mode 100644 index 0000000..595710a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java @@ -0,0 +1,160 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi; +import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import jakarta.annotation.Resource; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static com.tashow.cloud.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice; + +// TODO @puhui999ï¼šç›¸å…³çš„å•æµ‹ï¼Œå»ºè®®æ”¹ä¸€æ”¹ + +/** + * 满å‡é€æ´»åŠ¨çš„ {@link TradePriceCalculator} 实现类 + * + * @author èŠ‹é“æºç  + */ +@Component +@Order(TradePriceCalculator.ORDER_REWARD_ACTIVITY) +public class TradeRewardActivityPriceCalculator implements TradePriceCalculator { + + @Resource + private RewardActivityApi rewardActivityApi; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 0. åªæœ‰ã€æ™®é€šã€‘订å•,æ‰è®¡ç®—该优惠 + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) { + return; + } + // 获得 SKU 对应的满å‡é€æ´»åЍ + List rewardActivities = rewardActivityApi.getMatchRewardActivityListBySpuIds( + convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSpuId)).getCheckedData(); + if (CollUtil.isEmpty(rewardActivities)) { + return; + } + // å¤„ç†æœ€æ–°çš„æ»¡å‡é€æ´»åЍ + if (!rewardActivities.isEmpty()) { + calculate(param, result, rewardActivities.get(0)); + } + } + + private void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result, + RewardActivityMatchRespDTO rewardActivity) { + // 1.1 获得满å‡é€çš„订å•项(商å“)列表 + List orderItems = filterMatchActivityOrderItems(result, rewardActivity); + if (CollUtil.isEmpty(orderItems)) { + return; + } + // 1.2 获得最大匹é…的满å‡é€æ´»åŠ¨çš„è§„åˆ™ + RewardActivityMatchRespDTO.Rule rule = getMaxMatchRewardActivityRule(rewardActivity, orderItems); + if (rule == null) { + TradePriceCalculatorHelper.addNotMatchPromotion(result, orderItems, + rewardActivity.getId(), rewardActivity.getName(), PromotionTypeEnum.REWARD_ACTIVITY.getType(), + "满å‡é€ï¼š" + rewardActivity.getRules().get(0).getDescription()); + return; + } + + // 2.1 计算å¯ä»¥ä¼˜æƒ çš„é‡‘é¢ + Integer newDiscountPrice = rule.getDiscountPrice(); + // 2.2 è®¡ç®—åˆ†æ‘Šçš„ä¼˜æƒ é‡‘é¢ + List divideDiscountPrices = TradePriceCalculatorHelper.dividePrice(orderItems, newDiscountPrice); + // 2.3 计算是å¦åŒ…é‚® + if (Boolean.TRUE.equals(rule.getFreeDelivery())) { + result.setFreeDelivery(true); + } + + // 3.1 记录使用的优惠劵 + result.setCouponId(param.getCouponId()); + // 3.2 记录优惠明细 + TradePriceCalculatorHelper.addPromotion(result, orderItems, + rewardActivity.getId(), rewardActivity.getName(), PromotionTypeEnum.REWARD_ACTIVITY.getType(), + StrUtil.format("满å‡é€ï¼šçœ {} å…ƒ", formatPrice(rule.getDiscountPrice())), + divideDiscountPrices); + // 3.3 æ›´æ–° SKU ä¼˜æƒ é‡‘é¢ + for (int i = 0; i < orderItems.size(); i++) { + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + divideDiscountPrices.get(i)); + TradePriceCalculatorHelper.recountPayPrice(orderItem); + } + TradePriceCalculatorHelper.recountAllPrice(result); + + // 4.1 记录赠é€çš„积分 + if (rule.getPoint() != null && rule.getPoint() > 0) { + List dividePoints = TradePriceCalculatorHelper.dividePrice(orderItems, rule.getPoint()); + for (int i = 0; i < orderItems.size(); i++) { + // 商å“å¯èƒ½èµ é€äº†ç§¯åˆ†ï¼Œæ‰€ä»¥è¿™é‡Œè¦åŠ ä¸Š + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); + orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i)); + } + } + // 4.2 è®°å½•è®¢å•æ˜¯å¦åŒ…é‚® + if (Boolean.TRUE.equals(rule.getFreeDelivery())) { + // åªè¦æ»¡è¶³ä¸€ä¸ªæ´»åŠ¨åŒ…é‚®é‚£ä¹ˆè¿™å•就包邮 + result.setFreeDelivery(true); + } + // 4.3 记录赠é€çš„优惠券 + if (CollUtil.isNotEmpty(rule.getGiveCouponTemplateCounts())) { + for (Map.Entry entry : rule.getGiveCouponTemplateCounts().entrySet()) { + result.getGiveCouponTemplateCounts().put(entry.getKey(), + result.getGiveCouponTemplateCounts().getOrDefault(entry.getKey(), 0) + entry.getValue()); + } + } + } + + /** + * 获得满å‡é€çš„订å•项(商å“)列表 + * + * @param result 计算结果 + * @param rewardActivity 满å‡é€æ´»åЍ + * @return 订å•项(商å“)列表 + */ + private List filterMatchActivityOrderItems(TradePriceCalculateRespBO result, + RewardActivityMatchRespDTO rewardActivity) { + return filterList(result.getItems(), orderItem -> CollUtil.contains(rewardActivity.getSpuIds(), orderItem.getSpuId())); + } + + /** + * 获得最大匹é…的满å‡é€æ´»åŠ¨çš„è§„åˆ™ + * + * @param rewardActivity 满å‡é€æ´»åЍ + * @param orderItems 商å“项 + * @return 匹é…的活动规则 + */ + private RewardActivityMatchRespDTO.Rule getMaxMatchRewardActivityRule(RewardActivityMatchRespDTO rewardActivity, + List orderItems) { + // 1. 计算数é‡å’Œä»·æ ¼ + Integer count = TradePriceCalculatorHelper.calculateTotalCount(orderItems); + Integer price = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems); + assert count != null && price != null; + + // 2. å€’åºæ‰¾ä¸€ä¸ªæœ€å¤§ä¼˜æƒ çš„规则 + for (int i = rewardActivity.getRules().size() - 1; i >= 0; i--) { + RewardActivityMatchRespDTO.Rule rule = rewardActivity.getRules().get(i); + if (PromotionConditionTypeEnum.PRICE.getType().equals(rewardActivity.getConditionType()) + && price >= rule.getLimit()) { + return rule; + } + if (PromotionConditionTypeEnum.COUNT.getType().equals(rewardActivity.getConditionType()) + && count >= rule.getLimit()) { + return rule; + } + } + return null; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java new file mode 100644 index 0000000..cc02e34 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/java/com/tashow/cloud/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java @@ -0,0 +1,72 @@ +package com.tashow.cloud.trade.service.price.calculator; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; +import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; +import com.tashow.cloud.trade.service.order.TradeOrderQueryService; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateReqBO; +import com.tashow.cloud.trade.service.price.bo.TradePriceCalculateRespBO; +import jakarta.annotation.Resource; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT; + +// TODO huihuiï¼šå•æµ‹éœ€è¦è¡¥å…… + +/** + * ç§’æ€æ´»åŠ¨çš„ {@link TradePriceCalculator} 实现类 + * + * @author HUIHUI + */ +@Component +@Order(TradePriceCalculator.ORDER_SECKILL_ACTIVITY) +public class TradeSeckillActivityPriceCalculator implements TradePriceCalculator { + + @Resource + private SeckillActivityApi seckillActivityApi; + + @Resource + private TradeOrderQueryService tradeOrderQueryService; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 1. 判断订å•类型和是å¦å…·æœ‰ç§’æ€æ´»åŠ¨ç¼–å· + if (param.getSeckillActivityId() == null) { + return; + } + Assert.isTrue(param.getItems().size() == 1, "ç§’æ€æ—¶ï¼Œåªå…许选择一个商å“"); + // 2. 校验是å¦å¯ä»¥å‚ä¸Žç§’æ€ + TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0); + SeckillValidateJoinRespDTO seckillActivity = validateJoinSeckill( + param.getUserId(), param.getSeckillActivityId(), + orderItem.getSkuId(), orderItem.getCount()); + + // 3.1 记录优惠明细 + Integer discountPrice = orderItem.getPayPrice() - seckillActivity.getSeckillPrice() * orderItem.getCount(); + TradePriceCalculatorHelper.addPromotion(result, orderItem, + param.getSeckillActivityId(), seckillActivity.getName(), PromotionTypeEnum.SECKILL_ACTIVITY.getType(), + StrUtil.format("ç§’æ€æ´»åŠ¨ï¼šçœ {} å…ƒ", TradePriceCalculatorHelper.formatPrice(discountPrice)), + discountPrice); + // 3.2 æ›´æ–° SKU ä¼˜æƒ é‡‘é¢ + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice); + TradePriceCalculatorHelper.recountPayPrice(orderItem); + TradePriceCalculatorHelper.recountAllPrice(result); + } + + private SeckillValidateJoinRespDTO validateJoinSeckill(Long userId, Long activityId, Long skuId, Integer count) { + // 1. 校验是å¦å¯ä»¥å‚ä¸Žç§’æ€ + SeckillValidateJoinRespDTO seckillActivity = seckillActivityApi.validateJoinSeckill(activityId, skuId, count).getCheckedData(); + // 2. 校验总é™è´­æ•°é‡ï¼Œç›®å‰åªæœ‰ trade 有具体下å•的数æ®ï¼Œéœ€è¦äº¤ç»™ trade 价格计算使用 + int seckillProductCount = tradeOrderQueryService.getActivityProductCount(userId, activityId, TradeOrderTypeEnum.SECKILL); + if (seckillProductCount + count > seckillActivity.getTotalLimitCount()) { + throw exception(PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT); + } + return seckillActivity; + } + +} diff --git a/tashow-module/tashow-module-trade/src/main/resources/application-dev.yaml b/tashow-module/tashow-module-trade/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..afa7754 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/resources/application-dev.yaml @@ -0,0 +1,116 @@ +--- #################### 注册中心 + é…置中心相关é…ç½® #################### + +spring: + cloud: + nacos: + server-addr: 127.0.0.1:8848 # Nacos æœåŠ¡å™¨åœ°å€ + username: # Nacos è´¦å· + password: # Nacos å¯†ç  + discovery: # ã€é…置中心】é…置项 + namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # æœåŠ¡å®žä¾‹çš„ç‰ˆæœ¬å·ï¼Œå¯ç”¨äºŽç°åº¦å‘布 + config: # ã€æ³¨å†Œä¸­å¿ƒã€‘é…置项 + namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP + +--- #################### æ•°æ®åº“相关é…ç½® #################### +spring: + # æ•°æ®æºé…置项 + autoconfigure: + exclude: + datasource: + druid: # Druid ã€ç›‘控】相关的全局é…ç½® + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白åå•,ä¸å¡«åˆ™å…许所有访问 + url-pattern: /druid/* + login-username: # 控制å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç  + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # æ…¢ SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # å¤šæ•°æ®æºé…ç½® + druid: # Druid ã€è¿žæŽ¥æ± ã€‘相关的全局é…ç½® + initial-size: 5 # åˆå§‹è¿žæŽ¥æ•° + min-idle: 10 # 最å°è¿žæŽ¥æ± æ•°é‡ + max-active: 20 # æœ€å¤§è¿žæŽ¥æ± æ•°é‡ + max-wait: 600000 # é…置获å–连接等待超时的时间,å•ä½ï¼šæ¯«ç§’ + time-between-eviction-runs-millis: 60000 # é…置间隔多久æ‰è¿›è¡Œä¸€æ¬¡æ£€æµ‹ï¼Œæ£€æµ‹éœ€è¦å…³é—­çš„空闲连接,å•ä½ï¼šæ¯«ç§’ + min-evictable-idle-time-millis: 300000 # é…置一个连接在池中最å°ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•ä½ï¼šæ¯«ç§’ + max-evictable-idle-time-millis: 900000 # é…置一个连接在池中最大生存的时间,å•ä½ï¼šæ¯«ç§’ + validation-query: SELECT 1 FROM DUAL # é…ç½®æ£€æµ‹è¿žæŽ¥æ˜¯å¦æœ‰æ•ˆ + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: root + password: 123456 + slave: # æ¨¡æ‹Ÿä»Žåº“ï¼Œå¯æ ¹æ®è‡ªå·±éœ€è¦ä¿®æ”¹ # æ¨¡æ‹Ÿä»Žåº“ï¼Œå¯æ ¹æ®è‡ªå·±éœ€è¦ä¿®æ”¹ + lazy: true # 开坿‡’加载,ä¿è¯å¯åŠ¨é€Ÿåº¦ + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: root + password: 123456 + + # Redis é…置。Redisson 默认的é…置足够使用,一般ä¸éœ€è¦è¿›è¡Œè°ƒä¼˜ + data: + redis: + host: 400-infra.server.iocoder.cn # åœ°å€ + port: 6379 # ç«¯å£ + database: 1 # æ•°æ®åº“索引 +# password: 123456 # 密ç ï¼Œå»ºè®®ç”Ÿäº§çŽ¯å¢ƒå¼€å¯ + +--- #################### MQ 消æ¯é˜Ÿåˆ—相关é…ç½® #################### + +--- #################### 定时任务相关é…ç½® #################### +xxl: + job: + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # è°ƒåº¦ä¸­å¿ƒéƒ¨ç½²è·Ÿåœ°å€ + +--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® #################### + +# Lock4j é…置项 +lock4j: + acquire-timeout: 3000 # 获å–分布å¼é”超时时间,默认为 3000 毫秒 + expire: 30000 # 分布å¼é”的超时时间,默认为 30 毫秒 + +--- #################### 监控相关é…ç½® #################### + +# Actuator 监控端点的é…置项 +management: + endpoints: + web: + base-path: /actuator # Actuator æä¾›çš„ API 接å£çš„æ ¹ç›®å½•。默认为 /actuator + exposure: + include: '*' # 需è¦å¼€æ”¾çš„ç«¯ç‚¹ã€‚é»˜è®¤å€¼åªæ‰“å¼€ health å’Œ info 两个端点。通过设置 * ,å¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚ + +# Spring Boot Admin é…置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关é…ç½® + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + # Spring Boot Admin Server æœåŠ¡ç«¯çš„ç›¸å…³é…ç½® + context-path: /admin # é…ç½® Spring + +--- #################### 芋é“相关é…ç½® #################### + +# 芋é“é…置项,设置当å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½® +yudao: + demo: true # 开坿¼”ç¤ºæ¨¡å¼ + tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc diff --git a/tashow-module/tashow-module-trade/src/main/resources/application-local.yaml b/tashow-module/tashow-module-trade/src/main/resources/application-local.yaml new file mode 100644 index 0000000..ec71289 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/resources/application-local.yaml @@ -0,0 +1,138 @@ +--- #################### 注册中心 + é…置中心相关é…ç½® #################### + +spring: + cloud: + nacos: + server-addr: 127.0.0.1:8848 # Nacos æœåŠ¡å™¨åœ°å€ + username: # Nacos è´¦å· + password: # Nacos å¯†ç  + discovery: # ã€é…置中心】é…置项 + namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # æœåŠ¡å®žä¾‹çš„ç‰ˆæœ¬å·ï¼Œå¯ç”¨äºŽç°åº¦å‘布 + config: # ã€æ³¨å†Œä¸­å¿ƒã€‘é…置项 + namespace: dev # 命å空间。这里使用 dev å¼€å‘环境 + group: DEFAULT_GROUP # 使用的 Nacos é…置分组,默认为 DEFAULT_GROUP + +--- #################### æ•°æ®åº“相关é…ç½® #################### +spring: + # æ•°æ®æºé…置项 + autoconfigure: + exclude: + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # ç¦ç”¨ Spring Boot Admin çš„ Client 的自动é…ç½® + datasource: + druid: # Druid ã€ç›‘控】相关的全局é…ç½® + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白åå•,ä¸å¡«åˆ™å…许所有访问 + url-pattern: /druid/* + login-username: # 控制å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç  + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # æ…¢ SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # å¤šæ•°æ®æºé…ç½® + druid: # Druid ã€è¿žæŽ¥æ± ã€‘相关的全局é…ç½® + initial-size: 1 # åˆå§‹è¿žæŽ¥æ•° + min-idle: 1 # 最å°è¿žæŽ¥æ± æ•°é‡ + max-active: 20 # æœ€å¤§è¿žæŽ¥æ± æ•°é‡ + max-wait: 600000 # é…置获å–连接等待超时的时间,å•ä½ï¼šæ¯«ç§’ + time-between-eviction-runs-millis: 60000 # é…置间隔多久æ‰è¿›è¡Œä¸€æ¬¡æ£€æµ‹ï¼Œæ£€æµ‹éœ€è¦å…³é—­çš„空闲连接,å•ä½ï¼šæ¯«ç§’ + min-evictable-idle-time-millis: 300000 # é…置一个连接在池中最å°ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•ä½ï¼šæ¯«ç§’ + max-evictable-idle-time-millis: 900000 # é…置一个连接在池中最大生存的时间,å•ä½ï¼šæ¯«ç§’ + validation-query: SELECT 1 FROM DUAL # é…ç½®æ£€æµ‹è¿žæŽ¥æ˜¯å¦æœ‰æ•ˆ + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例 + # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 + username: root + password: 123456 + # username: sa # SQL Server 连接的示例 + # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例 + # username: SYSDBA # DM 连接的示例 + # password: SYSDBA # DM 连接的示例 + slave: # æ¨¡æ‹Ÿä»Žåº“ï¼Œå¯æ ¹æ®è‡ªå·±éœ€è¦ä¿®æ”¹ + lazy: true # 开坿‡’加载,ä¿è¯å¯åŠ¨é€Ÿåº¦ + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: 123456 + + # Redis é…置。Redisson 默认的é…置足够使用,一般ä¸éœ€è¦è¿›è¡Œè°ƒä¼˜ + data: + redis: + host: 127.0.0.1 # åœ°å€ + port: 6379 # ç«¯å£ + database: 0 # æ•°æ®åº“索引 +# password: 123456 # 密ç ï¼Œå»ºè®®ç”Ÿäº§çŽ¯å¢ƒå¼€å¯ + +--- #################### MQ 消æ¯é˜Ÿåˆ—相关é…ç½® #################### + +--- #################### 定时任务相关é…ç½® #################### + +xxl: + job: + enabled: false # 是å¦å¼€å¯è°ƒåº¦ä¸­å¿ƒï¼Œé»˜è®¤ä¸º true å¼€å¯ + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # è°ƒåº¦ä¸­å¿ƒéƒ¨ç½²è·Ÿåœ°å€ + +--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® #################### + +# Lock4j é…置项 +lock4j: + acquire-timeout: 3000 # 获å–分布å¼é”超时时间,默认为 3000 毫秒 + expire: 30000 # 分布å¼é”的超时时间,默认为 30 毫秒 + +--- #################### 监控相关é…ç½® #################### + +# Actuator 监控端点的é…置项 +management: + endpoints: + web: + base-path: /actuator # Actuator æä¾›çš„ API 接å£çš„æ ¹ç›®å½•。默认为 /actuator + exposure: + include: '*' # 需è¦å¼€æ”¾çš„ç«¯ç‚¹ã€‚é»˜è®¤å€¼åªæ‰“å¼€ health å’Œ info 两个端点。通过设置 * ,å¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚ + +# Spring Boot Admin é…置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关é…ç½® + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +# 日志文件é…ç½® +logging: + level: + # é…置自己写的 MyBatis Mapper æ‰“å°æ—¥å¿— + cn.iocoder.yudao.module.trade.dal.mysql: debug + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先ç¦ç”¨ï¼ŒSpring Boot 3.X 存在部分错误的 WARN æç¤º + +--- #################### 芋é“相关é…ç½® #################### + +# 芋é“é…置项,设置当å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½® +yudao: + env: # 多环境的é…置项 + tag: ${HOSTNAME} + security: + mock-enable: true + access-log: # 访问日志的é…置项 + enable: false + tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc diff --git a/tashow-module/tashow-module-trade/src/main/resources/application.yaml b/tashow-module/tashow-module-trade/src/main/resources/application.yaml new file mode 100644 index 0000000..b45cf90 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/resources/application.yaml @@ -0,0 +1,141 @@ +spring: + application: + name: trade-server + + profiles: + active: local + + main: + allow-circular-references: true # å…许循环ä¾èµ–,因为项目是三层架构,无法é¿å…这个情况。 + allow-bean-definition-overriding: true # å…许 Bean 覆盖,例如说 Feign 等会存在é‡å¤å®šä¹‰çš„æœåŠ¡ + + config: + import: + - optional:classpath:application-${spring.profiles.active}.yaml # åŠ è½½ã€æœ¬åœ°ã€‘é…ç½® + - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载ã€Nacos】的é…ç½® + + # Servlet é…ç½® + servlet: + # 文件上传相关é…置项 + multipart: + max-file-size: 16MB # å•ä¸ªæ–‡ä»¶å¤§å° + max-request-size: 32MB # è®¾ç½®æ€»ä¸Šä¼ çš„æ–‡ä»¶å¤§å° + + # Jackson é…置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 LocalDateTime 的格å¼ï¼Œä½¿ç”¨æ—¶é—´æˆ³ + write-date-timestamps-as-nanoseconds: false # 设置ä¸ä½¿ç”¨ nanoseconds 的格å¼ã€‚例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格å¼ï¼Œä½¿ç”¨æ—¶é—´æˆ³ + fail-on-empty-beans: false # å…许åºåˆ—化无属性的 Bean + + # Cache é…置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 å°æ—¶ + +server: + port: 48102 + +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件å,全路径 + +--- #################### æŽ¥å£æ–‡æ¡£é…ç½® #################### + +springdoc: + api-docs: + enabled: true # 1. 是å¦å¼€å¯ Swagger æŽ¥æ–‡æ¡£çš„å…ƒæ•°æ® + path: /v3/api-docs + swagger-ui: + enabled: true # 2.1 是å¦å¼€å¯ Swagger 文档的官方 UI ç•Œé¢ + path: /swagger-ui + default-flat-param-object: true # å‚è§ https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: false # TODO 芋艿:需è¦å…³é—­å¢žå¼ºï¼Œå…·ä½“原因è§ï¼šhttps://github.com/xiaoymin/knife4j/issues/874 + setting: + language: zh_cn + +# MyBatis Plus çš„é…置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # â€œæ™ºèƒ½â€æ¨¡å¼ï¼ŒåŸºäºŽ IdTypeEnvironmentPostProcessor + æ•°æ®æºçš„ç±»åž‹ï¼Œè‡ªåŠ¨é€‚é…æˆ AUTOã€INPUT 模å¼ã€‚ + # id-type: AUTO # 自增 IDï¼Œé€‚åˆ MySQL 等直接自增的数æ®åº“ + # id-type: INPUT # 用户输入 IDï¼Œé€‚åˆ Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“ + # id-type: ASSIGN_ID # åˆ†é… ID,默认使用雪花算法。注æ„,Oracleã€PostgreSQLã€Kingbaseã€DB2ã€H2 æ•°æ®åº“时,需è¦åŽ»é™¤å®žä½“ç±»ä¸Šçš„ @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制å°çš„ Banner æ‰“å° + type-aliases-package: ${yudao.info.base-package}.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,å¯ä½¿ç”¨ https://www.imaegoo.com/2020/aes-key-generator/ ç½‘ç«™ç”Ÿæˆ + +mybatis-plus-join: + banner: false # 关闭控制å°çš„ Banner æ‰“å° + +# Spring Data Redis é…ç½® +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis çš„ Repository,所以直接ç¦ç”¨ï¼Œä¿è¯å¯åŠ¨é€Ÿåº¦ + +# VO 转æ¢ï¼ˆæ•°æ®ç¿»è¯‘)相关 +easy-trans: + is-enable-global: true # å¯ç”¨å…¨å±€ç¿»è¯‘(拦截所有 SpringMVC ResponseBody 进行自动翻译 )ã€‚å¦‚æžœå¯¹äºŽæ€§èƒ½è¦æ±‚很高å¯å…³é—­æ­¤é…置,或通过 @IgnoreTrans 忽略æŸä¸ªæŽ¥å£ + +--- #################### RPC 远程调用相关é…ç½® #################### + +--- #################### MQ 消æ¯é˜Ÿåˆ—相关é…ç½® #################### + +--- #################### 定时任务相关é…ç½® #################### + +xxl: + job: + executor: + appname: ${spring.application.name} # 执行器 AppName + logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器è¿è¡Œæ—¥å¿—文件存储ç£ç›˜è·¯å¾„ + accessToken: default_token # 执行器通讯TOKEN + +--- #################### 芋é“相关é…ç½® #################### + +yudao: + info: + version: 1.0.0 + base-package: cn.iocoder.yudao.module.trade + web: + admin-ui: + url: http://dashboard.yudao.iocoder.cn # Admin 管ç†åŽå° UI çš„åœ°å€ + xss: + enable: false + exclude-urls: # 如下 url,仅仅是为了演示,去掉é…置也没关系 + - ${management.endpoints.web.base-path}/** # ä¸å¤„ç† Actuator 的请求 + swagger: + title: 管ç†åŽå° + description: æä¾›ç®¡ç†å‘˜ç®¡ç†çš„æ‰€æœ‰åŠŸèƒ½ + version: ${yudao.info.version} + tenant: # 多租户相关é…置项 + enable: true + ignore-urls: + ignore-tables: + trade: + order: + pay-expire-time: 2h # 支付的过期时间 + receive-expire-time: 14d # 收货的过期时间 + comment-expire-time: 7d # 评论的过期时间 + express: + client: KD_NIAO + kd-niao: + api-key: cb022f1e-48f1-4c4a-a723-9001ac9676b8 + business-id: 1809751 + request-type: 1002 # å…费版 1002;付费版 8001 + kd100: + key: pLXUGAwK5305 + customer: E77DF18BE109F454A5CD319E44BF5177 + +debug: false diff --git a/tashow-module/tashow-module-trade/src/main/resources/logback-spring.xml b/tashow-module/tashow-module-trade/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..b1b9f3f --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/resources/logback-spring.xml @@ -0,0 +1,76 @@ + + + + + + + + + +       + + + ${PATTERN_DEFAULT} + + + + + + + + + + ${PATTERN_DEFAULT} + + + + ${LOG_FILE} + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30} + + + + + + 0 + + 256 + + + + + + + + ${PATTERN_DEFAULT} + + + + + + + + + + + + + + + + + + + + + + diff --git a/tashow-module/tashow-module-trade/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml b/tashow-module/tashow-module-trade/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml new file mode 100644 index 0000000..151b33a --- /dev/null +++ b/tashow-module/tashow-module-trade/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/tashow-module/tashow-module-trade/src/test/resources/application-unit-test.yaml b/tashow-module/tashow-module-trade/src/test/resources/application-unit-test.yaml new file mode 100644 index 0000000..ab50eb5 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/test/resources/application-unit-test.yaml @@ -0,0 +1,60 @@ +spring: + main: + lazy-initialization: true # 开坿‡’加载,加快速度 + banner-mode: off # å•元测试,ç¦ç”¨ Banner + +--- #################### æ•°æ®åº“相关é…ç½® #################### + +spring: + # æ•°æ®æºé…置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模å¼ï¼›DATABASE_TO_UPPER é…置表和字段使用å°å†™ + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # å•元测试,异步åˆå§‹åŒ– Druid 连接池,æå‡å¯åŠ¨é€Ÿåº¦ + initial-size: 1 # å•元测试,é…置为 1,æå‡å¯åŠ¨é€Ÿåº¦ + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + + # Redis é…置。Redisson 默认的é…置足够使用,一般ä¸éœ€è¦è¿›è¡Œè°ƒä¼˜ + data: + redis: + host: 127.0.0.1 # åœ°å€ + port: 16379 # 端å£ï¼ˆå•元测试,使用 16379 端å£ï¼‰ + database: 0 # æ•°æ®åº“索引 + +mybatis: + lazy-initialization: true # å•元测试,设置 MyBatis Mapper 延迟加载,加速æ¯ä¸ªå•元测试 + +--- #################### 定时任务相关é…ç½® #################### + +--- #################### é…置中心相关é…ç½® #################### + +--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® #################### + +# Lock4j é…置项(å•元测试,ç¦ç”¨ Lock4j) + +--- #################### 监控相关é…ç½® #################### + +--- #################### 芋é“相关é…ç½® #################### + +# 芋é“é…置项,设置当å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½® +yudao: + info: + base-package: cn.iocoder.yudao.module + trade: + 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 \ No newline at end of file diff --git a/tashow-module/tashow-module-trade/src/test/resources/logback.xml b/tashow-module/tashow-module-trade/src/test/resources/logback.xml new file mode 100644 index 0000000..daf756b --- /dev/null +++ b/tashow-module/tashow-module-trade/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tashow-module/tashow-module-trade/src/test/resources/sql/clean.sql b/tashow-module/tashow-module-trade/src/test/resources/sql/clean.sql new file mode 100644 index 0000000..f7f3477 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/test/resources/sql/clean.sql @@ -0,0 +1,7 @@ +DELETE FROM trade_order; +DELETE FROM trade_order_item; +DELETE FROM trade_after_sale; +DELETE FROM trade_after_sale_log; +DELETE FROM trade_brokerage_user; +DELETE FROM trade_brokerage_record; +DELETE FROM "trade_brokerage_withdraw"; diff --git a/tashow-module/tashow-module-trade/src/test/resources/sql/create_tables.sql b/tashow-module/tashow-module-trade/src/test/resources/sql/create_tables.sql new file mode 100644 index 0000000..1d7ed24 --- /dev/null +++ b/tashow-module/tashow-module-trade/src/test/resources/sql/create_tables.sql @@ -0,0 +1,235 @@ +CREATE TABLE IF NOT EXISTS "trade_order" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "no" varchar NOT NULL, + "type" int NOT NULL, + "terminal" int NOT NULL, + "user_id" bigint NOT NULL, + "user_ip" varchar NOT NULL, + "user_remark" varchar, + "status" int NOT NULL, + "product_count" int NOT NULL, + "cancel_type" int, + "remark" varchar, + "comment_status" boolean, + "brokerage_user_id" bigint, + "pay_status" bit NOT NULL, + "pay_time" datetime, + "finish_time" datetime, + "cancel_time" datetime, + "total_price" int NULL, + "order_price" int NULL, + "discount_price" int NOT NULL, + "delivery_price" int NOT NULL, + "adjust_price" int NOT NULL, + "pay_price" int NOT NULL, + "delivery_type" int NOT NULL, + "pay_order_id" bigint, + "pay_channel_code" varchar, + "delivery_template_id" bigint, + "logistics_id" bigint, + "logistics_no" varchar, + "delivery_time" datetime, + "receive_time" datetime, + "receiver_name" varchar NOT NULL, + "receiver_mobile" varchar NOT NULL, + "receiver_area_id" int NOT NULL, + "receiver_post_code" int, + "receiver_detail_address" varchar NOT NULL, + "pick_up_store_id" long NULL, + "pick_up_verify_code" varchar NULL, + "refund_status" int NULL, + "refund_price" int NULL, + "after_sale_status" int NULL, + "coupon_id" bigint NOT NULL, + "coupon_price" int NOT NULL, + "use_point" int NULL, + "point_price" int NOT NULL, + "give_point" int NULL, + "refund_point" int NULL, + "vip_price" int NULL, + "give_coupons_map" varchar NULL, + "seckill_activity_id" long NULL, + "bargain_activity_id" long NULL, + "bargain_record_id" long NULL, + "combination_activity_id" long NULL, + "combination_head_id" long NULL, + "combination_record_id" long NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '交易订å•表'; + +CREATE TABLE IF NOT EXISTS "trade_order_item" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "order_id" bigint NOT NULL, + "cart_id" int NULL, + "spu_id" bigint NOT NULL, + "spu_name" varchar NOT NULL, + "sku_id" bigint NOT NULL, + "properties" varchar, + "pic_url" varchar, + "count" int NOT NULL, + "comment_status" boolean NULL, + "price" int NOT NULL, + "discount_price" int NOT NULL, + "delivery_price" int NULL, + "adjust_price" int NULL, + "pay_price" int NOT NULL, + "coupon_price" int NULL, + "point_price" int NULL, + "use_point" int NULL, + "give_point" int NULL, + "vip_price" int NULL, + "after_sale_id" long NULL, + "after_sale_status" int NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT 'äº¤æ˜“è®¢å•æ˜Žç»†è¡¨'; + +CREATE TABLE IF NOT EXISTS "trade_after_sale" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "no" varchar NOT NULL, + "status" int NOT NULL, + "type" int NOT NULL, + "way" int NOT NULL, + "user_id" bigint NOT NULL, + "apply_reason" varchar NOT NULL, + "apply_description" varchar, + "apply_pic_urls" varchar, + "order_id" bigint NOT NULL, + "order_no" varchar NOT NULL, + "order_item_id" bigint NOT NULL, + "spu_id" bigint NOT NULL, + "spu_name" varchar NOT NULL, + "sku_id" bigint NOT NULL, + "properties" varchar, + "pic_url" varchar, + "count" int NOT NULL, + "audit_time" varchar, + "audit_user_id" bigint, + "audit_reason" varchar, + "refund_price" int NOT NULL, + "pay_refund_id" bigint, + "refund_time" varchar, + "logistics_id" bigint, + "logistics_no" varchar, + "delivery_time" varchar, + "receive_time" varchar, + "receive_reason" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '交易售åŽè¡¨'; + +CREATE TABLE IF NOT EXISTS "trade_after_sale_log" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "user_type" int NOT NULL, + "after_sale_id" bigint NOT NULL, + "order_id" bigint NOT NULL, + "order_item_id" bigint NOT NULL, + "before_status" int, + "after_status" int NOT NULL, + "content" varchar NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT 'äº¤æ˜“å”®åŽæ—¥å¿—'; + +CREATE TABLE IF NOT EXISTS "trade_brokerage_user" +( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "bind_user_id" bigint NOT NULL, + "bind_user_time" varchar, + "brokerage_enabled" bit NOT NULL, + "brokerage_time" varchar, + "price" int NOT NULL, + "frozen_price" int NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint NOT NULL DEFAULT '0', + PRIMARY KEY ("id") +) COMMENT '分销用户'; +CREATE TABLE IF NOT EXISTS "trade_brokerage_record" +( + "id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "biz_id" varchar NOT NULL, + "biz_type" varchar NOT NULL, + "title" varchar NOT NULL, + "price" int NOT NULL, + "total_price" int NOT NULL, + "description" varchar NOT NULL, + "status" varchar NOT NULL, + "frozen_days" int NOT NULL, + "unfreeze_time" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '佣金记录'; +CREATE TABLE IF NOT EXISTS "trade_brokerage_withdraw" +( + "id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "price" int NOT NULL, + "fee_price" int NOT NULL, + "total_price" int NOT NULL, + "type" varchar NOT NULL, + "name" varchar, + "account_no" varchar, + "bank_name" varchar, + "bank_address" varchar, + "account_qr_code_url" varchar, + "status" varchar NOT NULL, + "audit_reason" varchar, + "audit_time" varchar, + "remark" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '佣金æçް'; + +CREATE TABLE IF NOT EXISTS "trade_delivery_express" +( + "id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar NULL, + "name" varchar, + "logo" varchar NULL, + "sort" int NOT NULL, + "status" int NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '佣金æçް'; \ No newline at end of file