trade:完成交易订单的发货逻辑

This commit is contained in:
YunaiV 2022-11-27 19:42:49 +08:00
parent 07f1681b50
commit 66abe4a84b
18 changed files with 332 additions and 126 deletions

View File

@ -62,7 +62,7 @@ public class PayOrderUnifiedReqDTO {
*/ */
@NotNull(message = "支付金额不能为空") @NotNull(message = "支付金额不能为空")
@DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
private Long amount; private Integer amount;
/** /**
* 支付过期时间 * 支付过期时间

View File

@ -63,7 +63,7 @@ public class PayRefundUnifiedReqDTO {
*/ */
@NotNull(message = "退款金额不能为空") @NotNull(message = "退款金额不能为空")
@DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
private Long amount; private Integer amount;
/** /**
* 退款结果 notify 回调地址 支付宝退款不需要回调地址 微信需要 * 退款结果 notify 回调地址 支付宝退款不需要回调地址 微信需要

View File

@ -69,7 +69,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
this.init(); this.init();
} }
protected Double calculateAmount(Long amount) { protected Double calculateAmount(Integer amount) {
return amount / 100.0; return amount / 100.0;
} }

View File

@ -122,7 +122,7 @@ public class PayClientFactoryImplIntegrationTest {
private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() { private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO(); PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
reqDTO.setAmount(123L); reqDTO.setAmount(123);
reqDTO.setSubject("IPhone 13"); reqDTO.setSubject("IPhone 13");
reqDTO.setBody("biubiubiu"); reqDTO.setBody("biubiubiu");
reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis())); reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));

View File

@ -73,7 +73,7 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
Long shopOrderId = System.currentTimeMillis(); Long shopOrderId = System.currentTimeMillis();
PayOrderUnifiedReqDTO reqDTO=new PayOrderUnifiedReqDTO(); PayOrderUnifiedReqDTO reqDTO=new PayOrderUnifiedReqDTO();
reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis())); reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
reqDTO.setAmount(1L); reqDTO.setAmount(1);
reqDTO.setBody("内容:" + shopOrderId); reqDTO.setBody("内容:" + shopOrderId);
reqDTO.setSubject("标题:"+shopOrderId); reqDTO.setSubject("标题:"+shopOrderId);
String notify="http://niubi.natapp1.cc/api/pay/order/notify"; String notify="http://niubi.natapp1.cc/api/pay/order/notify";

View File

@ -23,9 +23,10 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_NOT_FOUND = new ErrorCode(1011000011, "交易订单不存在"); ErrorCode ORDER_NOT_FOUND = new ErrorCode(1011000011, "交易订单不存在");
ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1011000012, "交易订单项更新售后状态失败,请重试"); ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1011000012, "交易订单项更新售后状态失败,请重试");
ErrorCode ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1011000013, "交易订单更新支付状态失败,订单不是【未支付】状态"); ErrorCode ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1011000013, "交易订单更新支付状态失败,订单不是【未支付】状态");
ErrorCode ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1011000014, "交易订单更新支付状态失败,支付单编号不匹配"); ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1011000014, "交易订单更新支付状态失败,支付单编号不匹配");
ErrorCode ORDER_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1011000015, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态"); ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1011000015, "交易订单更新支付状态失败,支付单状态不是【支付成功】状态");
ErrorCode ORDER_UPDATE_PAID_PAY_PRICE_NOT_MATCH = new ErrorCode(1011000016, "交易订单更新支付状态失败,支付单金额不匹配"); ErrorCode ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1011000016, "交易订单更新支付状态失败,支付单金额不匹配");
ErrorCode ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED = new ErrorCode(1011000017, "交易订单发货失败,订单不是【待发货】状态");
// ========== After Sale 模块 1-011-000-000 ========== // ========== After Sale 模块 1-011-000-000 ==========
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在"); ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.trade.enums.order;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 交易订单 - 发货状态
*
* @author 芋道源码
*/
@RequiredArgsConstructor
@Getter
public enum TradeOrderDeliveryStatusEnum {
UNDELIVERED(0, "未发货"),
DELIVERED(1, "已发货"),
RECEIVED(2, "已收货");
/**
* 状态值
*/
private final Integer status;
/**
* 状态名
*/
private final String name;
}

View File

@ -52,9 +52,30 @@ public enum TradeOrderStatusEnum implements IntArrayValuable {
* @return 是否 * @return 是否
*/ */
public static boolean isUnpaid(Integer status) { public static boolean isUnpaid(Integer status) {
return ObjectUtil.equal(UNPAID.getStatus(), status);
}
/**
* 判断指定状态是否正处于已支付状态
*
* @param status 指定状态
* @return 是否
*/
public static boolean isPaid(Integer status) {
return ObjectUtil.equal(PAID.getStatus(), status); return ObjectUtil.equal(PAID.getStatus(), status);
} }
/**
* 判断指定状态是否正处于待发货状态
*
* @param status 指定状态
* @return 是否
*/
public static boolean isUndelivered(Integer status) {
return ObjectUtil.equal(UNDELIVERED.getStatus(), status);
}
/** /**
* 判断指定状态是否正处于已取消状态 * 判断指定状态是否正处于已取消状态
* *

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.trade.controller.admin.order;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Api(tags = "管理后台 - 交易订单")
@RestController
@RequestMapping("/trade/order")
@Validated
@Slf4j
public class TradeOrderController {
@Resource
private TradeOrderService tradeOrderService;
@PostMapping("/delivery")
@ApiOperation("发货订单")
@PreAuthorize("@ss.hasPermission('trade:order:delivery')")
public CommonResult<Boolean> deliveryOrder(@RequestBody TradeOrderDeliveryReqVO deliveryReqVO) {
tradeOrderService.deliveryOrder(getLoginUserId(), deliveryReqVO);
return success(true);
}
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 订单发货 Request VO")
@Data
public class TradeOrderDeliveryReqVO {
@ApiModelProperty(name = "订单编号", required = true, example = "1024")
@NotNull(message = "订单编号不能为空")
private Long id;
@ApiModelProperty(name = "发货物流公司编号", required = true, example = "1")
@NotNull(message = "发货物流公司编号不能为空")
private Long logisticsId;
@ApiModelProperty(name = "发货物流单号", required = true, example = "SF123456789")
@NotEmpty(message = "发货物流单号不能为空")
private String logisticsNo;
}

View File

@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -24,7 +25,7 @@ public class AppTradeAfterSaleDeliveryReqVO {
private String logisticsNo; private String logisticsNo;
@ApiModelProperty(name = "退货时间", required = true) @ApiModelProperty(name = "退货时间", required = true)
@NotNull(message = "退货时间不能为空") @NotEmpty(message = "退货时间不能为空")
private LocalDateTime deliveryTime; private LocalDateTime deliveryTime;
} }

View File

@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO.OrderItem; import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO.OrderItem;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderDeliveryStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.KeySequence;
@ -178,20 +179,24 @@ public class TradeOrderDO extends BaseDO {
*/ */
private Long deliveryTemplateId; private Long deliveryTemplateId;
/** /**
* 物流公司单 * 发货物流公司编
*/ */
private String expressNo; private Long logisticsId;
/**
* 发货物流单号
*/
private String logisticsNo;
/** /**
* 发货状态 * 发货状态
* *
* true - 已发货 * 枚举 {@link TradeOrderDeliveryStatusEnum}
* false - 未发货
*/ */
private Boolean deliveryStatus; private Integer deliveryStatus;
/** /**
* 发货时间 * 发货时间
*/ */
private LocalDateTime deliveryTime; private LocalDateTime deliveryTime;
/** /**
* 收货时间 * 收货时间
*/ */

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.service.order; package cn.iocoder.yudao.module.trade.service.order;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@ -15,7 +16,7 @@ public interface TradeOrderService {
// =================== Order =================== // =================== Order ===================
/** /**
* 创建交易订单 * 会员创建交易订单
* *
* @param userId 登录用户 * @param userId 登录用户
* @param userIp 用户 IP 地址 * @param userIp 用户 IP 地址
@ -24,6 +25,22 @@ public interface TradeOrderService {
*/ */
Long createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO); Long createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO);
/**
* 更新交易订单已支付
*
* @param id 交易订单编号
* @param payOrderId 支付订单编号
*/
void updateOrderPaid(Long id, Long payOrderId);
/**
* 管理员发货交易订单
*
* @param userId 管理员编号
* @param deliveryReqVO 发货请求
*/
void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO);
/** /**
* 获得指定用户指定的交易订单 * 获得指定用户指定的交易订单
* *
@ -33,14 +50,6 @@ public interface TradeOrderService {
*/ */
TradeOrderDO getOrder(Long userId, Long orderId); TradeOrderDO getOrder(Long userId, Long orderId);
/**
* 更新交易订单已支付
*
* @param id 交易订单编号
* @param payOrderId 支付订单编号
*/
void updateOrderPaid(Long id, Long payOrderId);
// =================== Order Item =================== // =================== Order Item ===================
/** /**

View File

@ -24,6 +24,7 @@ import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
import cn.iocoder.yudao.module.promotion.api.price.PriceApi; import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO.Item; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO.Item;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
@ -107,94 +108,6 @@ public class TradeOrderServiceImpl implements TradeOrderService {
return tradeOrderDO.getId(); return tradeOrderDO.getId();
} }
@Override
public TradeOrderDO getOrder(Long userId, Long orderId) {
TradeOrderDO order = tradeOrderMapper.selectById(orderId);
if (order != null
&& ObjectUtil.notEqual(order.getUserId(), userId)) {
return null;
}
return order;
}
@Override
public void updateOrderPaid(Long id, Long payOrderId) {
// 校验并获得交易订单
KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPaid(id, payOrderId);
TradeOrderDO order = orderResult.getKey();
PayOrderRespDTO payOrder = orderResult.getValue();
// 更新 TradeOrderDO 状态为已支付等待发货
int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayed(true)
.setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
if (updateCount == 0) {
throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
}
// TODO 芋艿发送订单变化的消息
// TODO 芋艿发送站内信
// TODO 芋艿OrderLog
}
/**
* 校验交易订单满足被支付的条件
*
* 1. 交易订单未支付
* 2. 支付单已支付
*
* @param id 交易订单编号
* @param payOrderId 支付订单编号
* @return 交易订单
*/
private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPaid(Long id, Long payOrderId) {
// 校验订单是否存在
TradeOrderDO order = tradeOrderMapper.selectById(id);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// 校验订单未支付
if (TradeOrderStatusEnum.isUnpaid(order.getStatus())) { // 状态
log.error("[validateOrderPaid][order({}) 不处于待支付状态请进行处理order 数据是:{}]",
id, JsonUtils.toJsonString(order));
throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
}
// 校验支付订单匹配
if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
log.error("[validateOrderPaid][order({}) 支付单不匹配({})请进行处理order 数据是:{}]",
id, payOrderId, JsonUtils.toJsonString(order));
throw exception(ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR);
}
// 校验支付单是否存在
PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
if (payOrder == null) {
log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
throw exception(PAY_ORDER_NOT_FOUND);
}
// 校验支付单已支付
if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
log.error("[validateOrderPaid][order({}) payOrder({}) 未支付请进行处理payOrder 数据是:{}]",
id, payOrderId, JsonUtils.toJsonString(payOrder));
throw exception(ORDER_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS);
}
// 校验支付金额一致
if (ObjectUtil.notEqual(payOrder.getAmount(), order.getPayPrice())) {
log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配请进行处理order 数据是:{}payOrder 数据是:{}]",
id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
throw exception(ORDER_UPDATE_PAID_PAY_PRICE_NOT_MATCH);
}
// 校验支付订单匹配二次
if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
log.error("[validateOrderPaid][order({}) 支付单不匹配({})请进行处理payOrder 数据是:{}]",
id, payOrderId, JsonUtils.toJsonString(payOrder));
throw exception(ORDER_UPDATE_PAID_PAY_ORDER_ID_ERROR);
}
return new KeyValue<>(order, payOrder);
}
/** /**
* 校验商品 SKU 是否可出售 * 校验商品 SKU 是否可出售
* *
@ -269,7 +182,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
tradeOrderDO.setProductCount(getSumValue(order.getItems(), PriceCalculateRespDTO.OrderItem::getCount, Integer::sum)); tradeOrderDO.setProductCount(getSumValue(order.getItems(), PriceCalculateRespDTO.OrderItem::getCount, Integer::sum));
tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源? tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
tradeOrderDO.setAdjustPrice(0).setPayed(false); // 支付信息 tradeOrderDO.setAdjustPrice(0).setPayed(false); // 支付信息
tradeOrderDO.setDeliveryStatus(false); // 物流信息 tradeOrderDO.setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus()); // 物流信息
tradeOrderDO.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息 tradeOrderDO.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息
tradeOrderMapper.insert(tradeOrderDO); tradeOrderMapper.insert(tradeOrderDO);
return tradeOrderDO; return tradeOrderDO;
@ -324,7 +237,142 @@ public class TradeOrderServiceImpl implements TradeOrderService {
tradeOrderMapper.updateById(new TradeOrderDO().setId(tradeOrderDO.getId()).setPayOrderId(payOrderId)); tradeOrderMapper.updateById(new TradeOrderDO().setId(tradeOrderDO.getId()).setPayOrderId(payOrderId));
} }
@Override
public void updateOrderPaid(Long id, Long payOrderId) {
// 校验并获得交易订单可支付
KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
TradeOrderDO order = orderResult.getKey();
PayOrderRespDTO payOrder = orderResult.getValue();
// 更新 TradeOrderDO 状态为已支付等待发货
int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayed(true)
.setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));
if (updateCount == 0) {
throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
}
// TODO 芋艿发送订单变化的消息
// TODO 芋艿发送站内信
// TODO 芋艿OrderLog
}
/**
* 校验交易订单满足被支付的条件
*
* 1. 交易订单未支付
* 2. 支付单已支付
*
* @param id 交易订单编号
* @param payOrderId 支付订单编号
* @return 交易订单
*/
private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPayable(Long id, Long payOrderId) {
// 校验订单是否存在
TradeOrderDO order = tradeOrderMapper.selectById(id);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// 校验订单未支付
if (!TradeOrderStatusEnum.isUnpaid(order.getStatus()) || order.getPayed()) {
log.error("[validateOrderPaid][order({}) 不处于待支付状态请进行处理order 数据是:{}]",
id, JsonUtils.toJsonString(order));
throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
}
// 校验支付订单匹配
if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
log.error("[validateOrderPaid][order({}) 支付单不匹配({})请进行处理order 数据是:{}]",
id, payOrderId, JsonUtils.toJsonString(order));
throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
}
// 校验支付单是否存在
PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
if (payOrder == null) {
log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
throw exception(PAY_ORDER_NOT_FOUND);
}
// 校验支付单已支付
if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
log.error("[validateOrderPaid][order({}) payOrder({}) 未支付请进行处理payOrder 数据是:{}]",
id, payOrderId, JsonUtils.toJsonString(payOrder));
throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
}
// 校验支付金额一致
if (ObjectUtil.notEqual(payOrder.getAmount(), order.getPayPrice())) {
log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配请进行处理order 数据是:{}payOrder 数据是:{}]",
id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
}
// 校验支付订单匹配二次
if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
log.error("[validateOrderPaid][order({}) 支付单不匹配({})请进行处理payOrder 数据是:{}]",
id, payOrderId, JsonUtils.toJsonString(payOrder));
throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
}
return new KeyValue<>(order, payOrder);
}
@Override
public void deliveryOrder(Long userId, TradeOrderDeliveryReqVO deliveryReqVO) {
// 校验并获得交易订单可发货
TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
// TODO 芋艿logisticsId 校验存在
// 更新 TradeOrderDO 状态为已发货等待收货
int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
new TradeOrderDO().setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())
.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo())
.setDeliveryStatus(TradeOrderDeliveryStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now()));
if (updateCount == 0) {
throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
}
// TODO 芋艿发送订单变化的消息
// TODO 芋艿发送站内信
// TODO 芋艿OrderLog
// TODO 设计like是否要单独一个 delivery 发货单表
// TODO 设计niu要不要支持一个订单下多个 order item 单独发货类似有赞
// TODO 设计lili是不是发货后才支持售后
}
/**
* 校验交易订单满足被发货的条件
*
* 1. 交易订单未发货
*
* @param id 交易订单编号
* @return 交易订单
*/
private TradeOrderDO validateOrderDeliverable(Long id) {
// 校验订单是否存在
TradeOrderDO order = tradeOrderMapper.selectById(id);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// 校验订单是否是待发货状态
if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())
|| ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus())) {
throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
}
return order;
}
@Override
public TradeOrderDO getOrder(Long userId, Long orderId) {
TradeOrderDO order = tradeOrderMapper.selectById(orderId);
if (order != null
&& ObjectUtil.notEqual(order.getUserId(), userId)) {
return null;
}
return order;
}
// =================== Order Item =================== // =================== Order Item ===================

