diff --git a/README.md b/README.md index d0881be39..003679068 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,12 @@ > > 迫切希望,有前端能力不错的小伙伴,加入我们,一起来完善「芋道商城」。 +## 管理后台 + +体验传送门: + +![GIF 图-耐心等待](https://raw.githubusercontent.com/YunaiV/Blog/master/Mall/onemall-admin-min.gif) + ## H5 商城 体验传送门: @@ -42,14 +48,6 @@ ![GIF 图-耐心等待](https://raw.githubusercontent.com/YunaiV/Blog/master/Mall/onemall-h5-min.gif) -## 管理后台 - -体验传送门: - -*2M 带宽小水管,访问略微有点慢* - -![GIF 图-耐心等待](https://raw.githubusercontent.com/YunaiV/Blog/master/Mall/onemall-admin-min.gif) - ## 其它演示 下面,我们会提供目前用到的中间件的管理平台。 @@ -81,10 +79,10 @@ **XXL-Job Console** -* 地址: -* 管理员账号:admin / 233666 +* 地址: +* 管理员账号:admin / 123456 -> 教程:[《芋道 RocketMQ 安装部署》](http://www.iocoder.cn/XXL-JOB/install/?onemall) +> 教程:[《芋道 XXL-Job 安装部署》](http://www.iocoder.cn/XXL-JOB/install/?onemall) **Sentinel Console** @@ -107,33 +105,35 @@ TODO 此处应有一个架构图的装逼 JPG 图。 | 模块 | 名称 | 端口 | | | --- | --- | --- | --- | -| `admin-web` | 【前端】管理后台 | HTTP 8080 | | -| `mobile-web` | 【前端】商城 H5 | HTTP 8000 | | -| `system-application` | 管理员 HTTP 服务 | HTTP 18083 | [接口文档](http://api.shop.iocoder.cn/admin-api/doc.html) | -| `user-application` | 用户 HTTP 服务 | HTTP 18082 | [接口文档](http://api.shop.iocoder.cn/user-api/doc.html) | -| `product-application` | 商品 HTTP 服务 | HTTP 18081 | [接口文档](http://api.shop.iocoder.cn/product-api/doc.html) | -| `pay-application` | 支付 HTTP 服务 | HTTP 18084 | [接口文档](http://api.shop.iocoder.cn/pay-api/doc.html) | -| `promotion-application` | 促销 HTTP 服务 | HTTP 18085 | [接口文档](http://api.shop.iocoder.cn/promotion-api/doc.html) | -| `search-application` | 搜索 HTTP 服务 | HTTP 18086 | [接口文档](http://api.shop.iocoder.cn/search-api/doc.html) | -| `order-application` | 订单 HTTP 服务 | HTTP 18088 | [接口文档](http://api.shop.iocoder.cn/order-api/doc.html) | +| [`admin-dashboard-vue`](https://github.com/YunaiV/onemall-web/tree/master/admin-dashboard-vue) | 【前端】管理后台 | HTTP 9527 | | +| [`user-dashboard-vue`](https://github.com/YunaiV/onemall-web/tree/master/user-h5-vue) | 【前端】商城平台 | HTTP 8080 | | +| | | | +| | | | +| `management-web-app` | 【后端】管理平台 HTTP 服务 | HTTP 18083 | [接口文档](http://api-dashboard.shop.iocoder.cn/management-api/doc.html) | +| `shop-web-app` | 【后端】商城平台 HTTP 服务 | HTTP 18084 | [接口文档](http://api-h5.shop.iocoder.cn/shop-api/doc.html) | +| | | | +| | | | +| `system-service-project` | 系统 RPC 服务 | 随机 | +| `user-service-project` | 用户 RPC 服务 | 随机 | | +| `promotion-service-project` | 营销 RPC 服务 | 随机 | | +| `pay-service-project` | 支付 RPC 服务 | 随机 | | +| `trade-service-project` | 交易 RPC 服务 | 随机 | | +| `product-service-project` | 商品 RPC 服务 | 随机 | | +| `search-service-project` | 搜索å RPC 服务 | 随机 | | ------- 后端项目,目前的项目结构如下: ```Java -[-] xxx - ├──[-] xxx-application // 提供对外 HTTP API 。 - ├──[-] xxx-service-api // 提供 Dubbo 服务 API 。 - ├──[-] xxx-service-impl // 提供 Dubbo 服务 Service 实现。 +[-] xxx-web-app // 提供对外 HTTP API。 + +[-] xxx-service-project + ├──[-] xxx-service-api // 提供对内 RPC API 。 + ├──[-] xxx-service-app // 提供对内 RPC 实现。 + ├──[-] xxx-service-integration-test // 集成测试。 ``` -考虑到大多数公司,无需拆分的特别细,并且过多 JVM 带来的服务器成本。所以目前的设定是: - -* `xxx-service-impl` 内嵌在 `xxx-application` 中运行。 -* MQ 消费者、定时器执行器,内嵌在 `xxx-service-impl` 中运行。 - -也就是说,一个 `xxx-application` 启动后,该模块就完整启动了。 ## 技术栈 @@ -165,8 +165,6 @@ TODO 此处应有一个架构图的装逼 JPG 图。 ### 前端 -商城 H5 和管理后台,分别采用了 Vue 和 React ,基于其适合的场景考虑。具体的,可以看看 [《为什么 React 比 Vue 更适合大型应用?》](https://www.zhihu.com/question/314761485/answer/615318460) 的讨论。 - **商城 H5** | 框架 | 说明 | 版本 | @@ -178,8 +176,8 @@ TODO 此处应有一个架构图的装逼 JPG 图。 | 框架 | 说明 | 版本 | | --- | --- | --- | -| [React](https://reactjs.org/) | JavaScript 框架 | 16.7.0 | -| [Ant Design](https://ant.design/docs/react/introduce-cn) | React UI 组件库 | 3.13.0 | +| [Vue](https://cn.vuejs.org/index.html) | JavaScript 框架 | 2.5.17 | +| [Vue Element Admin](https://ant.design/docs/react/introduce-cn) | 后台前端解决方案 | - | ### 监控 diff --git a/common/mall-spring-boot-starter-xxl-job/pom.xml b/common/mall-spring-boot-starter-xxl-job/pom.xml new file mode 100644 index 000000000..50dfe6de8 --- /dev/null +++ b/common/mall-spring-boot-starter-xxl-job/pom.xml @@ -0,0 +1,34 @@ + + + + common + cn.iocoder.mall + 1.0-SNAPSHOT + + 4.0.0 + + mall-spring-boot-starter-xxl-job + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter + true + + + + + com.xuxueli + xxl-job-core + + + + diff --git a/common/mall-spring-boot-starter-xxl-job/src/main/java/cn/iocoder/mall/xxljob/config/XxlJobAutoConfiguration.java b/common/mall-spring-boot-starter-xxl-job/src/main/java/cn/iocoder/mall/xxljob/config/XxlJobAutoConfiguration.java new file mode 100644 index 000000000..40c96fc61 --- /dev/null +++ b/common/mall-spring-boot-starter-xxl-job/src/main/java/cn/iocoder/mall/xxljob/config/XxlJobAutoConfiguration.java @@ -0,0 +1,56 @@ +package cn.iocoder.mall.xxljob.config; + +import com.xxl.job.core.executor.XxlJobExecutor; +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Objects; + +/** + * XXL-Job 自动配置类 + */ +@Configuration +@ConditionalOnClass(XxlJobSpringExecutor.class) +@ConditionalOnProperty(prefix = "xxl.job", name = "enabled", havingValue = "true", matchIfMissing = true) +@EnableConfigurationProperties({XxlJobProperties.class}) +public class XxlJobAutoConfiguration { + + private static final Logger LOGGER = LoggerFactory.getLogger(XxlJobAutoConfiguration.class); + + private final XxlJobProperties properties; + + public XxlJobAutoConfiguration(XxlJobProperties properties) { + this.properties = properties; + } + + @Bean + @ConditionalOnMissingBean + public XxlJobExecutor xxlJobExecutor() { + LOGGER.info("初始化 XXL-Job 执行器的配置"); + + // 参数校验 + XxlJobProperties.AdminProperties admin = this.properties.getAdmin(); + XxlJobProperties.ExecutorProperties executor = this.properties.getExecutor(); + Objects.requireNonNull(admin, "xxl job admin properties must not be null."); + Objects.requireNonNull(executor, "xxl job executor properties must not be null."); + + // 初始化执行器 + XxlJobExecutor xxlJobExecutor = new XxlJobSpringExecutor(); + xxlJobExecutor.setIp(executor.getIp()); + xxlJobExecutor.setPort(executor.getPort()); + xxlJobExecutor.setAppname(executor.getAppName()); + xxlJobExecutor.setLogPath(executor.getLogPath()); + xxlJobExecutor.setLogRetentionDays(executor.getLogRetentionDays()); + xxlJobExecutor.setAdminAddresses(admin.getAddresses()); + xxlJobExecutor.setAccessToken(this.properties.getAccessToken()); + return xxlJobExecutor; + } + +} diff --git a/common/mall-spring-boot-starter-xxl-job/src/main/java/cn/iocoder/mall/xxljob/config/XxlJobProperties.java b/common/mall-spring-boot-starter-xxl-job/src/main/java/cn/iocoder/mall/xxljob/config/XxlJobProperties.java new file mode 100644 index 000000000..9b777a2cc --- /dev/null +++ b/common/mall-spring-boot-starter-xxl-job/src/main/java/cn/iocoder/mall/xxljob/config/XxlJobProperties.java @@ -0,0 +1,172 @@ +package cn.iocoder.mall.xxljob.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * XXL-Job 配置类 + */ +@ConfigurationProperties("xxl.job") +public class XxlJobProperties { + + /** + * 是否开启,默认为 true 关闭 + */ + private Boolean enabled = true; + /** + * 访问令牌 + */ + private String accessToken; + /** + * 控制器配置 + */ + private AdminProperties admin; + /** + * 执行器配置 + */ + private ExecutorProperties executor; + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + if (enabled != null) { + this.enabled = enabled; + } + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + if (accessToken != null && accessToken.trim().length() > 0) { + this.accessToken = accessToken; + } + } + + public AdminProperties getAdmin() { + return admin; + } + + public void setAdmin(AdminProperties admin) { + this.admin = admin; + } + + public ExecutorProperties getExecutor() { + return executor; + } + + public void setExecutor(ExecutorProperties executor) { + this.executor = executor; + } + + /** + * XXL-Job 调度器配置类 + */ + public static class AdminProperties { + + /** + * 调度器地址 + */ + private String addresses; + + public String getAddresses() { + return addresses; + } + + public void setAddresses(String addresses) { + this.addresses = addresses; + } + + @Override + public String toString() { + return "AdminProperties{" + + "addresses='" + addresses + '\'' + + '}'; + } + + } + + /** + * XXL-Job 执行器配置类 + */ + public static class ExecutorProperties { + + /** + * 默认端口 + * + * 这里使用 -1 表示随机 + */ + private static final Integer PORT_DEFAULT = -1; + + /** + * 默认日志保留天数 + * + * 默认为 -1,不清理,永久保留 + */ + private static final Integer LOG_RETENTION_DAYS_DEFAULT = -1; + + /** + * 应用名 + */ + private String appName; + /** + * 执行器的 IP + */ + private String ip; + /** + * 执行器的 Port + */ + private Integer port = PORT_DEFAULT; + /** + * 日志地址 + */ + private String logPath; + /** + * 日志保留天数 + */ + private Integer logRetentionDays = LOG_RETENTION_DAYS_DEFAULT; + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getLogPath() { + return logPath; + } + + public void setLogPath(String logPath) { + this.logPath = logPath; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Integer getLogRetentionDays() { + return logRetentionDays; + } + + public void setLogRetentionDays(Integer logRetentionDays) { + this.logRetentionDays = logRetentionDays; + } + } + +} diff --git a/common/mall-spring-boot-starter-xxl-job/src/main/resources/META-INF/spring.factories b/common/mall-spring-boot-starter-xxl-job/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..337f8f596 --- /dev/null +++ b/common/mall-spring-boot-starter-xxl-job/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + cn.iocoder.mall.xxljob.config.XxlJobAutoConfiguration diff --git a/common/pom.xml b/common/pom.xml index 34ea1fa22..109af83ba 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -24,6 +24,7 @@ mall-spring-boot-starter-dubbo mall-spring-boot-starter-system-error-code mall-spring-boot-starter-rocketmq + mall-spring-boot-starter-xxl-job diff --git a/mall-dependencies/pom.xml b/mall-dependencies/pom.xml index da3718609..069608c19 100644 --- a/mall-dependencies/pom.xml +++ b/mall-dependencies/pom.xml @@ -43,9 +43,9 @@ 2.7.7 - 2.1.0 + 2.1.1 - 2.0.1 + 2.2.0 1.1.0 @@ -249,6 +249,12 @@ ${xxl-job.version} + + cn.iocoder.mall + mall-spring-boot-starter-xxl-job + 1.0-SNAPSHOT + + diff --git a/moved/order/order-biz/src/main/java/cn/iocoder/mall/order/biz/dao/order/OrderMapper.java b/moved/order/order-biz/src/main/java/cn/iocoder/mall/order/biz/dao/order/OrderMapper.java deleted file mode 100644 index dac1f6847..000000000 --- a/moved/order/order-biz/src/main/java/cn/iocoder/mall/order/biz/dao/order/OrderMapper.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.iocoder.mall.order.biz.dao.order; - -import cn.iocoder.mall.order.biz.dataobject.OrderDO; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.springframework.stereotype.Repository; - -/** - * 订单 mapper - * - * @author Sin - * @time 2019-03-16 15:09 - */ -@Repository -public interface OrderMapper extends BaseMapper { - -// /** -// * 更新 - 根据 id 更新 -// * -// * @param orderDO -// * @return -// */ -// int updateById(OrderDO orderDO); -// -// int updateByIdAndStatus(@Param("id") Integer id, -// @Param("status") Integer status, -// @Param("updateObj") OrderDO updateObj); -// -// /** -// * 查询 - 根据id 查询 -// * -// * @param id -// * @return -// */ -// OrderDO selectById( -// @Param("id") Integer id -// ); -// -// /** -// * 查询 - 后台分页page -// * -// * @param orderQueryDTO -// * @return -// */ -// int selectPageCount(OrderQueryDTO orderQueryDTO); -// -// /** -// * 查询 - 后台分页page -// * -// * @param orderQueryDTO -// * @return -// */ -// List selectPage(OrderQueryDTO orderQueryDTO); -} diff --git a/moved/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/dao/OrderMapper.java b/moved/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/dao/OrderMapper.java index 642e96f48..d9c16f47f 100644 --- a/moved/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/dao/OrderMapper.java +++ b/moved/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/dao/OrderMapper.java @@ -17,28 +17,6 @@ import java.util.List; @Repository public interface OrderMapper extends BaseMapper { - /** - * 更新 - 根据 id 更新 - * - * @param orderDO - * @return - */ - int updateById(OrderDO orderDO); - - int updateByIdAndStatus(@Param("id") Integer id, - @Param("status") Integer status, - @Param("updateObj") OrderDO updateObj); - - /** - * 查询 - 根据id 查询 - * - * @param id - * @return - */ - OrderDO selectById( - @Param("id") Integer id - ); - /** * 查询 - 后台分页page * diff --git a/moved/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/OrderServiceImpl.java b/moved/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/OrderServiceImpl.java index 97a6af18d..2c55e2db3 100644 --- a/moved/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/OrderServiceImpl.java +++ b/moved/order/order-service-impl/src/main/java/cn/iocoder/mall/order/biz/service/OrderServiceImpl.java @@ -326,31 +326,6 @@ public class OrderServiceImpl implements OrderService { return CommonResult.success(null); } - @Override - public String updatePaySuccess(String orderId, Integer payAmount) { - OrderDO order = orderMapper.selectById(Integer.valueOf(orderId)); - if (order == null) { // 订单不存在 - return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_NOT_EXISTENT.getCode()).getMessage(); - } - if (!order.getStatus().equals(OrderStatusEnum.WAITING_PAYMENT.getValue())) { // 状态不处于等待支付 - return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_STATUS_NOT_WAITING_PAYMENT.getCode()).getMessage(); - } - if (!order.getPresentPrice().equals(payAmount)) { // 支付金额不正确 - return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_PAY_AMOUNT_ERROR.getCode()).getMessage(); - } - // 更新 OrderDO 状态为已支付,等待发货 - OrderDO updateOrderObj = new OrderDO() - .setStatus(OrderStatusEnum.WAIT_SHIPMENT.getValue()) - .setPayAmount(payAmount) - .setPaymentTime(new Date()); - int updateCount = orderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), updateOrderObj); - if (updateCount <= 0) { - return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_STATUS_NOT_WAITING_PAYMENT.getCode()).getMessage(); - } - // TODO FROM 芋艿 to 小范,把更新 OrderItem 给补全。 - return "success"; - } - @Override public CommonResult listenerConfirmGoods() { return null; diff --git a/moved/order/order-service-impl/src/main/resources/mapper/OrderMapper.xml b/moved/order/order-service-impl/src/main/resources/mapper/OrderMapper.xml index 2f03b33f3..e5bb8981d 100644 --- a/moved/order/order-service-impl/src/main/resources/mapper/OrderMapper.xml +++ b/moved/order/order-service-impl/src/main/resources/mapper/OrderMapper.xml @@ -2,100 +2,6 @@ - - id, user_id, order_no, buy_price, discount_price, logistics_price, present_price, pay_amount, - payment_time, delivery_time, receiver_time, closing_time, - has_return_exchange, - status, remark, create_time, update_time, `deleted` - - - - - - , order_no = #{orderNo} - - - , buy_price = #{buyPrice} - - - , discount_price = #{discountPrice} - - - , logistics_price = #{logisticsPrice} - - - , logistics_price = #{logisticsPrice} - - - , present_price = #{presentPrice} - - - , pay_amount = #{payAmount} - - - , delivery_time = #{deliveryTime} - - - , payment_time = #{paymentTime} - - - , receiver_time = #{receiverTime} - - - , closing_time = #{closingTime} - - - , has_return_exchange = #{hasReturnExchange} - - - - , status = #{status} - - - , remark = #{remark} - - - , `deleted` = #{deleted} - - - , create_time = #{createTime} - - - , update_time = #{updateTime} - - - - - - UPDATE `orders` - - WHERE id = #{id} - - - - UPDATE `orders` - - - , pay_amount = #{updateObj.payAmount} - - - , payment_time = #{updateObj.paymentTime} - - - , status = #{updateObj.status} - - - WHERE id = #{id} - AND status = #{status} - - - - AND `status` = #{status} diff --git a/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/config/XxlJobConfiguration.java b/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/config/XxlJobConfiguration.java deleted file mode 100644 index df934f473..000000000 --- a/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/config/XxlJobConfiguration.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.mall.pay.biz.config; - -import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; - -@Configuration -@Profile("dev") -public class XxlJobConfiguration { - - private Logger logger = LoggerFactory.getLogger(XxlJobConfiguration.class); - - @Value("${xxl.job.admin.addresses}") - private String adminAddresses; - @Value("${xxl.job.executor.appname}") - private String appName; - @Value("${xxl.job.executor.ip}") - private String ip; - @Value("${xxl.job.executor.port}") - private int port; - @Value("${xxl.job.accessToken}") - private String accessToken; - @Value("${xxl.job.executor.logpath}") - private String logPath; - @Value("${xxl.job.executor.logretentiondays}") - private int logRetentionDays; - - @Bean(initMethod = "start", destroyMethod = "destroy") - public XxlJobSpringExecutor xxlJobExecutor() { - logger.info(">>>>>>>>>>> xxl-job config init."); - XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); - xxlJobSpringExecutor.setAdminAddresses(adminAddresses); - xxlJobSpringExecutor.setAppName(appName); - xxlJobSpringExecutor.setIp(ip); - xxlJobSpringExecutor.setPort(port); - xxlJobSpringExecutor.setAccessToken(accessToken); - xxlJobSpringExecutor.setLogPath(logPath); - xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); - - return xxlJobSpringExecutor; - } - -} diff --git a/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/job/PayNotifyJob.java b/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/job/PayNotifyJob.java deleted file mode 100644 index f4ea7ad43..000000000 --- a/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/job/PayNotifyJob.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.mall.pay.biz.job; - -import cn.iocoder.mall.pay.biz.dao.PayNotifyTaskMapper; -import cn.iocoder.mall.pay.biz.dataobject.PayNotifyTaskDO; -import cn.iocoder.mall.pay.biz.service.PayNotifyServiceImpl; -import com.xxl.job.core.biz.model.ReturnT; -import com.xxl.job.core.handler.IJobHandler; -import com.xxl.job.core.handler.annotation.JobHandler; -import org.apache.rocketmq.spring.core.RocketMQTemplate; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.Date; -import java.util.List; - -/** - * 支付通知重试 Job - */ -@Component -@JobHandler(value = "payTransactionNotifyJob") -public class PayNotifyJob extends IJobHandler { - - @Autowired - private PayNotifyTaskMapper payTransactionNotifyTaskMapper; - - @Autowired - private PayNotifyServiceImpl payNotifyService; - - @Resource - private RocketMQTemplate rocketMQTemplate; - - @Override - public ReturnT execute(String param) { - // 获得需要通知的任务 - List notifyTasks = payTransactionNotifyTaskMapper.selectByNotify(); - // 循环任务,发送通知 - for (PayNotifyTaskDO notifyTask : notifyTasks) { - // 发送 MQ - payNotifyService.sendNotifyMessage(notifyTask); - // 更新最后通知时间 - // 1. 这样操作,虽然可能会出现 MQ 消费快于下面 PayTransactionNotifyTaskDO 的更新语句。但是,因为更新字段不同,所以不会有问题。 - // 2. 换个视角,如果先更新 PayTransactionNotifyTaskDO ,再发送 MQ 消息。如果 MQ 消息发送失败,则 PayTransactionNotifyTaskDO 再也不会被轮询到了。 - // 3. 当然,最最最完美的话,就是做事务消息,不过这样又过于复杂~ - PayNotifyTaskDO updateNotifyTask = new PayNotifyTaskDO() - .setId(notifyTask.getId()).setLastExecuteTime(new Date()); - payTransactionNotifyTaskMapper.update(updateNotifyTask); - } - return new ReturnT<>("执行通知数:" + notifyTasks.size()); - } - -} diff --git a/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/service/PayTransactionServiceImpl.java b/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/service/PayTransactionServiceImpl.java index 907f772d3..8dfab57a6 100644 --- a/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/service/PayTransactionServiceImpl.java +++ b/moved/pay/pay-application/src/main/java/cn/iocoder/mall/pay/biz/service/PayTransactionServiceImpl.java @@ -80,7 +80,4 @@ public class PayTransactionServiceImpl implements PayTransactionService { return null; } - - - } diff --git a/moved/pay/pay-application/src/main/resources/config/application-test.yaml b/moved/pay/pay-application/src/main/resources/config/application-test.yaml deleted file mode 100644 index a9b765157..000000000 --- a/moved/pay/pay-application/src/main/resources/config/application-test.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# xxl-job -xxl: - job: - admin: - addresses: http://s1.iocoder.cn:18079/ - executor: - appname: pay-job-executor - ip: - port: 0 - logpath: /Users/yunai/logs/xxl-job/ - logretentiondays: 1 - accessToken: diff --git a/moved/pay/pay-application/src/test/java/DubboGenericInvokerTest.java b/moved/pay/pay-application/src/test/java/DubboGenericInvokerTest.java deleted file mode 100644 index 0cf7cfd2d..000000000 --- a/moved/pay/pay-application/src/test/java/DubboGenericInvokerTest.java +++ /dev/null @@ -1,32 +0,0 @@ -import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.config.ReferenceConfig; -import org.apache.dubbo.config.RegistryConfig; -import org.apache.dubbo.rpc.service.GenericService; - -public class DubboGenericInvokerTest { - - public static void main(String[] args) { - ApplicationConfig application = new ApplicationConfig(); - application.setName("api-generic-consumer"); - - RegistryConfig registry = new RegistryConfig(); - registry.setAddress("zookeeper://127.0.0.1:2181"); - - application.setRegistry(registry); - - ReferenceConfig reference = new ReferenceConfig<>(); - // 弱类型接口名 - reference.setInterface("cn.iocoder.mall.order.api.OrderService"); - // 声明为泛化接口 - reference.setGeneric(true); - - reference.setApplication(application); - - // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用 - GenericService genericService = reference.get(); - - String name = (String) genericService.$invoke("updatePaySuccess", new String[]{String.class.getName()}, new Object[]{"1"}); - System.out.println(name); - } - -} diff --git a/pay-service-project/pay-service-app/pom.xml b/pay-service-project/pay-service-app/pom.xml index da8469b5a..f16d1a35d 100644 --- a/pay-service-project/pay-service-app/pom.xml +++ b/pay-service-project/pay-service-app/pom.xml @@ -37,6 +37,12 @@ mall-spring-boot-starter-rocketmq + + + cn.iocoder.mall + mall-spring-boot-starter-xxl-job + + com.alibaba.cloud diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/common/dubbo/DubboReferencePool.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/common/dubbo/DubboReferencePool.java index f853aea20..19ea518fb 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/common/dubbo/DubboReferencePool.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/common/dubbo/DubboReferencePool.java @@ -46,9 +46,15 @@ public class DubboReferencePool { @Value("${dubbo.application.name}") private String dubboApplicationName; + public ReferenceMeta getReferenceMeta(String notifyUrl) { + DubboReferencePool.ReferenceMeta referenceMeta = referenceMetaCache.getUnchecked(notifyUrl); + Assert.notNull(referenceMeta, String.format("notifyUrl(%s) 不存在对应的 ReferenceMeta 对象", notifyUrl)); + return referenceMeta; + } + private ReferenceMeta createGenericService(String notifyUrl) { // 使用 # 号分隔,格式为 服务名#方法名#版本号 - List notifyUrlParts = StringUtils.split(notifyUrl, "#"); + List notifyUrlParts = this.parseNotifyUrl(notifyUrl); // 创建 ApplicationConfig 对象 ApplicationConfig application = new ApplicationConfig(); application.setName(dubboApplicationName); @@ -69,10 +75,9 @@ public class DubboReferencePool { return new ReferenceMeta(reference, genericService, notifyUrlParts.get(1)); } - public ReferenceMeta getReferenceMeta(String notifyUrl) { - DubboReferencePool.ReferenceMeta referenceMeta = referenceMetaCache.getUnchecked(notifyUrl); - Assert.notNull(referenceMeta, String.format("notifyUrl(%s) 不存在对应的 ReferenceMeta 对象", notifyUrl)); - return referenceMeta; + // TODO 芋艿,后续重构成一个对象 + private List parseNotifyUrl(String notifyUrl) { + return StringUtils.split(notifyUrl, "#"); } } diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/config/AopConfiguration.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/config/AopConfiguration.java new file mode 100644 index 000000000..d472d4803 --- /dev/null +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/config/AopConfiguration.java @@ -0,0 +1,12 @@ +package cn.iocoder.mall.payservice.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * Spring Aop 配置类 + */ +@Configuration +@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) +public class AopConfiguration { +} diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/convert/notify/PayNotifyConvert.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/convert/notify/PayNotifyConvert.java index 3e2d0f05a..dca4e7db1 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/convert/notify/PayNotifyConvert.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/convert/notify/PayNotifyConvert.java @@ -4,6 +4,8 @@ import cn.iocoder.mall.payservice.dal.mysql.dataobject.notify.PayNotifyTaskDO; import cn.iocoder.mall.payservice.mq.producer.message.PayRefundSuccessMessage; import cn.iocoder.mall.payservice.mq.producer.message.PayTransactionSuccessMessage; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; @Mapper @@ -11,8 +13,18 @@ public interface PayNotifyConvert { PayNotifyConvert INSTANCE = Mappers.getMapper(PayNotifyConvert.class); - PayTransactionSuccessMessage convertTransaction(PayNotifyTaskDO payTransactionNotifyTaskDO); + @Mappings({ + @Mapping(source = "transaction.transactionId", target = "transactionId"), + @Mapping(source = "transaction.orderId", target = "orderId"), + }) + PayTransactionSuccessMessage convertTransaction(PayNotifyTaskDO entity); + + @Mappings({ + @Mapping(source = "refund.transactionId", target = "transactionId"), + @Mapping(source = "refund.orderId", target = "orderId"), + @Mapping(source = "refund.refundId", target = "refundId"), + }) + PayRefundSuccessMessage convertRefund(PayNotifyTaskDO entity); - PayRefundSuccessMessage convertRefund(PayNotifyTaskDO payTransactionNotifyTaskDO); } diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/dal/mysql/dataobject/notify/PayNotifyTaskDO.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/dal/mysql/dataobject/notify/PayNotifyTaskDO.java index c41d4773c..7ed7879cc 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/dal/mysql/dataobject/notify/PayNotifyTaskDO.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/dal/mysql/dataobject/notify/PayNotifyTaskDO.java @@ -5,7 +5,6 @@ import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactio import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionExtensionDO; import cn.iocoder.mall.payservice.enums.notify.PayNotifyStatusEnum; import cn.iocoder.mall.payservice.enums.notify.PayNotifyType; -import cn.iocoder.mall.payservice.service.transaction.PayTransactionService; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; @@ -56,25 +55,18 @@ public class PayNotifyTaskDO extends DeletableDO { * 外键 {@link PayNotifyStatusEnum} */ private Integer status; + /** + * 是否激活中,即处于正在 MQ 异步通知中 + * + * @see cn.iocoder.mall.payservice.job.notify.PayNotifyRetryJob + */ + private Boolean active; /** * 下一次通知时间 */ private Date nextNotifyTime; /** * 最后一次执行时间 - * - * 这个字段,需要结合 {@link #nextNotifyTime} 一起使用。 - * - * 1. 初始时,{@link PayTransactionService#updateTransactionPaySuccess(Integer, String)} - * nextNotifyTime 为当前时间 + 15 秒 - * lastExecuteTime 为空 - * 并发送给 MQ ,执行执行 - * - * 2. MQ 消费时,更新 lastExecuteTime 为当时时间 - * - * 3. 定时任务,扫描 nextNotifyTime < lastExecuteTime 的任务 - * nextNotifyTime 为当前时间 + N 秒。具体的 N ,由第几次通知决定 - * lastExecuteTime 为当前时间 */ private Date lastExecuteTime; /** diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/dal/mysql/mapper/notify/PayNotifyTaskMapper.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/dal/mysql/mapper/notify/PayNotifyTaskMapper.java index b76fe2f80..5ab193beb 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/dal/mysql/mapper/notify/PayNotifyTaskMapper.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/dal/mysql/mapper/notify/PayNotifyTaskMapper.java @@ -16,16 +16,21 @@ public interface PayNotifyTaskMapper extends BaseMapper { * * 1. status 非成功 * 2. nextNotifyTime 小于当前时间 - * 3. lastExecuteTime > nextNotifyTime + * 3. active 为 false 并未正在执行中 * * @return PayTransactionNotifyTaskDO 数组 */ default List selectListByNotify() { return selectList(new QueryWrapper() - .in("status", PayNotifyStatusEnum.WAITING.getName(), PayNotifyStatusEnum.REQUEST_SUCCESS.getName(), - PayNotifyStatusEnum.REQUEST_FAILURE.getName()) + .in("status", PayNotifyStatusEnum.WAITING.getStatus(), PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus(), + PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()) .le("next_notify_time", "NOW()") - .gt("last_execute_time", "next_notify_time")); + .eq("active", Boolean.FALSE)); + } + + default int update(PayNotifyTaskDO update, Integer whereNotifyTimes) { + return update(update, new QueryWrapper() + .eq("id", update.getId()).eq("notify_times", whereNotifyTimes)); } // diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/job/notify/PayNotifyRetryJob.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/job/notify/PayNotifyRetryJob.java new file mode 100644 index 000000000..b792c9e06 --- /dev/null +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/job/notify/PayNotifyRetryJob.java @@ -0,0 +1,51 @@ +package cn.iocoder.mall.payservice.job.notify; + +import cn.iocoder.mall.payservice.dal.mysql.dataobject.notify.PayNotifyTaskDO; +import cn.iocoder.mall.payservice.dal.mysql.mapper.notify.PayNotifyTaskMapper; +import cn.iocoder.mall.payservice.service.notify.PayNotifyService; +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 支付通知重试 Job + * + * 由于 RocketMQ 不支持指定时间的延迟消息,所以我们需要通过 Job 扫描到达 {@link PayNotifyTaskDO#getNextNotifyTime()} 时间的任务。 + * 扫描到后,通过发送 MQ 去异步通知,提高通知效率。 + * + * 考虑到 MQ 执行可能存在延迟的情况,导致一个 {@link PayNotifyTaskDO} 同时触发多个通知,通过 {@link PayNotifyTaskDO#getActive()} 标记解决。 + */ +@Component +@Slf4j +public class PayNotifyRetryJob extends IJobHandler { + + @Autowired + private PayNotifyTaskMapper payNotifyTaskMapper; + + @Autowired + private PayNotifyService payNotifyService; + + @Override + @XxlJob("payNotifyRetryJob") + public ReturnT execute(String param) { + // 获得需要通知的任务 + List notifyTasks = payNotifyTaskMapper.selectListByNotify(); + + // 循环任务,发送通知 + for (PayNotifyTaskDO notifyTask : notifyTasks) { + // 发送 MQ + payNotifyService.sendNotifyMessage(notifyTask); + + // 标记任务执行中。考虑到 MQ 可能会存在先于该操作执行完,所以更新时,增加一个 notifyTimes 作为额外条件,避免覆盖更新的问题。 + PayNotifyTaskDO updateNotifyTask = new PayNotifyTaskDO().setId(notifyTask.getId()).setActive(true); + payNotifyTaskMapper.update(updateNotifyTask, notifyTask.getNotifyTimes()); + } + return new ReturnT<>("执行通知数:" + notifyTasks.size()); + } + +} diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/job/package-info.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/job/package-info.java new file mode 100644 index 000000000..29479eb7a --- /dev/null +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/job/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.mall.payservice.job; diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/AbstractPayNotifySuccessMQConsumer.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/AbstractPayNotifySuccessMQConsumer.java index 05f36f697..380f153fc 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/AbstractPayNotifySuccessMQConsumer.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/AbstractPayNotifySuccessMQConsumer.java @@ -2,6 +2,7 @@ package cn.iocoder.mall.payservice.mq.consumer; import cn.iocoder.common.framework.util.DateUtil; import cn.iocoder.common.framework.util.ExceptionUtil; +import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.payservice.common.dubbo.DubboReferencePool; import cn.iocoder.mall.payservice.dal.mysql.dataobject.notify.PayNotifyLogDO; import cn.iocoder.mall.payservice.dal.mysql.dataobject.notify.PayNotifyTaskDO; @@ -10,14 +11,15 @@ import cn.iocoder.mall.payservice.dal.mysql.mapper.notify.PayNotifyTaskMapper; import cn.iocoder.mall.payservice.enums.notify.PayNotifyStatusEnum; import cn.iocoder.mall.payservice.mq.producer.message.AbstractPayNotifySuccessMessage; import com.alibaba.fastjson.JSON; -import org.apache.rocketmq.spring.core.RocketMQListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import java.util.Calendar; import java.util.Date; +import java.util.Map; -public abstract class AbstractPayNotifySuccessMQConsumer implements RocketMQListener { +public abstract class AbstractPayNotifySuccessMQConsumer { +// implements RocketMQListener TODO 芋艿,理论来说,可以实现 RocketMQListener 接口,然后 execute 作为 onMessage 的具体实现。但是新版本貌似不行,后续在排查下; @Autowired private DubboReferencePool dubboReferencePool; @@ -27,21 +29,22 @@ public abstract class AbstractPayNotifySuccessMQConsumer invokeResult = null; // RPC / HTTP 调用的响应 + Throwable invokeException = null; // PayNotifyTaskDO updateTask = new PayNotifyTaskDO() // 更新 PayTransactionNotifyTaskDO 对象 .setId(message.getId()) + .setActive(false) // 标记本地通知已经完成 .setLastExecuteTime(new Date()) .setNotifyTimes(message.getNotifyTimes() + 1); try { + // 获得 ReferenceMeta 对象 + DubboReferencePool.ReferenceMeta referenceMeta = dubboReferencePool.getReferenceMeta(message.getNotifyUrl()); // TODO 芋艿,这里要优化下,不要在事务里,进行 RPC 调用 - response = invoke(message, referenceMeta); - if ("success".equals(response)) { // 情况一,请求成功且返回成功 + invokeResult = invoke(message, referenceMeta); + if (invokeResult.isSuccess()) { // 情况一,请求成功且返回成功 // 更新通知成功 updateTask.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()); payNotifyTaskMapper.updateById(updateTask); @@ -53,8 +56,8 @@ public abstract class AbstractPayNotifySuccessMQConsumer invoke(T message, DubboReferencePool.ReferenceMeta referenceMeta); protected abstract void afterInvokeSuccess(T message); + /** + * 将 Dubbo 泛化调用的结果,解析成 CommonResult + * + * 目前,约定 Dubbo 返回的结果为 CommonResult + * + * @param dubboResult Dubbo 调用结果 + * @return CommonResult 结果 + */ + protected static CommonResult parseDubboGenericResult(Object dubboResult) { + // TODO 芋艿,目前暂时这么实现,未来找下更合适的 + Map dubboResultMap = (Map) dubboResult; + CommonResult commonResult = new CommonResult<>(); + commonResult.setCode((Integer) dubboResultMap.get("code")); + commonResult.setMessage((String) dubboResultMap.get("message")); + commonResult.setData((Boolean) dubboResultMap.get("data")); + return commonResult; + } + } diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/PayRefundSuccessMQConsumer.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/PayRefundSuccessMQConsumer.java index 3f4737d25..9bb3ac769 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/PayRefundSuccessMQConsumer.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/PayRefundSuccessMQConsumer.java @@ -1,5 +1,6 @@ package cn.iocoder.mall.payservice.mq.consumer; +import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.payservice.common.dubbo.DubboReferencePool; import cn.iocoder.mall.payservice.dal.mysql.dataobject.refund.PayRefundDO; import cn.iocoder.mall.payservice.dal.mysql.mapper.refund.PayRefundMapper; @@ -25,15 +26,22 @@ public class PayRefundSuccessMQConsumer extends AbstractPayNotifySuccessMQConsum private PayRefundMapper payRefundMapper; @Override - protected String invoke(PayRefundSuccessMessage message, DubboReferencePool.ReferenceMeta referenceMeta) { + public void onMessage(PayRefundSuccessMessage message) { + super.execute(message); + } + + @Override + protected CommonResult invoke(PayRefundSuccessMessage message, DubboReferencePool.ReferenceMeta referenceMeta) { // 查询支付交易 PayRefundDO refund = payRefundMapper.selectById(message.getRefundId()); Assert.notNull(refund, String.format("回调消息(%s) 退款单不能为空", message.toString())); // 执行调用 GenericService genericService = referenceMeta.getService(); String methodName = referenceMeta.getMethodName(); - return (String) genericService.$invoke(methodName, new String[]{String.class.getName(), Integer.class.getName()}, + Object dubboResult = genericService.$invoke(methodName, + new String[]{String.class.getName(), Integer.class.getName()}, new Object[]{message.getOrderId(), refund.getPrice()}); + return parseDubboGenericResult(dubboResult); } @Override diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/PayTransactionSuccessMQConsumer.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/PayTransactionSuccessMQConsumer.java index f351df00b..eae9efbea 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/PayTransactionSuccessMQConsumer.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/consumer/PayTransactionSuccessMQConsumer.java @@ -1,5 +1,6 @@ package cn.iocoder.mall.payservice.mq.consumer; +import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.mall.payservice.common.dubbo.DubboReferencePool; import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionDO; import cn.iocoder.mall.payservice.dal.mysql.mapper.transaction.PayTransactionMapper; @@ -25,15 +26,22 @@ public class PayTransactionSuccessMQConsumer extends AbstractPayNotifySuccessMQC private PayTransactionMapper payTransactionMapper; @Override - protected String invoke(PayTransactionSuccessMessage message, DubboReferencePool.ReferenceMeta referenceMeta) { + public void onMessage(PayTransactionSuccessMessage message) { + super.execute(message); + } + + @Override + protected CommonResult invoke(PayTransactionSuccessMessage message, DubboReferencePool.ReferenceMeta referenceMeta) { // 查询支付交易 PayTransactionDO transaction = payTransactionMapper.selectById(message.getTransactionId()); Assert.notNull(transaction, String.format("回调消息(%s) 订单交易不能为空", message.toString())); // 执行调用 GenericService genericService = referenceMeta.getService(); String methodName = referenceMeta.getMethodName(); - return (String) genericService.$invoke(methodName, new String[]{String.class.getName(), Integer.class.getName()}, + Object dubboResult = genericService.$invoke(methodName, + new String[]{String.class.getName(), Integer.class.getName()}, new Object[]{message.getOrderId(), transaction.getPrice()}); + return parseDubboGenericResult(dubboResult); } @Override diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/producer/PayMQProducer.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/producer/PayMQProducer.java index 6e465f356..da4153c51 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/producer/PayMQProducer.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/mq/producer/PayMQProducer.java @@ -17,8 +17,7 @@ public class PayMQProducer { @Autowired private RocketMQTemplate template; - public void sendPayRefundNotifyTaskMessage(PayRefundSuccessMessage message, Integer refundId, Integer transactionId, String orderId) { - message.setRefundId(refundId).setTransactionId(transactionId).setOrderId(orderId); + public void sendPayRefundNotifyTaskMessage(PayRefundSuccessMessage message) { try { SendResult sendResult = template.syncSend(PayTransactionSuccessMessage.TOPIC, message); if (!SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { @@ -29,8 +28,7 @@ public class PayMQProducer { } } - public void sendPayTransactionNotifyTaskMessage(PayTransactionSuccessMessage message, Integer transactionId, String orderId) { - message.setTransactionId(transactionId).setOrderId(orderId); + public void sendPayTransactionNotifyTaskMessage(PayTransactionSuccessMessage message) { try { SendResult sendResult = template.syncSend(PayTransactionSuccessMessage.TOPIC, message); if (!SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/service/notify/PayNotifyService.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/service/notify/PayNotifyService.java index d74e6967a..a3ae5741f 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/service/notify/PayNotifyService.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/service/notify/PayNotifyService.java @@ -1,5 +1,6 @@ package cn.iocoder.mall.payservice.service.notify; +import cn.iocoder.mall.payservice.dal.mysql.dataobject.notify.PayNotifyTaskDO; import cn.iocoder.mall.payservice.dal.mysql.dataobject.refund.PayRefundDO; import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionDO; import cn.iocoder.mall.payservice.dal.mysql.dataobject.transaction.PayTransactionExtensionDO; @@ -15,4 +16,7 @@ public interface PayNotifyService { // TODO 芋艿:后续优化下,不要暴露 entity 出来 void addPayTransactionNotifyTask(PayTransactionDO transaction, PayTransactionExtensionDO extension); + // TODO 芋艿:后续优化下,不要暴露 entity 出来 + void sendNotifyMessage(PayNotifyTaskDO notifyTask); + } diff --git a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/service/notify/impl/PayNotifyServiceImpl.java b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/service/notify/impl/PayNotifyServiceImpl.java index c4f802ec5..2d4a0ac76 100644 --- a/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/service/notify/impl/PayNotifyServiceImpl.java +++ b/pay-service-project/pay-service-app/src/main/java/cn/iocoder/mall/payservice/service/notify/impl/PayNotifyServiceImpl.java @@ -39,8 +39,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { payNotifyTaskMapper.insert(payNotifyTaskDO); // 发送 MQ 消息 - payMQProducer.sendPayRefundNotifyTaskMessage(PayNotifyConvert.INSTANCE.convertRefund(payNotifyTaskDO), - refund.getId(), refund.getTransactionId(), refund.getOrderId()); + sendNotifyMessage(payNotifyTaskDO); } @Override @@ -54,14 +53,24 @@ public class PayNotifyServiceImpl implements PayNotifyService { payNotifyTaskMapper.insert(payNotifyTaskDO); // 发送 MQ 消息 - payMQProducer.sendPayTransactionNotifyTaskMessage(PayNotifyConvert.INSTANCE.convertTransaction(payNotifyTaskDO), - transaction.getId(), transaction.getOrderId()); + sendNotifyMessage(payNotifyTaskDO); + } + + @Override + public void sendNotifyMessage(PayNotifyTaskDO notifyTask) { + if (PayNotifyType.TRANSACTION.getType().equals(notifyTask.getType())) { + payMQProducer.sendPayTransactionNotifyTaskMessage(PayNotifyConvert.INSTANCE.convertTransaction(notifyTask)); + } else if (PayNotifyType.REFUND.getType().equals(notifyTask.getType())) { + payMQProducer.sendPayRefundNotifyTaskMessage(PayNotifyConvert.INSTANCE.convertRefund(notifyTask)); + } else { + throw new IllegalArgumentException(String.format("通知任务(%s) 无法发送通知消息", notifyTask.toString())); + } } private PayNotifyTaskDO createBasePayNotifyTaskDO(String appId, String notifyUrl) { return new PayNotifyTaskDO() .setAppId(appId) - .setStatus(PayNotifyStatusEnum.WAITING.getStatus()) + .setStatus(PayNotifyStatusEnum.WAITING.getStatus()).setActive(true) .setNotifyTimes(0).setMaxNotifyTimes(PayNotifyTaskDO.NOTIFY_FREQUENCY.length + 1) .setNextNotifyTime(DateUtil.addDate(Calendar.SECOND, PayNotifyTaskDO.NOTIFY_FREQUENCY[0])) .setNotifyUrl(notifyUrl); diff --git a/pay-service-project/pay-service-app/src/main/resources/application-dev.yaml b/pay-service-project/pay-service-app/src/main/resources/application-dev.yaml index c9c18fe8e..c7b4acbb6 100644 --- a/pay-service-project/pay-service-app/src/main/resources/application-dev.yaml +++ b/pay-service-project/pay-service-app/src/main/resources/application-dev.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_pay?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_pay?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh @@ -19,3 +19,13 @@ dubbo: registry: # address: spring-cloud://400-infra.server.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址 address: nacos://400-infra.server.iocoder.cn:8848?namespace=dev # 指定 Dubbo 服务注册中心的地址 + +# XXL-Job 配置项 +xxl: + job: + admin: + addresses: http://127.0.0.1:9099/ + executor: + appname: ${spring.application.name} + logpath: /data/applogs/xxl-job/ + accessToken: diff --git a/pay-service-project/pay-service-app/src/main/resources/application-local.yaml b/pay-service-project/pay-service-app/src/main/resources/application-local.yaml index d287986cb..b57d1302f 100644 --- a/pay-service-project/pay-service-app/src/main/resources/application-local.yaml +++ b/pay-service-project/pay-service-app/src/main/resources/application-local.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_pay?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_pay?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh @@ -22,3 +22,19 @@ dubbo: # Dubbo 服务提供者的配置 provider: tag: ${DUBBO_TAG} # Dubbo 路由分组 + +# XXL-Job 配置项 +xxl: + job: + enabled: false # 本地开发时,关闭 XXL-Job + admin: + addresses: http://400-infra.server.iocoder.cn:9099 + executor: + appname: ${spring.application.name} + accessToken: + + +# MyBatis Plus 配置 +mybatis-plus: + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 本地开发环境下,多打印 SQL 到控制台 diff --git a/pay-service-project/pay-service-integration-test/src/test/java/cn/iocoder/mall/payservice/common/dubbo/DubboGenericInvokerTest.java b/pay-service-project/pay-service-integration-test/src/test/java/cn/iocoder/mall/payservice/common/dubbo/DubboGenericInvokerTest.java new file mode 100644 index 000000000..878c72956 --- /dev/null +++ b/pay-service-project/pay-service-integration-test/src/test/java/cn/iocoder/mall/payservice/common/dubbo/DubboGenericInvokerTest.java @@ -0,0 +1,49 @@ +package cn.iocoder.mall.payservice.common.dubbo; + +import cn.iocoder.common.framework.vo.CommonResult; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.rpc.service.GenericService; + +import java.util.Map; + +public class DubboGenericInvokerTest { + + public static void main(String[] args) { + ApplicationConfig application = new ApplicationConfig(); + application.setName("api-generic-consumer"); + + RegistryConfig registry = new RegistryConfig(); + registry.setAddress("nacos://400-infra.server.iocoder.cn:8848?namespace=dev"); + + application.setRegistry(registry); + + ReferenceConfig reference = new ReferenceConfig<>(); + // 弱类型接口名 + reference.setInterface("cn.iocoder.mall.tradeservice.rpc.order.TradeOrderRpc"); + reference.setVersion("1.0.0"); + // 声明为泛化接口 + reference.setGeneric(true); + + reference.setApplication(application); + + // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用 + GenericService genericService = reference.get(); + + Object result = genericService.$invoke("updateTradeOrderPaySuccess", + new String[]{String.class.getName(), Integer.class.getName()}, + new Object[]{"1", 100}); + CommonResult commonResult = parseCommonResult((Map) result); + System.out.println(result); + } + + private static CommonResult parseCommonResult(Map dubboResult) { + CommonResult commonResult = new CommonResult<>(); + commonResult.setCode((Integer) dubboResult.get("code")); + commonResult.setMessage((String) dubboResult.get("message")); + commonResult.setData((Boolean) dubboResult.get("data")); + return commonResult; + } + +} diff --git a/pay-service-project/pay-service-integration-test/src/test/java/cn/iocoder/mall/payservice/common/package-info.java b/pay-service-project/pay-service-integration-test/src/test/java/cn/iocoder/mall/payservice/common/package-info.java new file mode 100644 index 000000000..ed83cb4e9 --- /dev/null +++ b/pay-service-project/pay-service-integration-test/src/test/java/cn/iocoder/mall/payservice/common/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.mall.payservice.common; diff --git a/product-service-project/product-service-app/src/main/resources/application-dev.yaml b/product-service-project/product-service-app/src/main/resources/application-dev.yaml index dea36f740..8d210f360 100644 --- a/product-service-project/product-service-app/src/main/resources/application-dev.yaml +++ b/product-service-project/product-service-app/src/main/resources/application-dev.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh diff --git a/product-service-project/product-service-app/src/main/resources/application-local.yaml b/product-service-project/product-service-app/src/main/resources/application-local.yaml index 47a56028a..099913a16 100644 --- a/product-service-project/product-service-app/src/main/resources/application-local.yaml +++ b/product-service-project/product-service-app/src/main/resources/application-local.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh diff --git a/promotion-service-project/promotion-service-app/src/main/resources/application-dev.yaml b/promotion-service-project/promotion-service-app/src/main/resources/application-dev.yaml index 3a20078ed..99a349fac 100644 --- a/promotion-service-project/promotion-service-app/src/main/resources/application-dev.yaml +++ b/promotion-service-project/promotion-service-app/src/main/resources/application-dev.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_promotion?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_promotion?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh diff --git a/promotion-service-project/promotion-service-app/src/main/resources/application-local.yaml b/promotion-service-project/promotion-service-app/src/main/resources/application-local.yaml index 332deef02..19e0f347a 100644 --- a/promotion-service-project/promotion-service-app/src/main/resources/application-local.yaml +++ b/promotion-service-project/promotion-service-app/src/main/resources/application-local.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_promotion?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_promotion?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh diff --git a/system-service-project/system-service-app/src/main/resources/application-dev.yaml b/system-service-project/system-service-app/src/main/resources/application-dev.yaml index fd03a51f3..2214910e4 100644 --- a/system-service-project/system-service-app/src/main/resources/application-dev.yaml +++ b/system-service-project/system-service-app/src/main/resources/application-dev.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_system?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_system?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh diff --git a/system-service-project/system-service-app/src/main/resources/application-local.yaml b/system-service-project/system-service-app/src/main/resources/application-local.yaml index 1b5420bef..b0bbbf8f9 100644 --- a/system-service-project/system-service-app/src/main/resources/application-local.yaml +++ b/system-service-project/system-service-app/src/main/resources/application-local.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_system?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_system?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh diff --git a/trade-service-project/trade-service-api/src/main/java/cn/iocoder/mall/tradeservice/rpc/order/TradeOrderRpc.java b/trade-service-project/trade-service-api/src/main/java/cn/iocoder/mall/tradeservice/rpc/order/TradeOrderRpc.java index 14358aef1..fddd76fdf 100644 --- a/trade-service-project/trade-service-api/src/main/java/cn/iocoder/mall/tradeservice/rpc/order/TradeOrderRpc.java +++ b/trade-service-project/trade-service-api/src/main/java/cn/iocoder/mall/tradeservice/rpc/order/TradeOrderRpc.java @@ -38,6 +38,17 @@ public interface TradeOrderRpc { */ CommonResult> pageTradeOrder(TradeOrderPageReqDTO pageDTO); + // TODO 芋艿:需要重构成入参是 DTO,方便后续升级;返回是 CommonResult,用于返回失败的原因 + /** + * 更新交易订单支付成功 + * + * 目前用于对接 pay-service 支付服务,回调该交易订单在三方支付平台,支付成功 + * + * @param tradeOrderId 交易订单编号 + * @param payAmount 支付金额 + * @return 成功 + */ + CommonResult updateTradeOrderPaySuccess(String tradeOrderId, Integer payAmount); } diff --git a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/dal/mysql/mapper/order/TradeOrderItemMapper.java b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/dal/mysql/mapper/order/TradeOrderItemMapper.java index 3784500d1..0975b87f5 100644 --- a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/dal/mysql/mapper/order/TradeOrderItemMapper.java +++ b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/dal/mysql/mapper/order/TradeOrderItemMapper.java @@ -20,4 +20,9 @@ public interface TradeOrderItemMapper extends BaseMapper { return selectList(new QueryWrapper().in("order_id", orderIds)); } + default int updateListByOrderId(TradeOrderItemDO update, Integer orderId, Integer whereStatus) { + return update(update, new QueryWrapper().eq("order_id", orderId) + .eq("status", whereStatus)); + } + } diff --git a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/dal/mysql/mapper/order/TradeOrderMapper.java b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/dal/mysql/mapper/order/TradeOrderMapper.java index a0423cf24..924f4d7d4 100644 --- a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/dal/mysql/mapper/order/TradeOrderMapper.java +++ b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/dal/mysql/mapper/order/TradeOrderMapper.java @@ -4,6 +4,7 @@ import cn.iocoder.mall.mybatis.core.query.QueryWrapperX; import cn.iocoder.mall.mybatis.core.util.PageUtil; import cn.iocoder.mall.tradeservice.dal.mysql.dataobject.order.TradeOrderDO; import cn.iocoder.mall.tradeservice.rpc.order.dto.TradeOrderPageReqDTO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import org.springframework.stereotype.Repository; @@ -17,4 +18,9 @@ public interface TradeOrderMapper extends BaseMapper { .eqIfPresent("status", pageReqDTO.getOrderStatus())); } + default int update(TradeOrderDO update, Integer whereOrderStatus) { + return update(update, new QueryWrapper() + .eq("id", update.getId()).eq("order_status", whereOrderStatus)); + } + } diff --git a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/rpc/order/TradeOrderRpcImpl.java b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/rpc/order/TradeOrderRpcImpl.java index 49958df05..f9b3a9e30 100644 --- a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/rpc/order/TradeOrderRpcImpl.java +++ b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/rpc/order/TradeOrderRpcImpl.java @@ -37,4 +37,10 @@ public class TradeOrderRpcImpl implements TradeOrderRpc { return success(tradeOrderService.pageTradeOrder(pageDTO)); } + @Override + public CommonResult updateTradeOrderPaySuccess(String tradeOrderId, Integer payAmount) { + tradeOrderService.updateTradeOrderPaySuccess(Integer.valueOf(tradeOrderId), payAmount); + return success(true); + } + } diff --git a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/service/order/TradeOrderService.java b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/service/order/TradeOrderService.java index 587c1a3ce..468f5e27b 100644 --- a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/service/order/TradeOrderService.java +++ b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/service/order/TradeOrderService.java @@ -37,4 +37,12 @@ public interface TradeOrderService { */ PageResult pageTradeOrder(TradeOrderPageReqDTO pageReqDTO); + /** + * 更新交易订单支付成功 + * + * @param tradeOrderId 交易订单编号 + * @param payAmount 支付金额 + */ + void updateTradeOrderPaySuccess(Integer tradeOrderId, Integer payAmount); + } diff --git a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/service/order/impl/TradeOrderServiceImpl.java b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/service/order/impl/TradeOrderServiceImpl.java index be7f18e53..f9eaaf24d 100644 --- a/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/service/order/impl/TradeOrderServiceImpl.java +++ b/trade-service-project/trade-service-app/src/main/java/cn/iocoder/mall/tradeservice/service/order/impl/TradeOrderServiceImpl.java @@ -39,7 +39,7 @@ import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.common.framework.util.CollectionUtils.convertSet; -import static cn.iocoder.mall.tradeservice.enums.OrderErrorCodeConstants.ORDER_GET_GOODS_INFO_INCORRECT; +import static cn.iocoder.mall.tradeservice.enums.OrderErrorCodeConstants.*; import static cn.iocoder.mall.userservice.enums.UserErrorCodeConstants.USER_ADDRESS_NOT_FOUND; /** @@ -240,4 +240,40 @@ public class TradeOrderServiceImpl implements TradeOrderService { return pageResult; } + + @Override + @Transactional + public void updateTradeOrderPaySuccess(Integer tradeOrderId, Integer payAmount) { +// if (true) { +// throw new IllegalArgumentException("测试失败的情况"); +// } + // 校验交易订单,是否可以 + TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(tradeOrderId); + if (tradeOrderDO == null) { // 订单不存在 + throw ServiceExceptionUtil.exception(ORDER_NOT_EXISTENT); + } + if (!tradeOrderDO.getOrderStatus().equals(TradeOrderStatusEnum.WAITING_PAYMENT.getValue())) { // 状态不处于等待支付 + throw ServiceExceptionUtil.exception(ORDER_STATUS_NOT_WAITING_PAYMENT); + } + if (!tradeOrderDO.getPresentPrice().equals(payAmount)) { // 支付金额不正确 + throw ServiceExceptionUtil.exception(ORDER_PAY_AMOUNT_ERROR); + } + + // 更新 TradeOrderDO 状态为已支付,等待发货 + TradeOrderDO updateOrderObj = new TradeOrderDO().setId(tradeOrderId) + .setOrderStatus(TradeOrderStatusEnum.WAIT_SHIPMENT.getValue()) + .setPayPrice(payAmount) + .setPayTime(new Date()); + int updateCount = tradeOrderMapper.update(updateOrderObj, TradeOrderStatusEnum.WAITING_PAYMENT.getValue()); + if (updateCount <= 0) { + throw ServiceExceptionUtil.exception(ORDER_STATUS_NOT_WAITING_PAYMENT); + } + + // 更新 TradeOrderItemDO 状态为已支付,等待发货 + TradeOrderItemDO updateOrderItemObj = new TradeOrderItemDO() + .setStatus(TradeOrderStatusEnum.WAIT_SHIPMENT.getValue()); + tradeOrderItemMapper.updateListByOrderId(updateOrderItemObj, tradeOrderId, + TradeOrderStatusEnum.WAITING_PAYMENT.getValue()); + } + } diff --git a/trade-service-project/trade-service-app/src/main/resources/application-dev.yaml b/trade-service-project/trade-service-app/src/main/resources/application-dev.yaml index c0d071890..6359e3363 100644 --- a/trade-service-project/trade-service-app/src/main/resources/application-dev.yaml +++ b/trade-service-project/trade-service-app/src/main/resources/application-dev.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_trade?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_trade?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh diff --git a/trade-service-project/trade-service-app/src/main/resources/application-local.yaml b/trade-service-project/trade-service-app/src/main/resources/application-local.yaml index d3b714087..da9304ac3 100644 --- a/trade-service-project/trade-service-app/src/main/resources/application-local.yaml +++ b/trade-service-project/trade-service-app/src/main/resources/application-local.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_trade?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_trade?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh @@ -25,3 +25,8 @@ dubbo: # Dubbo 服务提供者的配置 provider: tag: ${DUBBO_TAG} # Dubbo 路由分组 + +# MyBatis Plus 配置 +mybatis-plus: + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 本地开发环境下,多打印 SQL 到控制台 diff --git a/user-service-project/user-service-app/src/main/resources/application-dev.yaml b/user-service-project/user-service-app/src/main/resources/application-dev.yaml index b366c798b..e08317307 100644 --- a/user-service-project/user-service-app/src/main/resources/application-dev.yaml +++ b/user-service-project/user-service-app/src/main/resources/application-dev.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh diff --git a/user-service-project/user-service-app/src/main/resources/application-local.yaml b/user-service-project/user-service-app/src/main/resources/application-local.yaml index 0e29f2d56..6a89f36d2 100644 --- a/user-service-project/user-service-app/src/main/resources/application-local.yaml +++ b/user-service-project/user-service-app/src/main/resources/application-local.yaml @@ -1,7 +1,7 @@ spring: # 数据源配置项 datasource: - url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT driver-class-name: com.mysql.jdbc.Driver username: root password: 3WLiVUBEwTbvAfsh