Commit e96422a47eaf68d0346b843d2939dee091635c7a
1 parent
1cd9085c
trade:完成支付成功后的回调,更新订单为已支付
Showing
16 changed files
with
267 additions
and
43 deletions
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
| ... | ... | @@ -20,8 +20,12 @@ public interface ErrorCodeConstants { |
| 20 | 20 | ErrorCode ORDER_CREATE_ADDRESS_NOT_FOUND = new ErrorCode(1011000006, "收货地址不存在"); |
| 21 | 21 | |
| 22 | 22 | ErrorCode ORDER_ITEM_NOT_FOUND = new ErrorCode(1011000010, "交易订单项不存在"); |
| 23 | - ErrorCode ORDER_NOT_FOUND = new ErrorCode(1011000010, "交易订单不存在"); | |
| 24 | - ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1011000011, "交易订单项更新售后状态失败,请重试"); | |
| 23 | + ErrorCode ORDER_NOT_FOUND = new ErrorCode(1011000011, "交易订单不存在"); | |
| 24 | + ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1011000012, "交易订单项更新售后状态失败,请重试"); | |
| 25 | + ErrorCode ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1011000013, "交易订单更新支付状态失败,订单不是【未支付】状态"); | |
| 26 | + ErrorCode ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1011000014, "交易订单更新支付状态失败,支付单编号不匹配"); | |
| 27 | + ErrorCode ORDER_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1011000015, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态"); | |
| 28 | + ErrorCode ORDER_UPDATE_PAID_PAY_PRICE_NOT_MATCH = new ErrorCode(1011000016, "交易订单更新支付状态失败,支付单金额不匹配"); | |
| 25 | 29 | |
| 26 | 30 | // ========== After Sale 模块 1-011-000-000 ========== |
| 27 | 31 | ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在"); | ... | ... |
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java
| ... | ... | @@ -42,6 +42,19 @@ public enum TradeOrderStatusEnum implements IntArrayValuable { |
| 42 | 42 | return ARRAYS; |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | + // ========== 问:为什么写了很多 isXXX 和 haveXXX 的判断逻辑呢? ========== | |
| 46 | + // ========== 答:方便找到某一类判断,哪些业务正在使用 ========== | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * 判断指定状态,是否正处于【未付款】状态 | |
| 50 | + * | |
| 51 | + * @param status 指定状态 | |
| 52 | + * @return 是否 | |
| 53 | + */ | |
| 54 | + public static boolean isUnpaid(Integer status) { | |
| 55 | + return ObjectUtil.equal(PAID.getStatus(), status); | |
| 56 | + } | |
| 57 | + | |
| 45 | 58 | /** |
| 46 | 59 | * 判断指定状态,是否正处于【已取消】状态 |
| 47 | 60 | * | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
| ... | ... | @@ -21,6 +21,8 @@ import org.springframework.web.bind.annotation.*; |
| 21 | 21 | import javax.annotation.Resource; |
| 22 | 22 | import javax.servlet.http.HttpServletRequest; |
| 23 | 23 | |
| 24 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 25 | + | |
| 24 | 26 | @Api(tags = "用户 App - 交易订单") |
| 25 | 27 | @RestController |
| 26 | 28 | @RequestMapping("/trade/order") |
| ... | ... | @@ -49,13 +51,15 @@ public class AppTradeOrderController { |
| 49 | 51 | String clientIp = ServletUtil.getClientIP(servletRequest); |
| 50 | 52 | // 创建交易订单,预支付记录 |
| 51 | 53 | Long orderId = tradeOrderService.createOrder(loginUserId, clientIp, createReqVO); |
| 52 | - return CommonResult.success(orderId); | |
| 54 | + return success(orderId); | |
| 53 | 55 | } |
| 54 | 56 | |
| 55 | 57 | @PostMapping("/update-paid") |
| 56 | 58 | @ApiOperation(value = "更新订单为已支付", notes = "由 pay-module 支付服务,进行回调,可见 PayNotifyJob") |
| 57 | 59 | public CommonResult<Boolean> updateOrderPaid(@RequestBody PayOrderNotifyReqDTO notifyReqDTO) { |
| 58 | - return null; | |
| 60 | + tradeOrderService.updateOrderPaid(Long.valueOf(notifyReqDTO.getMerchantOrderId()), | |
| 61 | + notifyReqDTO.getPayOrderId()); | |
| 62 | + return success(true); | |
| 59 | 63 | } |
| 60 | 64 | |
| 61 | 65 | @GetMapping("/get") | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
| ... | ... | @@ -95,6 +95,13 @@ public class TradeOrderDO extends BaseDO { |
| 95 | 95 | private String remark; |
| 96 | 96 | |
| 97 | 97 | // ========== 价格 + 支付基本信息 ========== |
| 98 | + | |
| 99 | + /** | |
| 100 | + * 支付订单编号 | |
| 101 | + * | |
| 102 | + * 对接 pay-module-biz 支付服务的支付订单编号,即 PayOrderDO 的 id 编号 | |
| 103 | + */ | |
| 104 | + private Long payOrderId; | |
| 98 | 105 | /** |
| 99 | 106 | * 是否已支付 |
| 100 | 107 | * |
| ... | ... | @@ -106,6 +113,12 @@ public class TradeOrderDO extends BaseDO { |
| 106 | 113 | * 付款时间 |
| 107 | 114 | */ |
| 108 | 115 | private LocalDateTime payTime; |
| 116 | + /** | |
| 117 | + * 支付成功的支付渠道 | |
| 118 | + * | |
| 119 | + * 对应 PayChannelEnum 枚举 | |
| 120 | + */ | |
| 121 | + private String payChannelCode; | |
| 109 | 122 | |
| 110 | 123 | // ========== 价格 + 支付基本信息 ========== |
| 111 | 124 | // 价格文档 - 淘宝:https://open.taobao.com/docV3.htm?docId=108471&docType=1 |
| ... | ... | @@ -156,18 +169,6 @@ public class TradeOrderDO extends BaseDO { |
| 156 | 169 | * + {@link #adjustPrice} |
| 157 | 170 | */ |
| 158 | 171 | private Integer payPrice; |
| 159 | - /** | |
| 160 | - * 支付订单编号 | |
| 161 | - * | |
| 162 | - * 对接 pay-module-biz 支付服务的支付订单编号,即 PayOrderDO 的 id 编号 | |
| 163 | - */ | |
| 164 | - private Long payOrderId; | |
| 165 | - /** | |
| 166 | - * 支付成功的支付渠道 | |
| 167 | - * | |
| 168 | - * 对应 PayChannelEnum 枚举 | |
| 169 | - */ | |
| 170 | - private Integer payChannel; | |
| 171 | 172 | |
| 172 | 173 | // ========== 收件 + 物流基本信息 ========== |
| 173 | 174 | /** |
| ... | ... | @@ -206,7 +207,7 @@ public class TradeOrderDO extends BaseDO { |
| 206 | 207 | /** |
| 207 | 208 | * 收件人地区编号 |
| 208 | 209 | */ |
| 209 | - private Long receiverAreaId; | |
| 210 | + private Integer receiverAreaId; | |
| 210 | 211 | /** |
| 211 | 212 | * 收件人邮编 |
| 212 | 213 | */ | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java
| ... | ... | @@ -2,8 +2,15 @@ package cn.iocoder.yudao.module.trade.dal.mysql.order; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 4 | 4 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 5 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 5 | 6 | import org.apache.ibatis.annotations.Mapper; |
| 6 | 7 | |
| 7 | 8 | @Mapper |
| 8 | 9 | public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> { |
| 10 | + | |
| 11 | + default int updateByIdAndStatus(Long id, Integer status, TradeOrderDO update) { | |
| 12 | + return update(update, new LambdaUpdateWrapper<TradeOrderDO>() | |
| 13 | + .eq(TradeOrderDO::getId, id).eq(TradeOrderDO::getStatus, status)); | |
| 14 | + } | |
| 15 | + | |
| 9 | 16 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java
| ... | ... | @@ -33,6 +33,14 @@ public interface TradeOrderService { |
| 33 | 33 | */ |
| 34 | 34 | TradeOrderDO getOrder(Long userId, Long orderId); |
| 35 | 35 | |
| 36 | + /** | |
| 37 | + * 更新交易订单已支付 | |
| 38 | + * | |
| 39 | + * @param id 交易订单编号 | |
| 40 | + * @param payOrderId 支付订单编号 | |
| 41 | + */ | |
| 42 | + void updateOrderPaid(Long id, Long payOrderId); | |
| 43 | + | |
| 36 | 44 | // =================== Order Item =================== |
| 37 | 45 | |
| 38 | 46 | /** |
| ... | ... | @@ -54,4 +62,5 @@ public interface TradeOrderService { |
| 54 | 62 | */ |
| 55 | 63 | void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, |
| 56 | 64 | Integer newAfterSaleStatus, Integer refundPrice); |
| 65 | + | |
| 57 | 66 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
| ... | ... | @@ -3,13 +3,17 @@ package cn.iocoder.yudao.module.trade.service.order; |
| 3 | 3 | import cn.hutool.core.util.IdUtil; |
| 4 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 5 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.iocoder.yudao.framework.common.core.KeyValue; | |
| 6 | 7 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.enums.TerminalEnum; |
| 8 | 9 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 10 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 9 | 11 | import cn.iocoder.yudao.module.member.api.address.AddressApi; |
| 10 | 12 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; |
| 11 | 13 | import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; |
| 12 | 14 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; |
| 15 | +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | |
| 16 | +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; | |
| 13 | 17 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; |
| 14 | 18 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 15 | 19 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; |
| ... | ... | @@ -30,6 +34,7 @@ import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; |
| 30 | 34 | import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants; |
| 31 | 35 | import cn.iocoder.yudao.module.trade.enums.order.*; |
| 32 | 36 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; |
| 37 | +import lombok.extern.slf4j.Slf4j; | |
| 33 | 38 | import org.springframework.stereotype.Service; |
| 34 | 39 | import org.springframework.transaction.annotation.Transactional; |
| 35 | 40 | |
| ... | ... | @@ -42,6 +47,7 @@ import java.util.Set; |
| 42 | 47 | |
| 43 | 48 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 44 | 49 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 50 | +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_ORDER_NOT_FOUND; | |
| 45 | 51 | import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; |
| 46 | 52 | |
| 47 | 53 | /** |
| ... | ... | @@ -51,6 +57,7 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; |
| 51 | 57 | * @since 2022-08-26 |
| 52 | 58 | */ |
| 53 | 59 | @Service |
| 60 | +@Slf4j | |
| 54 | 61 | public class TradeOrderServiceImpl implements TradeOrderService { |
| 55 | 62 | |
| 56 | 63 | @Resource |
| ... | ... | @@ -110,6 +117,84 @@ public class TradeOrderServiceImpl implements TradeOrderService { |
| 110 | 117 | return order; |
| 111 | 118 | } |
| 112 | 119 | |
| 120 | + @Override | |
| 121 | + public void updateOrderPaid(Long id, Long payOrderId) { | |
| 122 | + // 校验并获得交易订单 | |
| 123 | + KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPaid(id, payOrderId); | |
| 124 | + TradeOrderDO order = orderResult.getKey(); | |
| 125 | + PayOrderRespDTO payOrder = orderResult.getValue(); | |
| 126 | + | |
| 127 | + // 更新 TradeOrderDO 状态为已支付,等待发货 | |
| 128 | + int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(), | |
| 129 | + new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayed(true) | |
| 130 | + .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode())); | |
| 131 | + if (updateCount == 0) { | |
| 132 | + throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); | |
| 133 | + } | |
| 134 | + | |
| 135 | + // TODO 芋艿:发送订单变化的消息 | |
| 136 | + | |
| 137 | + // TODO 芋艿:发送站内信 | |
| 138 | + | |
| 139 | + // TODO 芋艿:OrderLog | |
| 140 | + } | |
| 141 | + | |
| 142 | + /** | |
| 143 | + * 校验交易订单满足被支付的条件 | |
| 144 | + * | |
| 145 | + * 1. 交易订单未支付 | |
| 146 | + * 2. 支付单已支付 | |
| 147 | + * | |
| 148 | + * @param id 交易订单编号 | |
| 149 | + * @param payOrderId 支付订单编号 | |
| 150 | + * @return 交易订单 | |
| 151 | + */ | |
| 152 | + private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPaid(Long id, Long payOrderId) { | |
| 153 | + // 校验订单是否存在 | |
| 154 | + TradeOrderDO order = tradeOrderMapper.selectById(id); | |
| 155 | + if (order == null) { | |
| 156 | + throw exception(ORDER_NOT_FOUND); | |
| 157 | + } | |
| 158 | + // 校验订单未支付 | |
| 159 | + if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) { // 状态 | |
| 160 | + log.error("[validateOrderPaid][order({}) 不处于待支付状态,请进行处理!order 数据是:{}]", | |
| 161 | + id, JsonUtils.toJsonString(order)); | |
| 162 | + throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); | |
| 163 | + } | |
| 164 | + // 校验支付订单匹配 | |
| 165 | + if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号 | |
| 166 | + log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]", | |
| 167 | + id, payOrderId, JsonUtils.toJsonString(order)); | |
| 168 | + throw exception(ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR); | |
| 169 | + } | |
| 170 | + | |
| 171 | + // 校验支付单是否存在 | |
| 172 | + PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId); | |
| 173 | + if (payOrder == null) { | |
| 174 | + log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId); | |
| 175 | + throw exception(PAY_ORDER_NOT_FOUND); | |
| 176 | + } | |
| 177 | + // 校验支付单已支付 | |
| 178 | + if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) { | |
| 179 | + log.error("[validateOrderPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]", | |
| 180 | + id, payOrderId, JsonUtils.toJsonString(payOrder)); | |
| 181 | + throw exception(ORDER_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS); | |
| 182 | + } | |
| 183 | + // 校验支付金额一致 | |
| 184 | + if (ObjectUtil.notEqual(payOrder.getAmount(), order.getPayPrice())) { | |
| 185 | + log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]", | |
| 186 | + id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder)); | |
| 187 | + throw exception(ORDER_UPDATE_PAID_PAY_PRICE_NOT_MATCH); | |
| 188 | + } | |
| 189 | + // 校验支付订单匹配(二次) | |
| 190 | + if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) { | |
| 191 | + log.error("[validateOrderPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]", | |
| 192 | + id, payOrderId, JsonUtils.toJsonString(payOrder)); | |
| 193 | + throw exception(ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR); | |
| 194 | + } | |
| 195 | + return new KeyValue<>(order, payOrder); | |
| 196 | + } | |
| 197 | + | |
| 113 | 198 | /** |
| 114 | 199 | * 校验商品 SKU 是否可出售 |
| 115 | 200 | * |
| ... | ... | @@ -120,7 +205,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { |
| 120 | 205 | List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(convertSet(items, Item::getSkuId)); |
| 121 | 206 | // SKU 不存在 |
| 122 | 207 | if (items.size() != skus.size()) { |
| 123 | - throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_NOT_FOUND); | |
| 208 | + throw exception(ORDER_CREATE_SKU_NOT_FOUND); | |
| 124 | 209 | } |
| 125 | 210 | // 校验是否禁用 or 库存不足 |
| 126 | 211 | Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId); |
| ... | ... | @@ -233,13 +318,15 @@ public class TradeOrderServiceImpl implements TradeOrderService { |
| 233 | 318 | // 创建支付单,用于后续的支付 |
| 234 | 319 | PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert( |
| 235 | 320 | tradeOrderDO, tradeOrderItemDOs, spus, tradeOrderProperties); |
| 236 | - Long payOrderId = payOrderApi.createPayOrder(payOrderCreateReqDTO); | |
| 321 | + Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO); | |
| 237 | 322 | |
| 238 | 323 | // 更新到交易单上 |
| 239 | 324 | tradeOrderMapper.updateById(new TradeOrderDO().setId(tradeOrderDO.getId()).setPayOrderId(payOrderId)); |
| 240 | 325 | } |
| 241 | 326 | |
| 242 | - // =================== Order =================== | |
| 327 | + | |
| 328 | + | |
| 329 | + // =================== Order Item =================== | |
| 243 | 330 | |
| 244 | 331 | @Override |
| 245 | 332 | public TradeOrderItemDO getOrderItem(Long userId, Long itemId) { | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java
| ... | ... | @@ -6,6 +6,8 @@ import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 6 | 6 | import cn.iocoder.yudao.module.member.api.address.AddressApi; |
| 7 | 7 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; |
| 8 | 8 | import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; |
| 9 | +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | |
| 10 | +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; | |
| 9 | 11 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; |
| 10 | 12 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 11 | 13 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; |
| ... | ... | @@ -109,7 +111,7 @@ public class TradeOrderServiceTest extends BaseDbUnitTest { |
| 109 | 111 | // mock 方法(用户收件地址的校验) |
| 110 | 112 | AddressRespDTO addressRespDTO = new AddressRespDTO().setId(10L).setUserId(userId).setName("芋艿") |
| 111 | 113 | .setMobile("15601691300").setAreaId(3306L).setPostCode("85757").setDetailAddress("土豆村"); |
| 112 | - when(addressApi.getAddress(eq(userId), eq(10L))).thenReturn(addressRespDTO); | |
| 114 | + when(addressApi.getAddress(eq(10L), eq(userId))).thenReturn(addressRespDTO); | |
| 113 | 115 | // mock 方法(价格计算) |
| 114 | 116 | PriceCalculateRespDTO.OrderItem priceOrderItem01 = new PriceCalculateRespDTO.OrderItem() |
| 115 | 117 | .setSpuId(11L).setSkuId(1L).setCount(3).setOriginalPrice(150).setOriginalUnitPrice(50) |
| ... | ... | @@ -131,7 +133,7 @@ public class TradeOrderServiceTest extends BaseDbUnitTest { |
| 131 | 133 | return true; |
| 132 | 134 | }))).thenReturn(new PriceCalculateRespDTO().setOrder(priceOrder)); |
| 133 | 135 | // mock 方法(创建支付单) |
| 134 | - when(payOrderApi.createPayOrder(argThat(createReqDTO -> { | |
| 136 | + when(payOrderApi.createOrder(argThat(createReqDTO -> { | |
| 135 | 137 | assertEquals(createReqDTO.getAppId(), 888L); |
| 136 | 138 | assertEquals(createReqDTO.getUserIp(), userIp); |
| 137 | 139 | assertNotNull(createReqDTO.getMerchantOrderId()); // 由于 tradeOrderId 后生成,只能校验非空 |
| ... | ... | @@ -169,7 +171,7 @@ public class TradeOrderServiceTest extends BaseDbUnitTest { |
| 169 | 171 | assertEquals(tradeOrderDO.getAdjustPrice(), 0); |
| 170 | 172 | assertEquals(tradeOrderDO.getPayPrice(), 80); |
| 171 | 173 | assertEquals(tradeOrderDO.getPayOrderId(), 1000L); |
| 172 | - assertNull(tradeOrderDO.getPayChannel()); | |
| 174 | + assertNull(tradeOrderDO.getPayChannelCode()); | |
| 173 | 175 | assertNull(tradeOrderDO.getDeliveryTemplateId()); |
| 174 | 176 | assertNull(tradeOrderDO.getExpressNo()); |
| 175 | 177 | assertFalse(tradeOrderDO.getDeliveryStatus()); |
| ... | ... | @@ -177,7 +179,7 @@ public class TradeOrderServiceTest extends BaseDbUnitTest { |
| 177 | 179 | assertNull(tradeOrderDO.getReceiveTime()); |
| 178 | 180 | assertEquals(tradeOrderDO.getReceiverName(), "芋艿"); |
| 179 | 181 | assertEquals(tradeOrderDO.getReceiverMobile(), "15601691300"); |
| 180 | - assertEquals(tradeOrderDO.getReceiverAreaId(), 3306L); | |
| 182 | + assertEquals(tradeOrderDO.getReceiverAreaId(), 3306); | |
| 181 | 183 | assertEquals(tradeOrderDO.getReceiverPostCode(), 85757); |
| 182 | 184 | assertEquals(tradeOrderDO.getReceiverDetailAddress(), "土豆村"); |
| 183 | 185 | assertEquals(tradeOrderDO.getAfterSaleStatus(), TradeOrderAfterSaleStatusEnum.NONE.getStatus()); |
| ... | ... | @@ -241,11 +243,30 @@ public class TradeOrderServiceTest extends BaseDbUnitTest { |
| 241 | 243 | assertEquals(reqDTO.getOrderId(), tradeOrderId); |
| 242 | 244 | return true; |
| 243 | 245 | })); |
| 244 | -// //mock 支付订单信息 | |
| 245 | -// when(payOrderApi.createPayOrder(any())).thenReturn(1L); | |
| 246 | - | |
| 247 | -// //价格 | |
| 248 | -// assertEquals(calculateRespDTO.getOrder().getItems().get(0).getPresentPrice(), tradeOrderItemDO.getPresentPrice()); | |
| 249 | 246 | } |
| 250 | 247 | |
| 248 | + @Test | |
| 249 | + public void updateOrderPaid() { | |
| 250 | + // mock 数据(TradeOrder) | |
| 251 | + TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> { | |
| 252 | + o.setId(1L).setStatus(TradeOrderStatusEnum.UNPAID.getStatus()); | |
| 253 | + o.setPayOrderId(10L).setPayPrice(100).setPayTime(null); | |
| 254 | + }); | |
| 255 | + tradeOrderMapper.insert(order); | |
| 256 | + // 准备参数 | |
| 257 | + Long id = 1L; | |
| 258 | + Long payOrderId = 10L; | |
| 259 | + // mock 方法(支付单) | |
| 260 | + when(payOrderApi.getOrder(eq(10L))).thenReturn(randomPojo(PayOrderRespDTO.class, | |
| 261 | + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()).setChannelCode("wx_pub") | |
| 262 | + .setMerchantOrderId("1")).setAmount(100)); | |
| 263 | + // 调用 | |
| 264 | + tradeOrderService.updateOrderPaid(id, payOrderId); | |
| 265 | + // 断言 | |
| 266 | + TradeOrderDO dbOrder = tradeOrderMapper.selectById(id); | |
| 267 | + assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.PAID.getStatus()); | |
| 268 | + assertTrue(dbOrder.getPayed()); | |
| 269 | + assertNotNull(dbOrder.getPayTime()); | |
| 270 | + assertEquals(dbOrder.getPayChannelCode(), "wx_pub"); | |
| 271 | + } | |
| 251 | 272 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/test/resources/sql/create_tables.sql
| ... | ... | @@ -21,9 +21,9 @@ CREATE TABLE IF NOT EXISTS "trade_order" ( |
| 21 | 21 | "adjust_price" int NOT NULL, |
| 22 | 22 | "pay_price" int NOT NULL, |
| 23 | 23 | "pay_order_id" int, |
| 24 | - "pay_channel" int, | |
| 25 | - "delivery_template_id" int, | |
| 26 | - "express_no" int, | |
| 24 | + "pay_channel_code" varchar, | |
| 25 | + "delivery_template_id" bigint, | |
| 26 | + "express_no" varchar, | |
| 27 | 27 | "delivery_status" bit NOT NULL, |
| 28 | 28 | "delivery_time" datetime, |
| 29 | 29 | "receive_time" datetime, | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApi.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.api.order; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; |
| 4 | +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | |
| 4 | 5 | |
| 5 | 6 | import javax.validation.Valid; |
| 6 | 7 | |
| ... | ... | @@ -18,6 +19,14 @@ public interface PayOrderApi { |
| 18 | 19 | * @param reqDTO 创建请求 |
| 19 | 20 | * @return 支付单编号 |
| 20 | 21 | */ |
| 21 | - Long createPayOrder(@Valid PayOrderCreateReqDTO reqDTO); | |
| 22 | + Long createOrder(@Valid PayOrderCreateReqDTO reqDTO); | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 获得支付单 | |
| 26 | + * | |
| 27 | + * @param id 支付单编号 | |
| 28 | + * @return 支付单 | |
| 29 | + */ | |
| 30 | + PayOrderRespDTO getOrder(Long id); | |
| 22 | 31 | |
| 23 | 32 | } | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.api.order.dto; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 支付单信息 Response DTO | |
| 8 | + * | |
| 9 | + * TODO 芋艿:还没定好字段 | |
| 10 | + * | |
| 11 | + * @author 芋道源码 | |
| 12 | + */ | |
| 13 | +@Data | |
| 14 | +public class PayOrderRespDTO { | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * 订单编号,数据库自增 | |
| 18 | + */ | |
| 19 | + private Long id; | |
| 20 | + /** | |
| 21 | + * 渠道编码 | |
| 22 | + * | |
| 23 | + * 枚举 PayChannelEnum | |
| 24 | + */ | |
| 25 | + private String channelCode; | |
| 26 | + | |
| 27 | + // ========== 商户相关字段 ========== | |
| 28 | + /** | |
| 29 | + * 商户订单编号 | |
| 30 | + * 例如说,内部系统 A 的订单号。需要保证每个 PayMerchantDO 唯一 | |
| 31 | + */ | |
| 32 | + private String merchantOrderId; | |
| 33 | + | |
| 34 | + // ========== 订单相关字段 ========== | |
| 35 | + /** | |
| 36 | + * 支付金额,单位:分 | |
| 37 | + */ | |
| 38 | + private Integer amount; | |
| 39 | + /** | |
| 40 | + * 支付状态 | |
| 41 | + * | |
| 42 | + * 枚举 {@link PayOrderStatusEnum} | |
| 43 | + */ | |
| 44 | + private Integer status; | |
| 45 | + | |
| 46 | + // ========== 渠道相关字段 ========== | |
| 47 | + | |
| 48 | +} | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
| ... | ... | @@ -28,9 +28,9 @@ public interface ErrorCodeConstants { |
| 28 | 28 | ErrorCode CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL = new ErrorCode(1007001007,"微信渠道v3版本apiclient_key.pem不可为空"); |
| 29 | 29 | ErrorCode CHANNEL_WECHAT_VERSION_3_CERT_KEY_IS_NULL = new ErrorCode(1007001008,"微信渠道v3版本中apiclient_cert.pem不可为空"); |
| 30 | 30 | ErrorCode PAY_CHANNEL_NOTIFY_VERIFY_FAILED = new ErrorCode(1007001009, "渠道通知校验失败"); |
| 31 | - /** | |
| 32 | - * ========== ORDER 模块 1-007-002-000 ========== | |
| 33 | - */ | |
| 31 | + | |
| 32 | + // ========== ORDER 模块 1-007-002-000 ========== | |
| 33 | + | |
| 34 | 34 | ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在"); |
| 35 | 35 | ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付"); |
| 36 | 36 | ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付"); |
| ... | ... | @@ -57,8 +57,4 @@ public interface ErrorCodeConstants { |
| 57 | 57 | ErrorCode PAY_MERCHANT_NOT_EXISTS = new ErrorCode(1007004000, "支付商户信息不存在"); |
| 58 | 58 | ErrorCode PAY_MERCHANT_EXIST_APP_CANT_DELETE = new ErrorCode(1007004001, "支付商户存在支付应用,无法删除"); |
| 59 | 59 | |
| 60 | - | |
| 61 | - | |
| 62 | - | |
| 63 | - | |
| 64 | 60 | } | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java
| ... | ... | @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.core.IntArrayValuable; |
| 4 | 4 | import lombok.AllArgsConstructor; |
| 5 | 5 | import lombok.Getter; |
| 6 | 6 | |
| 7 | +import java.util.Objects; | |
| 8 | + | |
| 7 | 9 | /** |
| 8 | 10 | * 支付订单的状态枚举 |
| 9 | 11 | * |
| ... | ... | @@ -26,4 +28,14 @@ public enum PayOrderStatusEnum implements IntArrayValuable { |
| 26 | 28 | return new int[0]; |
| 27 | 29 | } |
| 28 | 30 | |
| 31 | + /** | |
| 32 | + * 判断是否支付成功 | |
| 33 | + * | |
| 34 | + * @param status 状态 | |
| 35 | + * @return 是否支付成功 | |
| 36 | + */ | |
| 37 | + public static boolean isSuccess(Integer status) { | |
| 38 | + return Objects.equals(status, SUCCESS.getStatus()); | |
| 39 | + } | |
| 40 | + | |
| 29 | 41 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.api.order; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; |
| 4 | +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | |
| 5 | +import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert; | |
| 6 | +import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; | |
| 4 | 7 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 5 | 8 | import org.springframework.stereotype.Service; |
| 6 | 9 | |
| ... | ... | @@ -18,8 +21,14 @@ public class PayOrderApiImpl implements PayOrderApi { |
| 18 | 21 | private PayOrderService payOrderService; |
| 19 | 22 | |
| 20 | 23 | @Override |
| 21 | - public Long createPayOrder(PayOrderCreateReqDTO reqDTO) { | |
| 24 | + public Long createOrder(PayOrderCreateReqDTO reqDTO) { | |
| 22 | 25 | return payOrderService.createPayOrder(reqDTO); |
| 23 | 26 | } |
| 24 | 27 | |
| 28 | + @Override | |
| 29 | + public PayOrderRespDTO getOrder(Long id) { | |
| 30 | + PayOrderDO order = payOrderService.getOrder(id); | |
| 31 | + return PayOrderConvert.INSTANCE.convert2(order); | |
| 32 | + } | |
| 33 | + | |
| 25 | 34 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.convert.order; |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; |
| 5 | 5 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; |
| 6 | +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | |
| 6 | 7 | import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderDetailsRespVO; |
| 7 | 8 | import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExcelVO; |
| 8 | 9 | import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageItemRespVO; |
| ... | ... | @@ -41,13 +42,14 @@ public interface PayOrderConvert { |
| 41 | 42 | List<PayOrderExcelVO> convertList02(List<PayOrderDO> list); |
| 42 | 43 | |
| 43 | 44 | /** |
| 44 | - * 订单DO转自定义分页对象 | |
| 45 | + * 订单 DO 转自定义分页对象 | |
| 45 | 46 | * |
| 46 | 47 | * @param bean 订单DO |
| 47 | 48 | * @return 分页对象 |
| 48 | 49 | */ |
| 49 | 50 | PayOrderPageItemRespVO pageConvertItemPage(PayOrderDO bean); |
| 50 | 51 | |
| 52 | + // TODO 芋艿:优化下 convert 逻辑 | |
| 51 | 53 | default PayOrderExcelVO excelConvert(PayOrderDO bean) { |
| 52 | 54 | if (bean == null) { |
| 53 | 55 | return null; |
| ... | ... | @@ -94,4 +96,6 @@ public interface PayOrderConvert { |
| 94 | 96 | |
| 95 | 97 | PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqDTO bean); |
| 96 | 98 | |
| 99 | + PayOrderRespDTO convert2(PayOrderDO bean); | |
| 100 | + | |
| 97 | 101 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java