View File

@ -16,15 +16,13 @@ import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.price.PriceApi; import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderAfterSaleStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.*;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -38,6 +36,8 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@ -173,8 +173,8 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
assertEquals(tradeOrderDO.getPayOrderId(), 1000L); assertEquals(tradeOrderDO.getPayOrderId(), 1000L);
assertNull(tradeOrderDO.getPayChannelCode()); assertNull(tradeOrderDO.getPayChannelCode());
assertNull(tradeOrderDO.getDeliveryTemplateId()); assertNull(tradeOrderDO.getDeliveryTemplateId());
assertNull(tradeOrderDO.getExpressNo()); assertNull(tradeOrderDO.getLogisticsId());
assertFalse(tradeOrderDO.getDeliveryStatus()); assertEquals(tradeOrderDO.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus());
assertNull(tradeOrderDO.getDeliveryTime()); assertNull(tradeOrderDO.getDeliveryTime());
assertNull(tradeOrderDO.getReceiveTime()); assertNull(tradeOrderDO.getReceiveTime());
assertEquals(tradeOrderDO.getReceiverName(), "芋艿"); assertEquals(tradeOrderDO.getReceiverName(), "芋艿");
@ -246,11 +246,11 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
} }
@Test @Test
public void updateOrderPaid() { public void testUpdateOrderPaid() {
// mock 数据TradeOrder // mock 数据TradeOrder
TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> { TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> {
o.setId(1L).setStatus(TradeOrderStatusEnum.UNPAID.getStatus()); o.setId(1L).setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
o.setPayOrderId(10L).setPayPrice(100).setPayTime(null); o.setPayOrderId(10L).setPayed(false).setPayPrice(100).setPayTime(null);
}); });
tradeOrderMapper.insert(order); tradeOrderMapper.insert(order);
// 准备参数 // 准备参数
@ -260,13 +260,39 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
when(payOrderApi.getOrder(eq(10L))).thenReturn(randomPojo(PayOrderRespDTO.class, when(payOrderApi.getOrder(eq(10L))).thenReturn(randomPojo(PayOrderRespDTO.class,
o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()).setChannelCode("wx_pub") o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()).setChannelCode("wx_pub")
.setMerchantOrderId("1")).setAmount(100)); .setMerchantOrderId("1")).setAmount(100));
// 调用 // 调用
tradeOrderService.updateOrderPaid(id, payOrderId); tradeOrderService.updateOrderPaid(id, payOrderId);
// 断言 // 断言
TradeOrderDO dbOrder = tradeOrderMapper.selectById(id); TradeOrderDO dbOrder = tradeOrderMapper.selectById(id);
assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.PAID.getStatus()); assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
assertTrue(dbOrder.getPayed()); assertTrue(dbOrder.getPayed());
assertNotNull(dbOrder.getPayTime()); assertNotNull(dbOrder.getPayTime());
assertEquals(dbOrder.getPayChannelCode(), "wx_pub"); assertEquals(dbOrder.getPayChannelCode(), "wx_pub");
} }
@Test
public void testDeliveryOrder() {
// mock 数据TradeOrder
TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> {
o.setId(1L).setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
o.setLogisticsId(null).setLogisticsNo(null).setDeliveryTime(null)
.setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus());
});
tradeOrderMapper.insert(order);
// 准备参数
TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO().setId(1L)
.setLogisticsId(10L).setLogisticsNo("100");
// mock 方法支付单
// 调用
tradeOrderService.deliveryOrder(randomLongId(), deliveryReqVO);
// 断言
TradeOrderDO dbOrder = tradeOrderMapper.selectById(1L);
assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus());
assertEquals(dbOrder.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.DELIVERED.getStatus());
assertPojoEquals(dbOrder, deliveryReqVO);
assertNotNull(dbOrder.getDeliveryTime());
}
} }

View File

@ -20,10 +20,11 @@ CREATE TABLE IF NOT EXISTS "trade_order" (
"delivery_price" int NOT NULL, "delivery_price" int NOT NULL,
"adjust_price" int NOT NULL, "adjust_price" int NOT NULL,
"pay_price" int NOT NULL, "pay_price" int NOT NULL,
"pay_order_id" int, "pay_order_id" bigint,
"pay_channel_code" varchar, "pay_channel_code" varchar,
"delivery_template_id" bigint, "delivery_template_id" bigint,
"express_no" varchar, "logistics_id" bigint,
"logistics_no" varchar,
"delivery_status" bit NOT NULL, "delivery_status" bit NOT NULL,
"delivery_time" datetime, "delivery_time" datetime,
"receive_time" datetime, "receive_time" datetime,

View File

@ -132,11 +132,11 @@ public class PayRefundDO extends BaseDO {
/** /**
* 支付金额单位 * 支付金额单位
*/ */
private Long payAmount; private Integer payAmount;
/** /**
* 退款金额单位 * 退款金额单位
*/ */
private Long refundAmount; private Integer refundAmount;
/** /**
* 退款原因 * 退款原因

View File

@ -31,7 +31,7 @@ public class PayRefundReqDTO {
*/ */
@NotNull(message = "退款金额不能为空") @NotNull(message = "退款金额不能为空")
@DecimalMin(value = "0", inclusive = false, message = "退款金额必须大于零") @DecimalMin(value = "0", inclusive = false, message = "退款金额必须大于零")
private Long amount; private Integer amount;
/** /**
* 退款原因 * 退款原因