marge master
This commit is contained in:
@@ -1,94 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-trade-biz</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
trade 模块,主要实现交易相关功能
|
||||
例如:订单、退款、购物车等功能。
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-trade-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-product-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-pay-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-promotion-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-member-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- TODO 芋艿:引入依赖要优化下: -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-pay</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@@ -1,4 +0,0 @@
|
||||
### 获得交易售后分页 => 成功
|
||||
GET {{baseUrl}}/trade/after-sale/page?pageNo=1&pageSize=10
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
@@ -1,113 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.aftersale;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRespPageItemVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
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.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Api(tags = "管理后台 - 交易售后")
|
||||
@RestController
|
||||
@RequestMapping("/trade/after-sale")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class TradeAfterSaleController {
|
||||
|
||||
@Resource
|
||||
private TradeAfterSaleService afterSaleService;
|
||||
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
@Resource
|
||||
private ProductPropertyValueApi productPropertyValueApi;
|
||||
|
||||
@GetMapping("/page")
|
||||
@ApiOperation("获得交易售后分页")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:query')")
|
||||
public CommonResult<PageResult<TradeAfterSaleRespPageItemVO>> getAfterSalePage(@Valid TradeAfterSalePageReqVO pageVO) {
|
||||
// 查询售后
|
||||
PageResult<TradeAfterSaleDO> pageResult = afterSaleService.getAfterSalePage(pageVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
|
||||
// 查询商品属性
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails = productPropertyValueApi
|
||||
.getPropertyValueDetailList(TradeAfterSaleConvert.INSTANCE.convertPropertyValueIds(pageResult.getList()));
|
||||
// 查询会员
|
||||
Map<Long, MemberUserRespDTO> memberUsers = memberUserApi.getUserMap(
|
||||
convertSet(pageResult.getList(), TradeAfterSaleDO::getUserId));
|
||||
return success(TradeAfterSaleConvert.INSTANCE.convertPage(pageResult, memberUsers, propertyValueDetails));
|
||||
}
|
||||
|
||||
@PutMapping("/agree")
|
||||
@ApiOperation("同意售后")
|
||||
@ApiImplicitParam(name = "id", value = "售后编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:agree')")
|
||||
public CommonResult<Boolean> agreeAfterSale(@RequestParam("id") Long id) {
|
||||
afterSaleService.agreeAfterSale(getLoginUserId(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/disagree")
|
||||
@ApiOperation("拒绝售后")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:disagree')")
|
||||
public CommonResult<Boolean> disagreeAfterSale(@RequestBody TradeAfterSaleDisagreeReqVO confirmReqVO) {
|
||||
afterSaleService.disagreeAfterSale(getLoginUserId(), confirmReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/receive")
|
||||
@ApiOperation("确认收货")
|
||||
@ApiImplicitParam(name = "id", value = "售后编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:receive')")
|
||||
public CommonResult<Boolean> receiveAfterSale(@RequestParam("id") Long id) {
|
||||
afterSaleService.receiveAfterSale(getLoginUserId(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/refuse")
|
||||
@ApiOperation("确认收货")
|
||||
@ApiImplicitParam(name = "id", value = "售后编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:receive')")
|
||||
public CommonResult<Boolean> refuseAfterSale(TradeAfterSaleRefuseReqVO refuseReqVO) {
|
||||
afterSaleService.refuseAfterSale(getLoginUserId(), refuseReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/refund")
|
||||
@ApiOperation(value = "确认退款")
|
||||
@ApiImplicitParam(name = "id", value = "售后编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:refund')")
|
||||
public CommonResult<Boolean> refundAfterSale(@RequestParam("id") Long id) {
|
||||
afterSaleService.refundAfterSale(getLoginUserId(), getClientIP(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
@@ -1,119 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
/**
|
||||
* 交易售后 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class TradeAfterSaleBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "售后流水号", required = true, example = "202211190847450020500077")
|
||||
@NotNull(message = "售后流水号不能为空")
|
||||
private String no;
|
||||
|
||||
@ApiModelProperty(value = "售后状态", required = true, example = "10", notes = "参见 TradeAfterSaleStatusEnum 枚举")
|
||||
@NotNull(message = "售后状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "售后类型", required = true, example = "20", notes = "参见 TradeAfterSaleTypeEnum 枚举")
|
||||
@NotNull(message = "售后类型不能为空")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "售后方式", required = true, example = "10", notes = "参见 TradeAfterSaleWayEnum 枚举")
|
||||
@NotNull(message = "售后方式不能为空")
|
||||
private Integer way;
|
||||
|
||||
@ApiModelProperty(value = "用户编号", required = true, example = "30337")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "申请原因", required = true, example = "不喜欢")
|
||||
@NotNull(message = "申请原因不能为空")
|
||||
private String applyReason;
|
||||
|
||||
@ApiModelProperty(value = "补充描述", example = "你说的对")
|
||||
private String applyDescription;
|
||||
|
||||
@ApiModelProperty(value = "补充凭证图片", example = "https://www.iocoder.cn/1.png")
|
||||
private List<String> applyPicUrls;
|
||||
|
||||
@ApiModelProperty(value = "订单编号", required = true, example = "18078")
|
||||
@NotNull(message = "订单编号不能为空")
|
||||
private Long orderId;
|
||||
|
||||
@ApiModelProperty(value = "订单流水号", required = true, example = "2022111917190001")
|
||||
@NotNull(message = "订单流水号不能为空")
|
||||
private Long orderNo;
|
||||
|
||||
@ApiModelProperty(value = "订单项编号", required = true, example = "572")
|
||||
@NotNull(message = "订单项编号不能为空")
|
||||
private Long orderItemId;
|
||||
|
||||
@ApiModelProperty(value = "商品 SPU 编号", required = true, example = "2888")
|
||||
@NotNull(message = "商品 SPU 编号不能为空")
|
||||
private Long spuId;
|
||||
|
||||
@ApiModelProperty(value = "商品 SPU 名称", required = true, example = "李四")
|
||||
@NotNull(message = "商品 SPU 名称不能为空")
|
||||
private String spuName;
|
||||
|
||||
@ApiModelProperty(value = "商品 SKU 编号", required = true, example = "15657")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
private Long skuId;
|
||||
|
||||
@ApiModelProperty(value = "商品图片", example = "https://www.iocoder.cn/2.png")
|
||||
private String picUrl;
|
||||
|
||||
@ApiModelProperty(value = "购买数量", required = true, example = "20012")
|
||||
@NotNull(message = "购买数量不能为空")
|
||||
private Integer count;
|
||||
|
||||
@ApiModelProperty(value = "审批时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime auditTime;
|
||||
|
||||
@ApiModelProperty(value = "审批人", example = "30835")
|
||||
private Long auditUserId;
|
||||
|
||||
@ApiModelProperty(value = "审批备注", example = "不香")
|
||||
private String auditReason;
|
||||
|
||||
@ApiModelProperty(value = "退款金额,单位:分", required = true, example = "18077")
|
||||
@NotNull(message = "退款金额,单位:分不能为空")
|
||||
private Integer refundPrice;
|
||||
|
||||
@ApiModelProperty(value = "支付退款编号", example = "10271")
|
||||
private Long payRefundId;
|
||||
|
||||
@ApiModelProperty(value = "退款时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime refundTime;
|
||||
|
||||
@ApiModelProperty(value = "退货物流公司编号", example = "10")
|
||||
private Long logisticsId;
|
||||
|
||||
@ApiModelProperty(value = "退货物流单号", example = "610003952009")
|
||||
private String logisticsNo;
|
||||
|
||||
@ApiModelProperty(value = "退货时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime deliveryTime;
|
||||
|
||||
@ApiModelProperty(value = "收货时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime receiveTime;
|
||||
|
||||
@ApiModelProperty(value = "收货备注", example = "不喜欢")
|
||||
private String receiveReason;
|
||||
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.aftersale.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 TradeAfterSaleDisagreeReqVO {
|
||||
|
||||
@ApiModelProperty(value = "售后编号", required = true, example = "1024")
|
||||
@NotNull(message = "售后编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "审批备注", required = true, example = "你猜")
|
||||
@NotEmpty(message = "审批备注不能为空")
|
||||
private String auditReason;
|
||||
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@ApiModel("管理后台 - 交易售后分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TradeAfterSalePageReqVO extends PageParam {
|
||||
|
||||
@ApiModelProperty(value = "售后流水号", example = "202211190847450020500077", notes = "模糊匹配")
|
||||
private String no;
|
||||
|
||||
@ApiModelProperty(value = "售后状态", example = "10", notes = "参见 TradeAfterSaleStatusEnum 枚举")
|
||||
@InEnum(value = TradeAfterSaleStatusEnum.class, message = "售后状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "售后类型", example = "20", notes = "参见 TradeAfterSaleTypeEnum 枚举")
|
||||
@InEnum(value = TradeAfterSaleTypeEnum.class, message = "售后类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "售后方式", example = "10", notes = "参见 TradeAfterSaleWayEnum 枚举")
|
||||
@InEnum(value = TradeAfterSaleWayEnum.class, message = "售后方式必须是 {value}")
|
||||
private Integer way;
|
||||
|
||||
@ApiModelProperty(value = "订单编号", example = "18078", notes = "模糊匹配")
|
||||
private String orderNo;
|
||||
|
||||
@ApiModelProperty(value = "商品 SPU 名称", example = "李四", notes = "模糊匹配")
|
||||
private String spuName;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@ApiModel("管理后台 - 交易售后拒绝收货 Request VO")
|
||||
@Data
|
||||
public class TradeAfterSaleRefuseReqVO {
|
||||
|
||||
@ApiModelProperty(value = "售后编号", required = true, example = "1024")
|
||||
@NotNull(message = "售后编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "收货备注", required = true, example = "你猜")
|
||||
@NotNull(message = "收货备注不能为空")
|
||||
private String refuseMemo;
|
||||
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("管理后台 - 交易售后分页的每一条记录 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TradeAfterSaleRespPageItemVO extends TradeAfterSaleBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "售后编号", required = true, example = "27630")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 商品属性数组
|
||||
*/
|
||||
private List<ProductPropertyValueDetailRespVO> properties;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private MemberUserRespVO user;
|
||||
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@ApiModel("管理后台 - 交易售后日志 Response VO")
|
||||
@Data
|
||||
public class TradeAfterSaleLogRespVO {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true, example = "20669")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "用户编号", required = true, example = "22634")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "用户类型", required = true, example = "2")
|
||||
@NotNull(message = "用户类型不能为空")
|
||||
private Integer userType;
|
||||
|
||||
@ApiModelProperty(value = "售后编号", required = true, example = "3023")
|
||||
@NotNull(message = "售后编号不能为空")
|
||||
private Long afterSaleId;
|
||||
|
||||
@ApiModelProperty(value = "订单编号", required = true, example = "25870")
|
||||
@NotNull(message = "订单编号不能为空")
|
||||
private Long orderId;
|
||||
|
||||
@ApiModelProperty(value = "订单项编号", required = true, example = "23154")
|
||||
@NotNull(message = "订单项编号不能为空")
|
||||
private Long orderItemId;
|
||||
|
||||
@ApiModelProperty(value = "售后状态(之前)", example = "2", notes = "参见 TradeAfterSaleStatusEnum 枚举")
|
||||
private Integer beforeStatus;
|
||||
|
||||
@ApiModelProperty(value = "售后状态(之后)", required = true, example = "1", notes = "参见 TradeAfterSaleStatusEnum 枚举")
|
||||
@NotNull(message = "售后状态(之后)不能为空")
|
||||
private Integer afterStatus;
|
||||
|
||||
@ApiModelProperty(value = "操作明细", required = true, example = "维权完成,退款金额:¥37776.00")
|
||||
@NotNull(message = "操作明细不能为空")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* 占位符,可忽略
|
||||
*/
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.base.member;
|
@@ -1,20 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.base.member.user;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@ApiModel("管理后台 - 会员用户 Response VO")
|
||||
@Data
|
||||
public class MemberUserRespVO {
|
||||
|
||||
@ApiModelProperty(value = "用户 ID", required = true, example = "1")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "用户昵称", required = true, example = "芋道源码")
|
||||
private String nickname;
|
||||
|
||||
@ApiModelProperty(value = "用户头像", example = "https://www.iocoder.cn/xxx.png")
|
||||
private String avatar;
|
||||
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* 放置该模块通用的 VO 类
|
||||
*/
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.base;
|
@@ -1,23 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.base.product.property;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@ApiModel("管理后台 - 商品属性值的明细 Response VO")
|
||||
@Data
|
||||
public class ProductPropertyValueDetailRespVO {
|
||||
|
||||
@ApiModelProperty(value = "属性的编号", required = true, example = "1")
|
||||
private Long propertyId;
|
||||
|
||||
@ApiModelProperty(value = "属性的名称", required = true, example = "颜色")
|
||||
private String propertyName;
|
||||
|
||||
@ApiModelProperty(value = "属性值的编号", required = true, example = "1024")
|
||||
private Long valueId;
|
||||
|
||||
@ApiModelProperty(value = "属性值的名称", required = true, example = "红色")
|
||||
private String valueName;
|
||||
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
### 获得交易订单分页 => 成功
|
||||
GET {{baseUrl}}/trade/order/page?pageNo=1&pageSize=10
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
### 获得交易订单分页 => 成功
|
||||
GET {{baseUrl}}/trade/order/get-detail?id=21
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
@@ -1,93 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
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.service.order.TradeOrderService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
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.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Api(tags = "管理后台 - 交易订单")
|
||||
@RestController
|
||||
@RequestMapping("/trade/order")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class TradeOrderController {
|
||||
|
||||
@Resource
|
||||
private TradeOrderService tradeOrderService;
|
||||
|
||||
@Resource
|
||||
private ProductPropertyValueApi productPropertyValueApi;
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@GetMapping("/page")
|
||||
@ApiOperation("获得交易订单分页")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
public CommonResult<PageResult<TradeOrderPageItemRespVO>> getOrderPage(TradeOrderPageReqVO reqVO) {
|
||||
// 查询订单
|
||||
PageResult<TradeOrderDO> pageResult = tradeOrderService.getOrderPage(reqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
// 查询订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderService.getOrderItemListByOrderId(
|
||||
convertSet(pageResult.getList(), TradeOrderDO::getId));
|
||||
// 查询商品属性
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails = productPropertyValueApi
|
||||
.getPropertyValueDetailList(TradeOrderConvert.INSTANCE.convertPropertyValueIds(orderItems));
|
||||
// 最终组合
|
||||
return success(TradeOrderConvert.INSTANCE.convertPage(pageResult, orderItems, propertyValueDetails));
|
||||
}
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@ApiOperation("获得交易订单详情")
|
||||
@ApiImplicitParam(name = "id", value = "订单编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
public CommonResult<TradeOrderDetailRespVO> getOrderDetail(@RequestParam("id") Long id) {
|
||||
// 查询订单
|
||||
TradeOrderDO order = tradeOrderService.getOrder(id);
|
||||
// 查询订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderService.getOrderItemListByOrderId(id);
|
||||
// 查询商品属性
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails = productPropertyValueApi
|
||||
.getPropertyValueDetailList(TradeOrderConvert.INSTANCE.convertPropertyValueIds(orderItems));
|
||||
// 查询会员
|
||||
MemberUserRespDTO user = memberUserApi.getUser(order.getUserId());
|
||||
// 最终组合
|
||||
return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, propertyValueDetails, user));
|
||||
}
|
||||
|
||||
@PostMapping("/delivery")
|
||||
@ApiOperation("发货订单")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:delivery')")
|
||||
public CommonResult<Boolean> deliveryOrder(@RequestBody TradeOrderDeliveryReqVO deliveryReqVO) {
|
||||
tradeOrderService.deliveryOrder(getLoginUserId(), deliveryReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
@@ -1,145 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 交易订单 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class TradeOrderBaseVO {
|
||||
|
||||
// ========== 订单基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "订单编号", required = true, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "订单流水号", required = true, example = "1146347329394184195")
|
||||
private String no;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true, notes = "下单时间")
|
||||
private Date createTime;
|
||||
|
||||
@ApiModelProperty(value = "订单类型", required = true, example = "1", notes = "参见 TradeOrderTypeEnum 枚举")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "订单来源", required = true, example = "1", notes = "参见 TerminalEnum 枚举")
|
||||
private Integer terminal;
|
||||
|
||||
@ApiModelProperty(value = "用户编号", required = true, example = "2048")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "用户 IP", required = true, example = "127.0.0.1")
|
||||
private String userIp;
|
||||
|
||||
@ApiModelProperty(value = "用户备注", required = true, example = "你猜")
|
||||
private String userRemark;
|
||||
|
||||
@ApiModelProperty(value = "订单状态", required = true, example = "1", notes = "参见 TradeOrderStatusEnum 枚举")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "购买的商品数量", required = true, example = "10")
|
||||
private Integer productCount;
|
||||
|
||||
@ApiModelProperty(value = "订单完成时间")
|
||||
private LocalDateTime finishTime;
|
||||
|
||||
@ApiModelProperty(value = "订单取消时间")
|
||||
private LocalDateTime cancelTime;
|
||||
|
||||
@ApiModelProperty(value = "取消类型", example = "10", notes = "参见 TradeOrderCancelTypeEnum 枚举")
|
||||
private Integer cancelType;
|
||||
|
||||
@ApiModelProperty(value = "商家备注", example = "你猜一下")
|
||||
private String remark;
|
||||
|
||||
// ========== 价格 + 支付基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "支付订单编号", required = true, example = "1024")
|
||||
private Long payOrderId;
|
||||
|
||||
@ApiModelProperty(value = "是否已支付", required = true, example = "true")
|
||||
private Boolean payed;
|
||||
|
||||
@ApiModelProperty(value = "付款时间")
|
||||
private LocalDateTime payTime;
|
||||
|
||||
@ApiModelProperty(value = "支付渠道", required = true, example = "wx_lite", notes = "参见 PayChannelEnum 枚举")
|
||||
private String payChannelCode;
|
||||
|
||||
@ApiModelProperty(value = "商品原价(总)", required = true, example = "1000", notes = "单位:分")
|
||||
private Integer originalPrice;
|
||||
|
||||
@ApiModelProperty(value = "订单原价(总)", required = true, example = "1000", notes = "单位:分")
|
||||
private Integer orderPrice;
|
||||
|
||||
@ApiModelProperty(value = "订单优惠(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer discountPrice;
|
||||
|
||||
@ApiModelProperty(value = "运费金额", required = true, example = "100", notes = "单位:分")
|
||||
private Integer deliveryPrice;
|
||||
|
||||
@ApiModelProperty(value = "订单调价(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer adjustPrice;
|
||||
|
||||
@ApiModelProperty(value = "应付金额(总)", required = true, example = "1000", notes = "单位:分")
|
||||
private Integer payPrice;
|
||||
|
||||
// ========== 收件 + 物流基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "配送模板编号", example = "1024")
|
||||
private Long deliveryTemplateId;
|
||||
|
||||
@ApiModelProperty(value = "发货物流公司编号", example = "1024")
|
||||
private Long logisticsId;
|
||||
|
||||
@ApiModelProperty(value = "发货物流单号", example = "1024")
|
||||
private String logisticsNo;
|
||||
|
||||
@ApiModelProperty(value = "发货状态", required = true, example = "1", notes = "参见 TradeOrderDeliveryStatusEnum 枚举")
|
||||
private Integer deliveryStatus;
|
||||
|
||||
@ApiModelProperty(value = "发货时间")
|
||||
private LocalDateTime deliveryTime;
|
||||
|
||||
@ApiModelProperty(value = "收货时间")
|
||||
private LocalDateTime receiveTime;
|
||||
|
||||
@ApiModelProperty(value = "收件人名称", required = true, example = "张三")
|
||||
private String receiverName;
|
||||
|
||||
@ApiModelProperty(value = "收件人手机", required = true, example = "13800138000")
|
||||
private String receiverMobile;
|
||||
|
||||
@ApiModelProperty(value = "收件人地区编号", required = true, example = "110000")
|
||||
private Integer receiverAreaId;
|
||||
|
||||
@ApiModelProperty(value = "收件人邮编", required = true, example = "100000")
|
||||
private Integer receiverPostCode;
|
||||
|
||||
@ApiModelProperty(value = "收件人详细地址", required = true, example = "中关村大街 1 号")
|
||||
private String receiverDetailAddress;
|
||||
|
||||
// ========== 售后基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "售后状态", example = "1", notes = "参见 TradeOrderAfterSaleStatusEnum 枚举")
|
||||
private Integer afterSaleStatus;
|
||||
|
||||
@ApiModelProperty(value = "退款金额", required = true, example = "100", notes = "单位:分")
|
||||
private Integer refundPrice;
|
||||
|
||||
// ========== 营销基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "优惠劵编号", example = "1024")
|
||||
private Long couponId;
|
||||
|
||||
@ApiModelProperty(value = "优惠劵减免金额", required = true, example = "100", notes = "单位:分")
|
||||
private Integer couponPrice;
|
||||
|
||||
@ApiModelProperty(value = "积分抵扣的金额", required = true, example = "100", notes = "单位:分")
|
||||
private Integer pointPrice;
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("管理后台 - 交易订单的详情 Response VO")
|
||||
@Data
|
||||
public class TradeOrderDetailRespVO extends TradeOrderBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "收件人地区名字", required = true, example = "上海 上海市 普陀区")
|
||||
private String receiverAreaName;
|
||||
|
||||
/**
|
||||
* 订单项列表
|
||||
*/
|
||||
private List<Item> items;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private MemberUserRespVO user;
|
||||
|
||||
@ApiModel("管理后台 - 交易订单的详情的订单项目")
|
||||
@Data
|
||||
public static class Item extends TradeOrderItemBaseVO {
|
||||
|
||||
/**
|
||||
* 属性数组
|
||||
*/
|
||||
private List<ProductPropertyValueDetailRespVO> properties;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 交易订单项 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class TradeOrderItemBaseVO {
|
||||
|
||||
// ========== 订单项基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true, example = "1")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "用户编号", required = true, example = "1")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "订单编号", required = true, example = "1")
|
||||
private Long orderId;
|
||||
|
||||
// ========== 商品基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
|
||||
private Long spuId;
|
||||
|
||||
@ApiModelProperty(value = "商品 SPU 名称", required = true, example = "芋道源码")
|
||||
private String spuName;
|
||||
|
||||
@ApiModelProperty(value = "商品 SKU 编号", required = true, example = "1")
|
||||
private Long skuId;
|
||||
|
||||
@ApiModelProperty(value = "商品图片", required = true, example = "https://www.iocoder.cn/1.png")
|
||||
private String picUrl;
|
||||
|
||||
@ApiModelProperty(value = "购买数量", required = true, example = "1")
|
||||
private Integer count;
|
||||
|
||||
// ========== 价格 + 支付基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "商品原价(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer originalPrice;
|
||||
|
||||
@ApiModelProperty(value = "商品原价(单)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer originalUnitPrice;
|
||||
|
||||
@ApiModelProperty(value = "商品优惠(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer discountPrice;
|
||||
|
||||
@ApiModelProperty(value = "商品实付金额(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer payPrice;
|
||||
|
||||
@ApiModelProperty(value = "子订单分摊金额(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer orderPartPrice;
|
||||
|
||||
@ApiModelProperty(value = "分摊后子订单实付金额(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer orderDividePrice;
|
||||
|
||||
// ========== 营销基本信息 ==========
|
||||
|
||||
// TODO 芋艿:在捉摸一下
|
||||
|
||||
// ========== 售后基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "售后状态", required = true, example = "1", notes = "参见 TradeOrderItemAfterSaleStatusEnum 枚举类")
|
||||
private Integer afterSaleStatus;
|
||||
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("管理后台 - 交易订单的分页项 Response VO")
|
||||
@Data
|
||||
public class TradeOrderPageItemRespVO extends TradeOrderBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "收件人地区名字", required = true, example = "上海 上海市 普陀区")
|
||||
private String receiverAreaName;
|
||||
|
||||
/**
|
||||
* 订单项列表
|
||||
*/
|
||||
private List<Item> items;
|
||||
|
||||
@ApiModel("管理后台 - 交易订单的分页项的订单项目")
|
||||
@Data
|
||||
public static class Item extends TradeOrderItemBaseVO {
|
||||
|
||||
/**
|
||||
* 属性数组
|
||||
*/
|
||||
private List<ProductPropertyValueDetailRespVO> properties;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@ApiModel("管理后台 - 交易订单的分页 Request VO")
|
||||
@Data
|
||||
public class TradeOrderPageReqVO extends PageParam {
|
||||
|
||||
@ApiModelProperty(value = "订单号", example = "88888888", notes = "模糊匹配")
|
||||
private String no;
|
||||
|
||||
@ApiModelProperty(value = "用户编号", example = "1024")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "用户昵称", example = "小王", notes = "模糊匹配")
|
||||
private String userNickname;
|
||||
|
||||
@ApiModelProperty(value = "用户手机号", example = "小王", notes = "精准匹配")
|
||||
@Mobile
|
||||
private String userMobile;
|
||||
|
||||
@ApiModelProperty(value = "收件人名称", example = "小红", notes = "模糊匹配")
|
||||
private String receiverName;
|
||||
|
||||
@ApiModelProperty(value = "收件人手机", example = "1560", notes = "模糊匹配")
|
||||
@Mobile
|
||||
private String receiverMobile;
|
||||
|
||||
@ApiModelProperty(value = "订单类型", example = "1", notes = "参见 TradeOrderTypeEnum 枚举")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "订单状态", example = "1", notes = "参见 TradeOrderStatusEnum 枚举")
|
||||
@InEnum(value = TradeOrderStatusEnum.class, message = "订单状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "支付渠道", example = "wx_lite")
|
||||
private String payChannelCode;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
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 = "用户 App - 交易售后")
|
||||
@RestController
|
||||
@RequestMapping("/trade/after-sale")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppTradeAfterSaleController {
|
||||
|
||||
@Resource
|
||||
private TradeAfterSaleService afterSaleService;
|
||||
|
||||
@PostMapping(value = "/create")
|
||||
@ApiOperation(value = "申请售后")
|
||||
public CommonResult<Long> createAfterSale(@RequestBody AppTradeAfterSaleCreateReqVO createReqVO) {
|
||||
return success(afterSaleService.createAfterSale(getLoginUserId(), createReqVO));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/delivery")
|
||||
@ApiOperation(value = "退回货物")
|
||||
public CommonResult<Boolean> deliveryAfterSale(@RequestBody AppTradeAfterSaleDeliveryReqVO deliveryReqVO) {
|
||||
afterSaleService.deliveryAfterSale(getLoginUserId(), deliveryReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping(value = "/cancel")
|
||||
@ApiOperation(value = "取消售后")
|
||||
@ApiImplicitParam(name = "id", value = "售后编号", required = true, example = "1")
|
||||
public CommonResult<Boolean> cancelAfterSale(@RequestParam("id") Long id) {
|
||||
afterSaleService.cancelAfterSale(getLoginUserId(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("用户 App - 交易售后创建 Request VO")
|
||||
@Data
|
||||
public class AppTradeAfterSaleCreateReqVO {
|
||||
|
||||
@ApiModelProperty(name = "订单项编号", required = true, example = "1024")
|
||||
@NotNull(message = "订单项编号不能为空")
|
||||
private Long orderItemId;
|
||||
|
||||
@ApiModelProperty(name = "售后方式", required = true, example = "1", notes = "对应 TradeAfterSaleWayEnum 枚举")
|
||||
@NotNull(message = "售后方式不能为空")
|
||||
@InEnum(value = TradeAfterSaleWayEnum.class, message = "售后方式必须是 {value}")
|
||||
private Integer way;
|
||||
|
||||
@ApiModelProperty(name = "退款金额", required = true, example = "100", notes = "单位:分")
|
||||
@NotNull(message = "退款金额不能为空")
|
||||
@Min(value = 1, message = "退款金额必须大于 0")
|
||||
private Integer refundPrice;
|
||||
|
||||
@ApiModelProperty(name = "申请原因", required = true, example = "1", notes = "使用数据字典枚举,对应 trade_refund_apply_reason 类型")
|
||||
@NotNull(message = "申请原因不能为空")
|
||||
private String applyReason;
|
||||
|
||||
@ApiModelProperty(name = "补充描述", example = "商品质量不好")
|
||||
private String applyDescription;
|
||||
|
||||
@ApiModelProperty(name = "补充凭证图片", example = "https://www.iocoder.cn/1.png, https://www.iocoder.cn/2.png")
|
||||
private List<String> applyPicUrls;
|
||||
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.aftersale.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@ApiModel("用户 App - 交易售后退回货物 Request VO")
|
||||
@Data
|
||||
public class AppTradeAfterSaleDeliveryReqVO {
|
||||
|
||||
@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")
|
||||
@NotNull(message = "退货物流单号不能为空")
|
||||
private String logisticsNo;
|
||||
|
||||
@ApiModelProperty(name = "退货时间", required = true)
|
||||
@NotEmpty(message = "退货时间不能为空")
|
||||
private LocalDateTime deliveryTime;
|
||||
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* 基础包,放一些通用的 VO 类
|
||||
*/
|
||||
package cn.iocoder.yudao.module.trade.controller.app.base;
|
@@ -1,22 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.base.property;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description ="用户 App - 商品属性值的明细 Response VO")
|
||||
@Data
|
||||
public class AppProductPropertyValueDetailRespVO {
|
||||
|
||||
@Schema(description = "属性的编号", required = true, example = "1")
|
||||
private Long propertyId;
|
||||
|
||||
@Schema(description = "属性的名称", required = true, example = "颜色")
|
||||
private String propertyName;
|
||||
|
||||
@Schema(description = "属性值的编号", required = true, example = "1024")
|
||||
private Long valueId;
|
||||
|
||||
@Schema(description = "属性值的名称", required = true, example = "红色")
|
||||
private String valueName;
|
||||
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.base.sku;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品 SKU 基础 Response VO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class AppProductSkuBaseRespVO {
|
||||
|
||||
@Schema(description = "主键", required = true, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品 SKU 名字", required = true, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "图片地址", example = "https://www.iocoder.cn/xx.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "库存", required = true, example = "1")
|
||||
private Integer stock;
|
||||
|
||||
/**
|
||||
* 属性数组
|
||||
*/
|
||||
private List<AppProductPropertyValueDetailRespVO> properties;
|
||||
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.base.spu;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品 SPU 基础 Response VO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class AppProductSpuBaseRespVO {
|
||||
|
||||
@Schema(description = "主键", required = true, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品 SPU 名字", required = true, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商品主图地址", example = "https://www.iocoder.cn/xx.png")
|
||||
private List<String> picUrls;
|
||||
|
||||
}
|
@@ -1,47 +0,0 @@
|
||||
### 请求 /trade/cart/add-count 接口 => 成功
|
||||
POST {{appApi}}/trade/cart/add-count
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"skuId": 1,
|
||||
"count": 1
|
||||
}
|
||||
|
||||
### 请求 /trade/cart/update-count 接口 => 成功
|
||||
PUT {{appApi}}/trade/cart/update-count
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"skuId": 1,
|
||||
"count": 5
|
||||
}
|
||||
|
||||
### 请求 /trade/cart/update-selected 接口 => 成功
|
||||
PUT {{appApi}}/trade/cart/update-selected
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"skuIds": [1],
|
||||
"selected": false
|
||||
}
|
||||
|
||||
### 请求 /trade/cart/delete 接口 => 成功
|
||||
DELETE {{appApi}}/trade/cart/delete?skuIds=1
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
||||
### 请求 /trade/cart/get-count 接口 => 成功
|
||||
GET {{appApi}}/trade/cart/get-count
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
||||
### 请求 /trade/cart/get-detail 接口 => 成功
|
||||
GET {{appApi}}/trade/cart/get-detail
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
@@ -1,84 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
|
||||
import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "用户 App - 购物车")
|
||||
@RestController
|
||||
@RequestMapping("/trade/cart")
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class TradeCartController {
|
||||
|
||||
@Resource
|
||||
private TradeCartService cartService;
|
||||
|
||||
@PostMapping("/add-count")
|
||||
@Operation(summary = "添加商品到购物车")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> addCartItemCount(@Valid @RequestBody AppTradeCartItemAddCountReqVO addCountReqVO) {
|
||||
cartService.addCartItemCount(getLoginUserId(), addCountReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("update-count")
|
||||
@Operation(summary = "更新购物车商品数量")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> updateCartItemQuantity(@Valid @RequestBody AppTradeCartItemUpdateCountReqVO updateCountReqVO) {
|
||||
cartService.updateCartItemCount(getLoginUserId(), updateCountReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("update-selected")
|
||||
@Operation(summary = "更新购物车商品是否选中")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> updateCartItemSelected(@Valid @RequestBody AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) {
|
||||
cartService.updateCartItemSelected(getLoginUserId(), updateSelectedReqVO);
|
||||
// 获得目前购物车明细
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除购物车商品")
|
||||
@Parameter(name = "skuIds", description = "商品 SKU 编号的数组", required = true, example = "1024,2048")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> deleteCartItem(@RequestParam("skuIds") List<Long> skuIds) {
|
||||
cartService.deleteCartItems(getLoginUserId(), skuIds);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("get-count")
|
||||
@Operation(summary = "查询用户在购物车中的商品数量")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Integer> getCartCount() {
|
||||
return success(cartService.getCartCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "查询用户的购物车的详情")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppTradeCartDetailRespVO> getCartDetail() {
|
||||
return success(cartService.getCartDetail(getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
@@ -1,117 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 用户的购物车明细 Response VO")
|
||||
@Data
|
||||
public class AppTradeCartDetailRespVO {
|
||||
|
||||
/**
|
||||
* 商品分组数组
|
||||
*/
|
||||
private List<ItemGroup> itemGroups;
|
||||
|
||||
/**
|
||||
* 费用
|
||||
*/
|
||||
private Order order;
|
||||
|
||||
@Schema(description = "商品分组,多个商品,参加同一个活动,从而形成分组")
|
||||
@Data
|
||||
public static class ItemGroup {
|
||||
|
||||
/**
|
||||
* 商品数组
|
||||
*/
|
||||
private List<Sku> items;
|
||||
/**
|
||||
* 营销活动,订单级别
|
||||
*/
|
||||
private Promotion promotion;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "商品 SKU")
|
||||
@Data
|
||||
public static class Sku extends AppProductSkuBaseRespVO {
|
||||
|
||||
/**
|
||||
* SPU 信息
|
||||
*/
|
||||
private AppProductSkuBaseRespVO spu;
|
||||
|
||||
// ========== 购物车相关的字段 ==========
|
||||
|
||||
@Schema(description = "商品数量", required = true, example = "1")
|
||||
private Integer count;
|
||||
@Schema(description = "是否选中", required = true, example = "true")
|
||||
private Boolean selected;
|
||||
|
||||
// ========== 价格相关的字段,对应 PriceCalculateRespDTO.OrderItem 的属性 ==========
|
||||
|
||||
// TODO 芋艿:后续可以去除一些无用的字段
|
||||
|
||||
@Schema(description = "商品原价(单)", required = true, example = "100")
|
||||
private Integer originalPrice;
|
||||
@Schema(description = "商品原价(总)", required = true, example = "200")
|
||||
private Integer totalOriginalPrice;
|
||||
@Schema(description = "商品级优惠(总)", required = true, example = "300")
|
||||
private Integer totalPromotionPrice;
|
||||
@Schema(description = "最终购买金额(总)", required = true, example = "400")
|
||||
private Integer totalPresentPrice;
|
||||
@Schema(description = "最终购买金额(单)", required = true, example = "500")
|
||||
private Integer presentPrice;
|
||||
@Schema(description = "应付金额(总)", required = true, example = "600")
|
||||
private Integer totalPayPrice;
|
||||
|
||||
// ========== 营销相关的字段 ==========
|
||||
/**
|
||||
* 营销活动,商品级别
|
||||
*/
|
||||
private Promotion promotion;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "订单,对应 PriceCalculateRespDTO.Order 类,用于费用(合计)")
|
||||
@Data
|
||||
public static class Order {
|
||||
|
||||
// TODO 芋艿:后续可以去除一些无用的字段
|
||||
|
||||
@Schema(description = "商品原价(总)", required = true, example = "100")
|
||||
private Integer skuOriginalPrice;
|
||||
@Schema(description = "商品优惠(总)", required = true, example = "200")
|
||||
private Integer skuPromotionPrice;
|
||||
@Schema(description = "订单优惠(总)", required = true, example = "300")
|
||||
private Integer orderPromotionPrice;
|
||||
@Schema(description = "运费金额", required = true, example = "400")
|
||||
private Integer deliveryPrice;
|
||||
@Schema(description = "应付金额(总)", required = true, example = "500")
|
||||
private Integer payPrice;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "营销活动,对应 PriceCalculateRespDTO.Promotion 类的属性")
|
||||
@Data
|
||||
public static class Promotion {
|
||||
|
||||
@Schema(description = "营销编号,营销活动的编号、优惠劵的编号", required = true, example = "1024")
|
||||
private Long id;
|
||||
@Schema(description = "营销名字", required = true, example = "xx 活动")
|
||||
private String name;
|
||||
@Schema(description = "营销类型,参见 PromotionTypeEnum 枚举类", required = true, example = "1")
|
||||
private Integer type;
|
||||
|
||||
// ========== 匹配情况 ==========
|
||||
@Schema(description = "是否满足优惠条件", required = true, example = "true")
|
||||
private Boolean meet;
|
||||
@Schema(description = "满足条件的提示", required = true, example = "圣诞价:省 150.00 元")
|
||||
private String meetTip;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "用户 App - 购物车添加购物项 Request VO")
|
||||
@Data
|
||||
public class AppTradeCartItemAddCountReqVO {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", required = true,example = "1024")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "商品数量,注意,这是新增数量", required = true, example = "1")
|
||||
@NotNull(message = "数量不能为空")
|
||||
@Min(message = "数量必须大于 0", value = 1L)
|
||||
private Integer count;
|
||||
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "用户 App - 购物车更新数量 Request VO")
|
||||
@Data
|
||||
public class AppTradeCartItemUpdateCountReqVO {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", required = true, example = "1024")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "商品数量", required = true, example = "1")
|
||||
@NotNull(message = "数量不能为空")
|
||||
@Min(message = "数量必须大于 0", value = 1L)
|
||||
private Integer count;
|
||||
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collection;
|
||||
|
||||
@Schema(description = "用户 App - 购物车更新是否选中 Request VO")
|
||||
@Data
|
||||
public class AppTradeCartItemUpdateSelectedReqVO {
|
||||
|
||||
@Schema(description = "商品 SKU 编号列表", required = true, example = "1024,2048")
|
||||
@NotNull(message = "商品 SKU 编号列表不能为空")
|
||||
private Collection<Long> skuIds;
|
||||
|
||||
@Schema(description = "是否选中", required = true, example = "true")
|
||||
@NotNull(message = "是否选中不能为空")
|
||||
private Boolean selected;
|
||||
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
### /trade-order/confirm-create-order-info 基于商品,确认创建订单
|
||||
GET {{appApi}}/trade/order/get-create-info?items[0].skuId=1&items[0].count=1
|
||||
Authorization: Bearer {{user-access-token}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### /trade-order/confirm-create-order-info-from-cart 基于购物车,确认创建订单
|
||||
GET {{shop-api-base-url}}/trade-order/confirm-create-order-info-from-cart
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Authorization: Bearer {{user-access-token}}
|
||||
|
||||
### /trade-order/create 基于商品,创建订单
|
||||
POST {{appApi}}/trade/order/create
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
{
|
||||
"addressId": 21,
|
||||
"remark": "我是备注",
|
||||
"fromCart": false,
|
||||
"items": [
|
||||
{
|
||||
"skuId": 29,
|
||||
"count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
### 获得订单交易的分页
|
||||
GET {{appApi}}/trade/order/page?pageNo=1&pageSize=10
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### 获得订单交易的详细
|
||||
GET {{appApi}}/trade/order/get-detail?id=21
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
@@ -1,102 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order;
|
||||
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
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.service.order.TradeOrderService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "用户 App - 交易订单")
|
||||
@RestController
|
||||
@RequestMapping("/trade/order")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppTradeOrderController {
|
||||
|
||||
@Resource
|
||||
private TradeOrderService tradeOrderService;
|
||||
|
||||
@Resource
|
||||
private ProductPropertyValueApi productPropertyValueApi;
|
||||
|
||||
@GetMapping("/get-create-info")
|
||||
@Operation(summary = "基于商品,确认创建订单")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppTradeOrderGetCreateInfoRespVO> getOrderCreateInfo(AppTradeOrderCreateReqVO createReqVO) {
|
||||
// return success(tradeOrderService.getOrderConfirmCreateInfo(UserSecurityContextHolder.getUserId(), skuId, quantity, couponCardId));
|
||||
return null;
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建订单")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Long> createOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO,
|
||||
HttpServletRequest servletRequest) {
|
||||
// 获取登录用户、用户 IP 地址
|
||||
Long loginUserId = getLoginUserId();
|
||||
String clientIp = ServletUtil.getClientIP(servletRequest);
|
||||
// 创建交易订单,预支付记录
|
||||
Long orderId = tradeOrderService.createOrder(loginUserId, clientIp, createReqVO);
|
||||
return success(orderId);
|
||||
}
|
||||
|
||||
@PostMapping("/update-paid")
|
||||
@Operation(summary = "更新订单为已支付", notes = "由 pay-module 支付服务,进行回调,可见 PayNotifyJob")
|
||||
public CommonResult<Boolean> updateOrderPaid(@RequestBody PayOrderNotifyReqDTO notifyReqDTO) {
|
||||
tradeOrderService.updateOrderPaid(Long.valueOf(notifyReqDTO.getMerchantOrderId()),
|
||||
notifyReqDTO.getPayOrderId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "获得交易订单")
|
||||
@Parameter(name = "id", description = "交易订单编号", required = true)
|
||||
public CommonResult<AppTradeOrderDetailRespVO> getOrder(@RequestParam("id") Long id) {
|
||||
// 查询订单
|
||||
TradeOrderDO order = tradeOrderService.getOrder(getLoginUserId(), id);
|
||||
// 查询订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderService.getOrderItemListByOrderId(order.getId());
|
||||
// 查询商品属性
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails = productPropertyValueApi
|
||||
.getPropertyValueDetailList(TradeOrderConvert.INSTANCE.convertPropertyValueIds(orderItems));
|
||||
// 最终组合
|
||||
return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, propertyValueDetails));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得订单交易分页")
|
||||
public CommonResult<PageResult<AppTradeOrderPageItemRespVO>> getOrderPage(AppTradeOrderPageReqVO reqVO) {
|
||||
// 查询订单
|
||||
PageResult<TradeOrderDO> pageResult = tradeOrderService.getOrderPage(getLoginUserId(), reqVO);
|
||||
// 查询订单项
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderService.getOrderItemListByOrderId(
|
||||
convertSet(pageResult.getList(), TradeOrderDO::getId));
|
||||
// 查询商品属性
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails = productPropertyValueApi
|
||||
.getPropertyValueDetailList(TradeOrderConvert.INSTANCE.convertPropertyValueIds(orderItems));
|
||||
// 最终组合
|
||||
return success(TradeOrderConvert.INSTANCE.convertPage02(pageResult, orderItems, propertyValueDetails));
|
||||
}
|
||||
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 交易订单创建 Request VO")
|
||||
@Data
|
||||
public class AppTradeOrderCreateReqVO {
|
||||
|
||||
@Schema(description = "收件地址编号", required = true, example = "1")
|
||||
@NotNull(message = "收件地址不能为空")
|
||||
private Long addressId;
|
||||
|
||||
@Schema(description = "优惠劵编号", example = "1024")
|
||||
private Long couponId;
|
||||
|
||||
@Schema(description = "备注", example = "这个是我的订单哟")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "是否来自购物车,true - 来自购物车;false - 立即购买", required = true, example = "true")
|
||||
@NotNull(message = "是否来自购物车不能为空")
|
||||
private Boolean fromCart;
|
||||
|
||||
/**
|
||||
* 订单商品项列表
|
||||
*/
|
||||
@NotEmpty(message = "必须选择购买的商品")
|
||||
@Valid
|
||||
private List<Item> items;
|
||||
|
||||
@Schema(description = "订单商品项")
|
||||
@Data
|
||||
public static class Item {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", required = true, example = "111")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "商品 SKU 购买数量", required = true, example = "1024")
|
||||
@NotNull(message = "商品 SKU 购买数量不能为空")
|
||||
@Min(value = 1, message = "商品 SKU 购买数量必须大于 0")
|
||||
private Integer count;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,150 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("用户 App - 订单交易的明细 Response VO")
|
||||
@Data
|
||||
public class AppTradeOrderDetailRespVO {
|
||||
|
||||
// ========== 订单基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "订单编号", required = true, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "订单流水号", required = true, example = "1146347329394184195")
|
||||
private String no;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true, notes = "下单时间")
|
||||
private Date createTime;
|
||||
|
||||
@ApiModelProperty(value = "用户备注", required = true, example = "你猜")
|
||||
private String userRemark;
|
||||
|
||||
@ApiModelProperty(value = "订单状态", required = true, example = "1", notes = "参见 TradeOrderStatusEnum 枚举")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "购买的商品数量", required = true, example = "10")
|
||||
private Integer productCount;
|
||||
|
||||
@ApiModelProperty(value = "订单完成时间")
|
||||
private LocalDateTime finishTime;
|
||||
|
||||
@ApiModelProperty(value = "订单取消时间")
|
||||
private LocalDateTime cancelTime;
|
||||
|
||||
// ========== 价格 + 支付基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "支付订单编号", required = true, example = "1024")
|
||||
private Long payOrderId;
|
||||
|
||||
@ApiModelProperty(value = "付款时间")
|
||||
private LocalDateTime payTime;
|
||||
|
||||
@ApiModelProperty(value = "商品原价(总)", required = true, example = "1000", notes = "单位:分")
|
||||
private Integer originalPrice;
|
||||
|
||||
@ApiModelProperty(value = "订单原价(总)", required = true, example = "1000", notes = "单位:分")
|
||||
private Integer orderPrice;
|
||||
|
||||
@ApiModelProperty(value = "订单优惠(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer discountPrice;
|
||||
|
||||
@ApiModelProperty(value = "运费金额", required = true, example = "100", notes = "单位:分")
|
||||
private Integer deliveryPrice;
|
||||
|
||||
@ApiModelProperty(value = "订单调价(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer adjustPrice;
|
||||
|
||||
@ApiModelProperty(value = "应付金额(总)", required = true, example = "1000", notes = "单位:分")
|
||||
private Integer payPrice;
|
||||
|
||||
// ========== 收件 + 物流基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "发货物流单号", example = "1024")
|
||||
private String logisticsNo;
|
||||
|
||||
@ApiModelProperty(value = "发货时间")
|
||||
private LocalDateTime deliveryTime;
|
||||
|
||||
@ApiModelProperty(value = "收货时间")
|
||||
private LocalDateTime receiveTime;
|
||||
|
||||
@ApiModelProperty(value = "收件人名称", required = true, example = "张三")
|
||||
private String receiverName;
|
||||
|
||||
@ApiModelProperty(value = "收件人手机", required = true, example = "13800138000")
|
||||
private String receiverMobile;
|
||||
|
||||
@ApiModelProperty(value = "收件人地区编号", required = true, example = "110000")
|
||||
private Integer receiverAreaId;
|
||||
|
||||
@ApiModelProperty(value = "收件人地区名字", required = true, example = "上海 上海市 普陀区")
|
||||
private String receiverAreaName;
|
||||
|
||||
@ApiModelProperty(value = "收件人邮编", required = true, example = "100000")
|
||||
private Integer receiverPostCode;
|
||||
|
||||
@ApiModelProperty(value = "收件人详细地址", required = true, example = "中关村大街 1 号")
|
||||
private String receiverDetailAddress;
|
||||
|
||||
// ========== 售后基本信息 ==========
|
||||
|
||||
// ========== 营销基本信息 ==========
|
||||
|
||||
@ApiModelProperty(value = "优惠劵编号", example = "1024")
|
||||
private Long couponId;
|
||||
|
||||
@ApiModelProperty(value = "优惠劵减免金额", required = true, example = "100", notes = "单位:分")
|
||||
private Integer couponPrice;
|
||||
|
||||
@ApiModelProperty(value = "积分抵扣的金额", required = true, example = "100", notes = "单位:分")
|
||||
private Integer pointPrice;
|
||||
|
||||
/**
|
||||
* 订单项数组
|
||||
*/
|
||||
private List<Item> items;
|
||||
|
||||
@ApiModel("用户 App - 交易订单的分页项的订单项目")
|
||||
@Data
|
||||
public static class Item {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true, example = "1")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
|
||||
private Long spuId;
|
||||
|
||||
@ApiModelProperty(value = "商品 SPU 名称", required = true, example = "芋道源码")
|
||||
private String spuName;
|
||||
|
||||
@ApiModelProperty(value = "商品 SKU 编号", required = true, example = "1")
|
||||
private Long skuId;
|
||||
|
||||
@ApiModelProperty(value = "商品图片", required = true, example = "https://www.iocoder.cn/1.png")
|
||||
private String picUrl;
|
||||
|
||||
@ApiModelProperty(value = "购买数量", required = true, example = "1")
|
||||
private Integer count;
|
||||
|
||||
@ApiModelProperty(value = "商品原价(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer originalPrice;
|
||||
|
||||
@ApiModelProperty(value = "商品原价(单)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer originalUnitPrice;
|
||||
|
||||
/**
|
||||
* 属性数组
|
||||
*/
|
||||
private List<AppProductPropertyValueDetailRespVO> properties;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,168 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 订单获得创建信息 Response VO")
|
||||
@Data
|
||||
public class AppTradeOrderGetCreateInfoRespVO {
|
||||
|
||||
/**
|
||||
* 商品分组数组
|
||||
*/
|
||||
private List<ItemGroup> itemGroups;
|
||||
/**
|
||||
* 费用
|
||||
*/
|
||||
private Fee fee;
|
||||
|
||||
// /**
|
||||
// * 优惠劵列表 TODO 芋艿,后续改改
|
||||
// */
|
||||
// private List<CouponCardAvailableRespDTO> coupons;
|
||||
|
||||
@Schema(description = "商品分组,多个商品,参加同一个活动,从而形成分组")
|
||||
@Data
|
||||
public static class ItemGroup {
|
||||
|
||||
// /**
|
||||
// * 优惠活动
|
||||
// */
|
||||
// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
|
||||
/**
|
||||
* 商品 SKU 数组
|
||||
*/
|
||||
private List<Sku> items;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "商品 SKU")
|
||||
@Data
|
||||
public static class Sku {
|
||||
|
||||
// SKU 自带信息
|
||||
@Schema(description = "SKU 编号", required = true, example = "1024")
|
||||
private Integer id;
|
||||
/**
|
||||
* SPU 信息
|
||||
*/
|
||||
private Spu spu;
|
||||
/**
|
||||
* 图片地址
|
||||
*/
|
||||
private String picURL;
|
||||
// /**
|
||||
// * 属性数组
|
||||
// */
|
||||
// private List<ProductAttrKeyValueRespVO> attrs; // TODO 后面改下
|
||||
/**
|
||||
* 价格,单位:分
|
||||
*/
|
||||
private Integer price;
|
||||
/**
|
||||
* 库存数量
|
||||
*/
|
||||
private Integer stock;
|
||||
|
||||
// 非 SKU 自带信息
|
||||
|
||||
/**
|
||||
* 购买数量
|
||||
*/
|
||||
private Integer buyQuantity;
|
||||
// /**
|
||||
// * 优惠活动
|
||||
// */
|
||||
// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
|
||||
/**
|
||||
* 原始单价,单位:分。
|
||||
*/
|
||||
private Integer originPrice;
|
||||
/**
|
||||
* 购买单价,单位:分
|
||||
*/
|
||||
private Integer buyPrice;
|
||||
/**
|
||||
* 最终价格,单位:分。
|
||||
*/
|
||||
private Integer presentPrice;
|
||||
/**
|
||||
* 购买总金额,单位:分
|
||||
*
|
||||
* 用途类似 {@link #presentTotal}
|
||||
*/
|
||||
private Integer buyTotal;
|
||||
/**
|
||||
* 优惠总金额,单位:分。
|
||||
*/
|
||||
private Integer discountTotal;
|
||||
/**
|
||||
* 最终总金额,单位:分。
|
||||
*
|
||||
* 注意,presentPrice * quantity 不一定等于 presentTotal 。
|
||||
* 因为,存在无法整除的情况。
|
||||
* 举个例子,presentPrice = 8.33 ,quantity = 3 的情况,presentTotal 有可能是 24.99 ,也可能是 25 。
|
||||
* 所以,需要存储一个该字段。
|
||||
*/
|
||||
private Integer presentTotal;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Spu {
|
||||
|
||||
/**
|
||||
* SPU 编号
|
||||
*/
|
||||
private Integer id;
|
||||
|
||||
// ========== 基本信息 =========
|
||||
/**
|
||||
* SPU 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 分类编号
|
||||
*/
|
||||
private Integer cid;
|
||||
/**
|
||||
* 商品主图地址
|
||||
*
|
||||
* 数组,以逗号分隔
|
||||
*
|
||||
* 建议尺寸:800*800像素,你可以拖拽图片调整顺序,最多上传15张
|
||||
*/
|
||||
private List<String> picUrls;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "费用(合计)")
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class Fee {
|
||||
|
||||
@Schema(description = "购买总价", required = true, example = "1024")
|
||||
private Integer buyPrice;
|
||||
/**
|
||||
* 优惠总价
|
||||
*
|
||||
* 注意,满多少元包邮,不算在优惠中。
|
||||
*/
|
||||
private Integer discountTotal;
|
||||
/**
|
||||
* 邮费
|
||||
*/
|
||||
private Integer postageTotal;
|
||||
/**
|
||||
* 最终价格
|
||||
*
|
||||
* 计算公式 = 总价 - 优惠总价 + 邮费
|
||||
*/
|
||||
private Integer presentTotal;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ApiModel("用户 App - 订单交易的分页项 Response VO")
|
||||
@Data
|
||||
public class AppTradeOrderPageItemRespVO {
|
||||
|
||||
@ApiModelProperty(value = "订单编号", required = true, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "订单流水号", required = true, example = "1146347329394184195")
|
||||
private String no;
|
||||
|
||||
@ApiModelProperty(value = "订单状态", required = true, example = "1", notes = "参见 TradeOrderStatusEnum 枚举")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "购买的商品数量", required = true, example = "10")
|
||||
private Integer productCount;
|
||||
|
||||
/**
|
||||
* 订单项数组
|
||||
*/
|
||||
private List<Item> items;
|
||||
|
||||
@ApiModel("用户 App - 交易订单的明细的订单项目")
|
||||
@Data
|
||||
public static class Item {
|
||||
|
||||
@ApiModelProperty(value = "编号", required = true, example = "1")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
|
||||
private Long spuId;
|
||||
|
||||
@ApiModelProperty(value = "商品 SPU 名称", required = true, example = "芋道源码")
|
||||
private String spuName;
|
||||
|
||||
@ApiModelProperty(value = "商品 SKU 编号", required = true, example = "1")
|
||||
private Long skuId;
|
||||
|
||||
@ApiModelProperty(value = "商品图片", required = true, example = "https://www.iocoder.cn/1.png")
|
||||
private String picUrl;
|
||||
|
||||
@ApiModelProperty(value = "购买数量", required = true, example = "1")
|
||||
private Integer count;
|
||||
|
||||
@ApiModelProperty(value = "商品原价(总)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer originalPrice;
|
||||
|
||||
@ApiModelProperty(value = "商品原价(单)", required = true, example = "100", notes = "单位:分")
|
||||
private Integer originalUnitPrice;
|
||||
|
||||
/**
|
||||
* 属性数组
|
||||
*/
|
||||
private List<AppProductPropertyValueDetailRespVO> properties;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
// TODO 芋艿:字段优化
|
||||
@Schema(description = "交易订单分页 Request VO")
|
||||
@Data
|
||||
public class AppTradeOrderPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "订单状态-参见 TradeOrderStatusEnum 枚举", example = "1")
|
||||
@InEnum(value = TradeOrderStatusEnum.class, message = "订单状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* 提供 RESTful API 给前端:
|
||||
* 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目
|
||||
* 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
|
||||
*/
|
||||
package cn.iocoder.yudao.module.trade.controller;
|
@@ -1,89 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.aftersale;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRespPageItemVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
@Mapper
|
||||
public interface TradeAfterSaleConvert {
|
||||
|
||||
TradeAfterSaleConvert INSTANCE = Mappers.getMapper(TradeAfterSaleConvert.class);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "id", ignore = true),
|
||||
@Mapping(target = "createTime", ignore = true),
|
||||
@Mapping(target = "updateTime", ignore = true),
|
||||
@Mapping(target = "creator", ignore = true),
|
||||
@Mapping(target = "updater", ignore = true),
|
||||
})
|
||||
TradeAfterSaleDO convert(AppTradeAfterSaleCreateReqVO createReqVO, TradeOrderItemDO tradeOrderItem);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "afterSale.orderId", target = "merchantOrderId"),
|
||||
@Mapping(source = "afterSale.applyReason", target = "reason"),
|
||||
@Mapping(source = "afterSale.refundPrice", target = "amount")
|
||||
})
|
||||
PayRefundCreateReqDTO convert(String userIp, TradeAfterSaleDO afterSale,
|
||||
TradeOrderProperties orderProperties);
|
||||
|
||||
MemberUserRespVO convert(MemberUserRespDTO bean);
|
||||
|
||||
PageResult<TradeAfterSaleRespPageItemVO> convertPage(PageResult<TradeAfterSaleDO> page);
|
||||
|
||||
default PageResult<TradeAfterSaleRespPageItemVO> convertPage(PageResult<TradeAfterSaleDO> pageResult,
|
||||
Map<Long, MemberUserRespDTO> memberUsers, List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
|
||||
PageResult<TradeAfterSaleRespPageItemVO> pageVOResult = convertPage(pageResult);
|
||||
// 处理会员 + 商品属性等关联信息
|
||||
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
|
||||
for (int i = 0; i < pageResult.getList().size(); i++) {
|
||||
TradeAfterSaleRespPageItemVO afterSaleVO = pageVOResult.getList().get(i);
|
||||
TradeAfterSaleDO afterSaleDO = pageResult.getList().get(i);
|
||||
// 会员
|
||||
afterSaleVO.setUser(convert(memberUsers.get(afterSaleDO.getUserId())));
|
||||
// 商品属性
|
||||
if (CollUtil.isNotEmpty(afterSaleDO.getProperties())) {
|
||||
afterSaleVO.setProperties(new ArrayList<>(afterSaleDO.getProperties().size()));
|
||||
// 遍历每个 properties,设置到 TradeOrderPageItemRespVO.Item 中
|
||||
afterSaleDO.getProperties().forEach(property -> {
|
||||
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
|
||||
if (propertyValueDetail == null) {
|
||||
return;
|
||||
}
|
||||
afterSaleVO.getProperties().add(convert(propertyValueDetail));
|
||||
});
|
||||
}
|
||||
}
|
||||
return pageVOResult;
|
||||
}
|
||||
|
||||
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
|
||||
|
||||
default Set<Long> convertPropertyValueIds(List<TradeAfterSaleDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return list.stream().filter(item -> item.getProperties() != null)
|
||||
.flatMap(p -> p.getProperties().stream()) // 遍历多个 Property 属性
|
||||
.map(TradeOrderItemDO.Property::getValueId) // 将每个 Property 转换成对应的 propertyId,最后形成集合
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.cart;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Mapper
|
||||
public interface TradeCartConvert {
|
||||
|
||||
TradeCartConvert INSTANCE = Mappers.getMapper(TradeCartConvert.class);
|
||||
|
||||
default AppTradeCartDetailRespVO buildEmptyAppTradeCartDetailRespVO() {
|
||||
return new AppTradeCartDetailRespVO().setItemGroups(Collections.emptyList())
|
||||
.setOrder(new AppTradeCartDetailRespVO.Order().setSkuOriginalPrice(0).setSkuPromotionPrice(0)
|
||||
.setOrderPromotionPrice(0).setDeliveryPrice(0).setPayPrice(0));
|
||||
}
|
||||
|
||||
default PriceCalculateReqDTO convert(Long userId, List<TradeCartItemDO> cartItems) {
|
||||
return new PriceCalculateReqDTO().setUserId(userId)
|
||||
.setItems(convertList(cartItems, cartItem -> new PriceCalculateReqDTO.Item().setSkuId(cartItem.getSkuId())
|
||||
.setCount(cartItem.getSelected() ? cartItem.getCount() : 0)));
|
||||
}
|
||||
|
||||
// ========== AppTradeCartDetailRespVO 相关 ==========
|
||||
|
||||
AppTradeCartDetailRespVO.Promotion convert(PriceCalculateRespDTO.Promotion bean);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "cartItem.count", target = "count")
|
||||
})
|
||||
AppTradeCartDetailRespVO.Sku convert(PriceCalculateRespDTO.OrderItem orderItem, TradeCartItemDO cartItem);
|
||||
|
||||
AppTradeCartDetailRespVO.Order convert(PriceCalculateRespDTO.Order bean);
|
||||
|
||||
}
|
@@ -1,241 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageItemRespVO;
|
||||
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.enums.order.TradeOrderItemAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
|
||||
|
||||
@Mapper
|
||||
public interface TradeOrderConvert {
|
||||
|
||||
TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "id", ignore = true),
|
||||
@Mapping(source = "createReqVO.couponId", target = "couponId"),
|
||||
@Mapping(target = "remark", ignore = true),
|
||||
@Mapping(source = "createReqVO.remark", target = "userRemark"),
|
||||
@Mapping(source = "address.name", target = "receiverName"),
|
||||
@Mapping(source = "address.mobile", target = "receiverMobile"),
|
||||
@Mapping(source = "address.areaId", target = "receiverAreaId"),
|
||||
@Mapping(source = "address.postCode", target = "receiverPostCode"),
|
||||
@Mapping(source = "address.detailAddress", target = "receiverDetailAddress"),
|
||||
})
|
||||
TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO,
|
||||
PriceCalculateRespDTO.Order order, AddressRespDTO address);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "id", ignore = true),
|
||||
@Mapping(source = "sku.spuId", target = "spuId"),
|
||||
})
|
||||
TradeOrderItemDO convert(PriceCalculateRespDTO.OrderItem orderItem, ProductSkuRespDTO sku);
|
||||
|
||||
default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrderDO,
|
||||
List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) {
|
||||
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
|
||||
return CollectionUtils.convertList(orderItems, orderItem -> {
|
||||
TradeOrderItemDO tradeOrderItemDO = convert(orderItem, skuMap.get(orderItem.getSkuId()));
|
||||
tradeOrderItemDO.setOrderId(tradeOrderDO.getId());
|
||||
tradeOrderItemDO.setUserId(tradeOrderDO.getUserId());
|
||||
tradeOrderItemDO.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); // 退款信息
|
||||
// tradeOrderItemDO.setCommented(false);
|
||||
return tradeOrderItemDO;
|
||||
});
|
||||
}
|
||||
|
||||
@Mapping(source = "userId" , target = "userId")
|
||||
PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "skuId", target = "id"),
|
||||
@Mapping(source = "count", target = "incrCount"),
|
||||
})
|
||||
ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean);
|
||||
List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
|
||||
|
||||
default PayOrderCreateReqDTO convert(TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs,
|
||||
List<ProductSpuRespDTO> spus, TradeOrderProperties tradeOrderProperties) {
|
||||
PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO()
|
||||
.setAppId(tradeOrderProperties.getAppId()).setUserIp(tradeOrderDO.getUserIp());
|
||||
// 商户相关字段
|
||||
createReqDTO.setMerchantOrderId(String.valueOf(tradeOrderDO.getId()));
|
||||
String subject = spus.get(0).getName();
|
||||
if (spus.size() > 1) {
|
||||
subject += " 等多件";
|
||||
}
|
||||
createReqDTO.setSubject(subject);
|
||||
// 订单相关字段
|
||||
createReqDTO.setAmount(tradeOrderDO.getPayPrice()).setExpireTime(addTime(tradeOrderProperties.getExpireTime()));
|
||||
return createReqDTO;
|
||||
}
|
||||
|
||||
default Set<Long> convertPropertyValueIds(List<TradeOrderItemDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return list.stream().filter(item -> item.getProperties() != null)
|
||||
.flatMap(p -> p.getProperties().stream()) // 遍历多个 Property 属性
|
||||
.map(TradeOrderItemDO.Property::getValueId) // 将每个 Property 转换成对应的 propertyId,最后形成集合
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
default PageResult<TradeOrderPageItemRespVO> convertPage(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
|
||||
Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
|
||||
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
|
||||
// 转化 List
|
||||
List<TradeOrderPageItemRespVO> orderVOs = CollectionUtils.convertList(pageResult.getList(), order -> {
|
||||
List<TradeOrderItemDO> xOrderItems = orderItemMap.get(order.getId());
|
||||
TradeOrderPageItemRespVO orderVO = convert(order, xOrderItems);
|
||||
if (CollUtil.isNotEmpty(xOrderItems)) {
|
||||
// 处理商品属性
|
||||
for (int i = 0; i < xOrderItems.size(); i++) {
|
||||
List<TradeOrderItemDO.Property> properties = xOrderItems.get(i).getProperties();
|
||||
if (CollUtil.isEmpty(properties)) {
|
||||
continue;
|
||||
}
|
||||
TradeOrderPageItemRespVO.Item item = orderVO.getItems().get(i);
|
||||
item.setProperties(new ArrayList<>(properties.size()));
|
||||
// 遍历每个 properties,设置到 TradeOrderPageItemRespVO.Item 中
|
||||
properties.forEach(property -> {
|
||||
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
|
||||
if (propertyValueDetail == null) {
|
||||
return;
|
||||
}
|
||||
item.getProperties().add(convert(propertyValueDetail));
|
||||
});
|
||||
}
|
||||
}
|
||||
// 处理收货地址
|
||||
orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
|
||||
return orderVO;
|
||||
});
|
||||
return new PageResult<>(orderVOs, pageResult.getTotal());
|
||||
}
|
||||
TradeOrderPageItemRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> items);
|
||||
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
|
||||
|
||||
default TradeOrderDetailRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails, MemberUserRespDTO user) {
|
||||
TradeOrderDetailRespVO orderVO = convert2(order, orderItems);
|
||||
// 处理商品属性
|
||||
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
|
||||
for (int i = 0; i < orderItems.size(); i++) {
|
||||
List<TradeOrderItemDO.Property> properties = orderItems.get(i).getProperties();
|
||||
if (CollUtil.isEmpty(properties)) {
|
||||
continue;
|
||||
}
|
||||
TradeOrderDetailRespVO.Item item = orderVO.getItems().get(i);
|
||||
item.setProperties(new ArrayList<>(properties.size()));
|
||||
// 遍历每个 properties,设置到 TradeOrderPageItemRespVO.Item 中
|
||||
properties.forEach(property -> {
|
||||
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
|
||||
if (propertyValueDetail == null) {
|
||||
return;
|
||||
}
|
||||
item.getProperties().add(convert(propertyValueDetail));
|
||||
});
|
||||
}
|
||||
// 处理收货地址
|
||||
orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
|
||||
// 处理用户信息
|
||||
orderVO.setUser(convert(user));
|
||||
return orderVO;
|
||||
}
|
||||
TradeOrderDetailRespVO convert2(TradeOrderDO order, List<TradeOrderItemDO> items);
|
||||
MemberUserRespVO convert(MemberUserRespDTO bean);
|
||||
|
||||
default PageResult<AppTradeOrderPageItemRespVO> convertPage02(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
|
||||
Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
|
||||
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
|
||||
// 转化 List
|
||||
List<AppTradeOrderPageItemRespVO> orderVOs = CollectionUtils.convertList(pageResult.getList(), order -> {
|
||||
List<TradeOrderItemDO> xOrderItems = orderItemMap.get(order.getId());
|
||||
AppTradeOrderPageItemRespVO orderVO = convert02(order, xOrderItems);
|
||||
if (CollUtil.isNotEmpty(xOrderItems)) {
|
||||
// 处理商品属性
|
||||
for (int i = 0; i < xOrderItems.size(); i++) {
|
||||
List<TradeOrderItemDO.Property> properties = xOrderItems.get(i).getProperties();
|
||||
if (CollUtil.isEmpty(properties)) {
|
||||
continue;
|
||||
}
|
||||
AppTradeOrderPageItemRespVO.Item item = orderVO.getItems().get(i);
|
||||
item.setProperties(new ArrayList<>(properties.size()));
|
||||
// 遍历每个 properties,设置到 TradeOrderPageItemRespVO.Item 中
|
||||
properties.forEach(property -> {
|
||||
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
|
||||
if (propertyValueDetail == null) {
|
||||
return;
|
||||
}
|
||||
item.getProperties().add(convert02(propertyValueDetail));
|
||||
});
|
||||
}
|
||||
}
|
||||
return orderVO;
|
||||
});
|
||||
return new PageResult<>(orderVOs, pageResult.getTotal());
|
||||
}
|
||||
AppTradeOrderPageItemRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> items);
|
||||
AppProductPropertyValueDetailRespVO convert02(ProductPropertyValueDetailRespDTO bean);
|
||||
|
||||
default AppTradeOrderDetailRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
|
||||
AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems);
|
||||
// 处理商品属性
|
||||
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
|
||||
for (int i = 0; i < orderItems.size(); i++) {
|
||||
List<TradeOrderItemDO.Property> properties = orderItems.get(i).getProperties();
|
||||
if (CollUtil.isEmpty(properties)) {
|
||||
continue;
|
||||
}
|
||||
AppTradeOrderDetailRespVO.Item item = orderVO.getItems().get(i);
|
||||
item.setProperties(new ArrayList<>(properties.size()));
|
||||
// 遍历每个 properties,设置到 TradeOrderPageItemRespVO.Item 中
|
||||
properties.forEach(property -> {
|
||||
ProductPropertyValueDetailRespDTO propertyValueDetail = propertyValueDetailMap.get(property.getValueId());
|
||||
if (propertyValueDetail == null) {
|
||||
return;
|
||||
}
|
||||
item.getProperties().add(convert02(propertyValueDetail));
|
||||
});
|
||||
}
|
||||
// 处理收货地址
|
||||
orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
|
||||
return orderVO;
|
||||
}
|
||||
AppTradeOrderDetailRespVO convert3(TradeOrderDO order, List<TradeOrderItemDO> items);
|
||||
|
||||
}
|
@@ -1,201 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
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.enums.aftersale.TradeAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 交易售后,用于处理 {@link TradeOrderDO} 交易订单的退款退货流程
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "trade_after_sale", autoResultMap = true)
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class TradeAfterSaleDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 售后编号,主键自增
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 售后流水号
|
||||
*
|
||||
* 例如说,1146347329394184195
|
||||
*/
|
||||
private String no;
|
||||
/**
|
||||
* 退款状态
|
||||
*
|
||||
* 枚举 {@link TradeAfterSaleStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 售后方式
|
||||
*
|
||||
* 枚举 {@link TradeAfterSaleWayEnum}
|
||||
*/
|
||||
private Integer way;
|
||||
/**
|
||||
* 售后类型
|
||||
*
|
||||
* 枚举 {@link TradeAfterSaleTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
* 关联 MemberUserDO 的 id 编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 申请原因
|
||||
*
|
||||
* type = 退款,对应 trade_after_sale_refund_reason 类型
|
||||
* type = 退货退款,对应 trade_after_sale_refund_and_return_reason 类型
|
||||
*/
|
||||
private String applyReason;
|
||||
/**
|
||||
* 补充描述
|
||||
*/
|
||||
private String applyDescription;
|
||||
/**
|
||||
* 补充凭证图片
|
||||
*
|
||||
* 数组,以逗号分隔
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<String> applyPicUrls;
|
||||
|
||||
// ========== 交易订单相关 ==========
|
||||
/**
|
||||
* 交易订单编号
|
||||
*
|
||||
* 关联 {@link TradeOrderDO#getId()}
|
||||
*/
|
||||
private Long orderId;
|
||||
/**
|
||||
* 订单流水号
|
||||
*
|
||||
* 冗余 {@link TradeOrderDO#getNo()}
|
||||
*/
|
||||
private String orderNo;
|
||||
/**
|
||||
* 交易订单项编号
|
||||
*
|
||||
* 关联 {@link TradeOrderItemDO#getId()}
|
||||
*/
|
||||
private Long orderItemId;
|
||||
/**
|
||||
* 商品 SPU 编号
|
||||
*
|
||||
* 关联 ProductSpuDO 的 id 字段
|
||||
* 冗余 {@link TradeOrderItemDO#getSpuId()}
|
||||
*/
|
||||
private Long spuId;
|
||||
/**
|
||||
* 商品 SPU 名称
|
||||
*
|
||||
* 关联 ProductSkuDO 的 name 字段
|
||||
* 冗余 {@link TradeOrderItemDO#getSpuName()}
|
||||
*/
|
||||
private String spuName;
|
||||
/**
|
||||
* 商品 SKU 编号
|
||||
*
|
||||
* 关联 ProductSkuDO 的编号
|
||||
*/
|
||||
private Long skuId;
|
||||
/**
|
||||
* 属性数组,JSON 格式
|
||||
*
|
||||
* 冗余 {@link TradeOrderItemDO#getProperties()}
|
||||
*/
|
||||
@TableField(typeHandler = TradeOrderItemDO.PropertyTypeHandler.class)
|
||||
private List<TradeOrderItemDO.Property> properties;
|
||||
/**
|
||||
* 商品图片
|
||||
*
|
||||
* 冗余 {@link TradeOrderItemDO#getPicUrl()}
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* 退货商品数量
|
||||
*/
|
||||
private Integer count;
|
||||
|
||||
// ========== 审批相关 ==========
|
||||
|
||||
/**
|
||||
* 审批时间
|
||||
*/
|
||||
private LocalDateTime auditTime;
|
||||
/**
|
||||
* 审批人
|
||||
*
|
||||
* 关联 AdminUserDO 的 id 编号
|
||||
*/
|
||||
private Long auditUserId;
|
||||
/**
|
||||
* 审批备注
|
||||
*
|
||||
* 注意,只有审批不通过才会填写
|
||||
*/
|
||||
private String auditReason;
|
||||
|
||||
// ========== 退款相关 ==========
|
||||
/**
|
||||
* 退款金额,单位:分。
|
||||
*/
|
||||
private Integer refundPrice;
|
||||
/**
|
||||
* 支付退款编号
|
||||
*
|
||||
* 对接 pay-module-biz 支付服务的退款订单编号,即 PayRefundDO 的 id 编号
|
||||
*/
|
||||
private Long payRefundId;
|
||||
/**
|
||||
* 退款时间
|
||||
*/
|
||||
private LocalDateTime refundTime;
|
||||
|
||||
// ========== 退货相关 ==========
|
||||
/**
|
||||
* 退货物流公司编号
|
||||
*
|
||||
* 关联 LogisticsDO 的 id 编号
|
||||
*/
|
||||
private Long logisticsId;
|
||||
/**
|
||||
* 退货物流单号
|
||||
*/
|
||||
private String logisticsNo;
|
||||
/**
|
||||
* 退货时间
|
||||
*/
|
||||
private LocalDateTime deliveryTime;
|
||||
/**
|
||||
* 收货时间
|
||||
*/
|
||||
private LocalDateTime receiveTime;
|
||||
/**
|
||||
* 收货备注
|
||||
*
|
||||
* 注意,只有拒绝收货才会填写
|
||||
*/
|
||||
private String receiveReason;
|
||||
|
||||
}
|
@@ -1,83 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 交易售后日志 DO
|
||||
*
|
||||
* // TODO 可优化:参考淘宝或者有赞:1)增加 action 表示什么操作;2)content 记录每个操作的明细
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("trade_after_sale_log")
|
||||
@KeySequence("trade_after_sale_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TradeAfterSaleLogDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
* 关联 1:AdminUserDO 的 id 字段
|
||||
* 关联 2:MemberUserDO 的 id 字段
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* 枚举 {@link UserTypeEnum}
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 售后编号
|
||||
*
|
||||
* 关联 {@link TradeAfterSaleDO#getId()}
|
||||
*/
|
||||
private Long afterSaleId;
|
||||
/**
|
||||
* 订单编号
|
||||
*
|
||||
* 关联 {@link TradeOrderDO#getId()}
|
||||
*/
|
||||
private Long orderId;
|
||||
/**
|
||||
* 订单项编号
|
||||
*
|
||||
* 关联 {@link TradeOrderItemDO#getId()}
|
||||
*/
|
||||
private Long orderItemId;
|
||||
/**
|
||||
* 售后状态(之前)
|
||||
*
|
||||
* 枚举 {@link TradeAfterSaleStatusEnum}
|
||||
*/
|
||||
private Integer beforeStatus;
|
||||
/**
|
||||
* 售后状态(之后)
|
||||
*
|
||||
* 枚举 {@link TradeAfterSaleStatusEnum}
|
||||
*/
|
||||
private Integer afterStatus;
|
||||
/**
|
||||
* 操作明细
|
||||
*/
|
||||
private String content;
|
||||
|
||||
}
|
@@ -1,90 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.cart;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 购物车的商品信息 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("trade_cart_item")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class TradeCartItemDO extends BaseDO {
|
||||
|
||||
// ========= 基础字段 BEGIN =========
|
||||
|
||||
/**
|
||||
* 编号,唯一自增
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 是否选中
|
||||
*/
|
||||
private Boolean selected;
|
||||
|
||||
// ========= 基础字段 END =========
|
||||
|
||||
// ========= 买家信息 BEGIN =========
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
* 关联 MemberUserDO 的 id 编号
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
// ========= 买家信息 END =========
|
||||
|
||||
// ========= 商品信息 BEGIN =========
|
||||
|
||||
/**
|
||||
* 商品 SPU 编号
|
||||
*
|
||||
* 关联 ProductSpuDO 的 id 编号
|
||||
*/
|
||||
private Long spuId;
|
||||
/**
|
||||
* 商品 SKU 编号
|
||||
*
|
||||
* 关联 ProductSkuDO 的 id 编号
|
||||
*/
|
||||
private Long skuId;
|
||||
/**
|
||||
* 商品购买数量
|
||||
*/
|
||||
private Integer count;
|
||||
|
||||
// ========= 商品信息 END =========
|
||||
|
||||
// ========= 优惠信息 BEGIN =========
|
||||
|
||||
// /**
|
||||
// * 商品营销活动编号
|
||||
// */
|
||||
// private Long activityId; // discount_id
|
||||
// /**
|
||||
// * 商品营销活动类型
|
||||
// */
|
||||
// private Integer activityType;
|
||||
// TODO 芋艿:combination_id 拼团 ID
|
||||
// TODO 芋艿:seckill_id 秒杀产品 ID
|
||||
// TODO 芋艿:bargain_id 砍价 ID
|
||||
|
||||
// ========= 优惠信息 END =========
|
||||
|
||||
// TODO 待确定字段:mf
|
||||
// TODO 芋艿:distribution_card_no 推广员
|
||||
// TODO 芋艿:is_pay 未购买、已购买
|
||||
// TODO 芋艿:is_new 是否立即购买
|
||||
|
||||
// TODO 待确定字段: yv
|
||||
// TODO isPay: 是否购买
|
||||
// TODO isNew:是否立即购买
|
||||
|
||||
}
|
@@ -1,257 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO.OrderItem;
|
||||
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.TradeOrderDeliveryStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 交易订单 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("trade_order")
|
||||
@KeySequence("trade_order_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TradeOrderDO extends BaseDO {
|
||||
|
||||
// ========== 订单基本信息 ==========
|
||||
/**
|
||||
* 订单编号,主键自增
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 订单流水号
|
||||
*
|
||||
* 例如说,1146347329394184195
|
||||
*/
|
||||
private String no;
|
||||
/**
|
||||
* 订单类型
|
||||
*
|
||||
* 枚举 {@link TradeOrderTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 订单来源
|
||||
*
|
||||
* 枚举 {@link TerminalEnum}
|
||||
*/
|
||||
private Integer terminal;
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
* 关联 MemberUserDO 的 id 编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户 IP
|
||||
*/
|
||||
private String userIp;
|
||||
/**
|
||||
* 用户备注
|
||||
*/
|
||||
private String userRemark;
|
||||
/**
|
||||
* 订单状态
|
||||
*
|
||||
* 枚举 {@link TradeOrderStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 购买的商品数量
|
||||
*/
|
||||
private Integer productCount;
|
||||
/**
|
||||
* 订单完成时间
|
||||
*/
|
||||
private LocalDateTime finishTime;
|
||||
/**
|
||||
* 订单取消时间
|
||||
*/
|
||||
private LocalDateTime cancelTime;
|
||||
/**
|
||||
* 取消类型
|
||||
*
|
||||
* 枚举 {@link TradeOrderCancelTypeEnum}
|
||||
*/
|
||||
private Integer cancelType;
|
||||
/**
|
||||
* 商家备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
// ========== 价格 + 支付基本信息 ==========
|
||||
|
||||
// 价格文档 - 淘宝:https://open.taobao.com/docV3.htm?docId=108471&docType=1
|
||||
// 价格文档 - 京东到家:https://openo2o.jddj.com/api/getApiDetail/182/4d1494c5e7ac4679bfdaaed950c5bc7f.htm
|
||||
// 价格文档 - 有赞:https://doc.youzanyun.com/detail/API/0/906
|
||||
|
||||
/**
|
||||
* 支付订单编号
|
||||
*
|
||||
* 对接 pay-module-biz 支付服务的支付订单编号,即 PayOrderDO 的 id 编号
|
||||
*/
|
||||
private Long payOrderId;
|
||||
/**
|
||||
* 是否已支付
|
||||
*
|
||||
* true - 已经支付过
|
||||
* false - 没有支付过
|
||||
*/
|
||||
private Boolean payed;
|
||||
/**
|
||||
* 付款时间
|
||||
*/
|
||||
private LocalDateTime payTime;
|
||||
/**
|
||||
* 支付渠道
|
||||
*
|
||||
* 对应 PayChannelEnum 枚举
|
||||
*/
|
||||
private String payChannelCode;
|
||||
|
||||
/**
|
||||
* 商品原价(总),单位:分
|
||||
*
|
||||
* 基于 {@link TradeOrderItemDO#getOriginalPrice()} 求和
|
||||
*
|
||||
* 对应 taobao 的 trade.total_fee 字段
|
||||
*/
|
||||
private Integer originalPrice;
|
||||
/**
|
||||
* 订单原价(总),单位:分
|
||||
*
|
||||
* 基于 {@link OrderItem#getPayPrice()} 求和
|
||||
* 和 {@link #originalPrice} 的差异:去除商品级优惠
|
||||
*/
|
||||
private Integer orderPrice;
|
||||
/**
|
||||
* 订单优惠(总),单位:分
|
||||
*
|
||||
* 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。
|
||||
*
|
||||
* 对应 taobao 的 order.discount_fee 字段
|
||||
*/
|
||||
private Integer discountPrice;
|
||||
/**
|
||||
* 运费金额,单位:分
|
||||
*/
|
||||
private Integer deliveryPrice;
|
||||
/**
|
||||
* 订单调价(总),单位:分
|
||||
*
|
||||
* 正数,加价;负数,减价
|
||||
*/
|
||||
private Integer adjustPrice;
|
||||
/**
|
||||
* 应付金额(总),单位:分
|
||||
*
|
||||
* = {@link OrderItem#getPayPrice()} 求和
|
||||
* - {@link #couponPrice}
|
||||
* - {@link #pointPrice}
|
||||
* + {@link #deliveryPrice}
|
||||
* - {@link #discountPrice}
|
||||
* + {@link #adjustPrice}
|
||||
*/
|
||||
private Integer payPrice;
|
||||
|
||||
// ========== 收件 + 物流基本信息 ==========
|
||||
/**
|
||||
* 配置模板的编号
|
||||
*
|
||||
* 关联 DeliveryTemplateDO 的 id 编号
|
||||
*/
|
||||
private Long deliveryTemplateId;
|
||||
/**
|
||||
* 发货物流公司编号
|
||||
*/
|
||||
private Long logisticsId;
|
||||
/**
|
||||
* 发货物流单号
|
||||
*/
|
||||
private String logisticsNo;
|
||||
/**
|
||||
* 发货状态
|
||||
*
|
||||
* 枚举 {@link TradeOrderDeliveryStatusEnum}
|
||||
*/
|
||||
private Integer deliveryStatus;
|
||||
/**
|
||||
* 发货时间
|
||||
*/
|
||||
private LocalDateTime deliveryTime;
|
||||
|
||||
/**
|
||||
* 收货时间
|
||||
*/
|
||||
private LocalDateTime receiveTime;
|
||||
/**
|
||||
* 收件人名称
|
||||
*/
|
||||
private String receiverName;
|
||||
/**
|
||||
* 收件人手机
|
||||
*/
|
||||
private String receiverMobile;
|
||||
/**
|
||||
* 收件人地区编号
|
||||
*/
|
||||
private Integer receiverAreaId;
|
||||
/**
|
||||
* 收件人邮编
|
||||
*/
|
||||
private Integer receiverPostCode;
|
||||
/**
|
||||
* 收件人详细地址
|
||||
*/
|
||||
private String receiverDetailAddress;
|
||||
|
||||
// ========== 售后基本信息 ==========
|
||||
/**
|
||||
* 收货状态
|
||||
*
|
||||
* 枚举 {@link TradeOrderAfterSaleStatusEnum}
|
||||
*/
|
||||
private Integer afterSaleStatus;
|
||||
/**
|
||||
* 退款金额,单位:分
|
||||
*
|
||||
* 注意,退款并不会影响 {@link #payPrice} 实际支付金额
|
||||
* 也就说,一个订单最终产生多少金额的收入 = payPrice - refundPrice
|
||||
*/
|
||||
private Integer refundPrice;
|
||||
|
||||
// ========== 营销基本信息 ==========
|
||||
/**
|
||||
* 优惠劵编号
|
||||
*/
|
||||
private Long couponId;
|
||||
/**
|
||||
* 优惠劵减免金额,单位:分
|
||||
*
|
||||
* 对应 taobao 的 trade.coupon_fee 字段
|
||||
*/
|
||||
private Integer couponPrice;
|
||||
/**
|
||||
* 积分抵扣的金额,单位:分
|
||||
*
|
||||
* 对应 taobao 的 trade.point_fee 字段
|
||||
*/
|
||||
private Integer pointPrice;
|
||||
|
||||
}
|
@@ -1,190 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 交易订单项 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "trade_order_item", autoResultMap = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TradeOrderItemDO extends BaseDO {
|
||||
|
||||
// ========== 订单项基本信息 ==========
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
* 关联 MemberUserDO 的 id 编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 订单编号
|
||||
*
|
||||
* 关联 {@link TradeOrderDO#getId()}
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
// ========== 商品基本信息; 冗余较多字段,减少关联查询 ==========
|
||||
/**
|
||||
* 商品 SPU 编号
|
||||
*
|
||||
* 关联 ProductSkuDO 的 spuId 编号
|
||||
*/
|
||||
private Long spuId;
|
||||
/**
|
||||
* 商品 SPU 名称
|
||||
*
|
||||
* 冗余 ProductSkuDO 的 spuName 编号
|
||||
*/
|
||||
private String spuName;
|
||||
/**
|
||||
* 商品 SKU 编号
|
||||
*
|
||||
* 关联 ProductSkuDO 的 id 编号
|
||||
*/
|
||||
private Long skuId;
|
||||
/**
|
||||
* 属性数组,JSON 格式
|
||||
*
|
||||
* 冗余 ProductSkuDO 的 properties 字段
|
||||
*/
|
||||
@TableField(typeHandler = PropertyTypeHandler.class)
|
||||
private List<Property> properties;
|
||||
/**
|
||||
* 商品图片
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* 购买数量
|
||||
*/
|
||||
private Integer count;
|
||||
// /**
|
||||
// * 是否评论 TODO
|
||||
// *
|
||||
// * false - 未评论
|
||||
// * true - 已评论
|
||||
// */
|
||||
// private Boolean commented;
|
||||
|
||||
// ========== 价格 + 支付基本信息 ==========
|
||||
|
||||
/**
|
||||
* 商品原价(总),单位:分
|
||||
*
|
||||
* = {@link #originalUnitPrice} * {@link #getCount()}
|
||||
*/
|
||||
private Integer originalPrice;
|
||||
/**
|
||||
* 商品原价(单),单位:分
|
||||
*
|
||||
* 对应 ProductSkuDO 的 price 字段
|
||||
* 对应 taobao 的 order.price 字段
|
||||
*/
|
||||
private Integer originalUnitPrice;
|
||||
/**
|
||||
* 商品优惠(总),单位:分
|
||||
*
|
||||
* 商品级优惠:对单个商品的,常见如:商品原价的 8 折;商品原价的减 50 元
|
||||
*
|
||||
* 对应 taobao 的 order.discount_fee 字段
|
||||
*/
|
||||
private Integer discountPrice;
|
||||
/**
|
||||
* 子订单实付金额,不算主订单分摊金额,单位:分
|
||||
*
|
||||
* = {@link #originalPrice}
|
||||
* - {@link #discountPrice}
|
||||
*
|
||||
* 对应 taobao 的 order.payment 字段
|
||||
*/
|
||||
private Integer payPrice;
|
||||
|
||||
/**
|
||||
* 子订单分摊金额(总),单位:分
|
||||
* 需要分摊 {@link TradeOrderDO#getDiscountPrice()}、{@link TradeOrderDO#getCouponPrice()}、{@link TradeOrderDO#getPointPrice()}
|
||||
*
|
||||
* 对应 taobao 的 order.part_mjz_discount 字段
|
||||
* 淘宝说明:子订单分摊优惠基础逻辑:一般正常优惠券和满减优惠按照子订单的金额进行分摊,特殊情况如果优惠券是指定商品使用的,只会分摊到对应商品子订单上不分摊。
|
||||
*/
|
||||
private Integer orderPartPrice;
|
||||
/**
|
||||
* 分摊后子订单实付金额(总),单位:分
|
||||
*
|
||||
* = {@link #payPrice}
|
||||
* - {@link #orderPartPrice}
|
||||
*
|
||||
* 对应 taobao 的 divide_order_fee 字段
|
||||
*/
|
||||
private Integer orderDividePrice;
|
||||
|
||||
// ========== 营销基本信息 ==========
|
||||
|
||||
// TODO 芋艿:在捉摸一下
|
||||
|
||||
// ========== 售后基本信息 ==========
|
||||
/**
|
||||
* 售后状态
|
||||
*
|
||||
* 枚举 {@link TradeOrderItemAfterSaleStatusEnum}
|
||||
*
|
||||
* @see TradeAfterSaleDO
|
||||
*/
|
||||
private Integer afterSaleStatus;
|
||||
|
||||
/**
|
||||
* 商品属性
|
||||
*/
|
||||
@Data
|
||||
public static class Property implements Serializable {
|
||||
|
||||
/**
|
||||
* 属性编号
|
||||
*
|
||||
* 关联 ProductPropertyDO 的 id 编号
|
||||
*/
|
||||
private Long propertyId;
|
||||
/**
|
||||
* 属性值编号
|
||||
*
|
||||
* 关联 ProductPropertyValueDO 的 id 编号
|
||||
*/
|
||||
private Long valueId;
|
||||
|
||||
}
|
||||
|
||||
// TODO @芋艿:可以找一些新的思路
|
||||
public static class PropertyTypeHandler extends AbstractJsonTypeHandler<List<Property>> {
|
||||
|
||||
@Override
|
||||
protected List<Property> parse(String json) {
|
||||
return JsonUtils.parseArray(json, Property.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toJson(List<Property> obj) {
|
||||
return JsonUtils.toJsonString(obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,9 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleLogDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface TradeAfterSaleLogMapper extends BaseMapperX<TradeAfterSaleLogDO> {
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface TradeAfterSaleMapper extends BaseMapperX<TradeAfterSaleDO> {
|
||||
|
||||
default PageResult<TradeAfterSaleDO> selectPage(TradeAfterSalePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<TradeAfterSaleDO>()
|
||||
.likeIfPresent(TradeAfterSaleDO::getNo, reqVO.getNo())
|
||||
.eqIfPresent(TradeAfterSaleDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(TradeAfterSaleDO::getType, reqVO.getType())
|
||||
.eqIfPresent(TradeAfterSaleDO::getWay, reqVO.getWay())
|
||||
.likeIfPresent(TradeAfterSaleDO::getOrderNo, reqVO.getOrderNo())
|
||||
.likeIfPresent(TradeAfterSaleDO::getSpuName, reqVO.getSpuName())
|
||||
.betweenIfPresent(TradeAfterSaleDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(TradeAfterSaleDO::getId));
|
||||
}
|
||||
|
||||
default int updateByIdAndStatus(Long id, Integer status, TradeAfterSaleDO update) {
|
||||
return update(update, new LambdaUpdateWrapper<TradeAfterSaleDO>()
|
||||
.eq(TradeAfterSaleDO::getId, id).eq(TradeAfterSaleDO::getStatus, status));
|
||||
}
|
||||
|
||||
default TradeAfterSaleDO selectByPayRefundId(Long payRefundId) {
|
||||
return selectOne(TradeAfterSaleDO::getPayRefundId, payRefundId);
|
||||
}
|
||||
|
||||
}
|
@@ -1,47 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.cart;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
public interface TradeCartItemMapper extends BaseMapperX<TradeCartItemDO> {
|
||||
|
||||
default TradeCartItemDO selectByUserIdAndSkuId(Long userId, Long skuId) {
|
||||
return selectOne(TradeCartItemDO::getUserId, userId,
|
||||
TradeCartItemDO::getSkuId, skuId);
|
||||
}
|
||||
|
||||
default List<TradeCartItemDO> selectListByUserIdAndSkuIds(Long userId, Collection<Long> skuIds) {
|
||||
return selectList(new LambdaQueryWrapper<TradeCartItemDO>().eq(TradeCartItemDO::getUserId, userId)
|
||||
.in(TradeCartItemDO::getSkuId, skuIds));
|
||||
}
|
||||
|
||||
default void updateByIds(Collection<Long> ids, TradeCartItemDO updateObject) {
|
||||
update(updateObject, new LambdaQueryWrapper<TradeCartItemDO>().in(TradeCartItemDO::getId, ids));
|
||||
}
|
||||
|
||||
default Integer selectSumByUserId(Long userId) {
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<TradeCartItemDO>()
|
||||
.select("SUM(count) AS sumCount")
|
||||
.eq("user_id", userId));
|
||||
// 获得数量
|
||||
return CollUtil.isNotEmpty(result) ? MapUtil.getInt(result.get(0), "sumCount") : 0;
|
||||
}
|
||||
|
||||
default List<TradeCartItemDO> selectListByUserId(Long userId, Boolean selected) {
|
||||
return selectList(new LambdaQueryWrapperX<TradeCartItemDO>().eq(TradeCartItemDO::getUserId, userId)
|
||||
.eqIfPresent(TradeCartItemDO::getSelected, selected));
|
||||
}
|
||||
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
|
||||
|
||||
default int updateAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus) {
|
||||
return update(new TradeOrderItemDO().setAfterSaleStatus(newAfterSaleStatus),
|
||||
new LambdaUpdateWrapper<>(new TradeOrderItemDO().setId(id).setAfterSaleStatus(oldAfterSaleStatus)));
|
||||
}
|
||||
|
||||
default List<TradeOrderItemDO> selectListByOrderId(Long orderId) {
|
||||
return selectList(TradeOrderItemDO::getOrderId, orderId);
|
||||
}
|
||||
|
||||
default List<TradeOrderItemDO> selectListByOrderId(Collection<Long> orderIds) {
|
||||
return selectList(TradeOrderItemDO::getOrderId, orderIds);
|
||||
}
|
||||
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Mapper
|
||||
public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
|
||||
|
||||
default int updateByIdAndStatus(Long id, Integer status, TradeOrderDO update) {
|
||||
return update(update, new LambdaUpdateWrapper<TradeOrderDO>()
|
||||
.eq(TradeOrderDO::getId, id).eq(TradeOrderDO::getStatus, status));
|
||||
}
|
||||
|
||||
default TradeOrderDO selectByIdAndUserId(Long id, Long userId) {
|
||||
return selectOne(TradeOrderDO::getId, id, TradeOrderDO::getUserId, userId);
|
||||
}
|
||||
|
||||
default PageResult<TradeOrderDO> selectPage(TradeOrderPageReqVO reqVO, Set<Long> userIds) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<TradeOrderDO>()
|
||||
.likeIfPresent(TradeOrderDO::getNo, reqVO.getNo())
|
||||
.eqIfPresent(TradeOrderDO::getUserId, reqVO.getUserId())
|
||||
.inIfPresent(TradeOrderDO::getUserId, userIds)
|
||||
.likeIfPresent(TradeOrderDO::getReceiverName, reqVO.getReceiverName())
|
||||
.likeIfPresent(TradeOrderDO::getReceiverMobile, reqVO.getReceiverMobile())
|
||||
.eqIfPresent(TradeOrderDO::getType, reqVO.getType())
|
||||
.eqIfPresent(TradeOrderDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(TradeOrderDO::getPayChannelCode, reqVO.getPayChannelCode())
|
||||
.betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime()));
|
||||
}
|
||||
|
||||
default PageResult<TradeOrderDO> selectPage(AppTradeOrderPageReqVO reqVO, Long userId) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<TradeOrderDO>()
|
||||
.eq(TradeOrderDO::getUserId, userId)
|
||||
.eqIfPresent(TradeOrderDO::getStatus, reqVO.getStatus())
|
||||
.orderByDesc(TradeOrderDO::getId)); // TODO 芋艿:未来不同的 status,不同的排序
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* TODO 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql;
|
@@ -1,14 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.framework.order.config;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
// TODO @LeeYan9: 可以直接给 TradeOrderProperties 一个 @Component生效哈
|
||||
/**
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-15
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TradeOrderProperties.class)
|
||||
public class TradeOrderConfig {
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.framework.order.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 交易订单的配置项
|
||||
*
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-15
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "yudao.trade.order")
|
||||
@Data
|
||||
@Validated
|
||||
public class TradeOrderProperties {
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
*/
|
||||
@NotNull(message = "应用编号不能为空")
|
||||
private Long appId;
|
||||
|
||||
/**
|
||||
* 支付超时时间
|
||||
*/
|
||||
@NotNull(message = "支付超时时间不能为空")
|
||||
private Duration expireTime;
|
||||
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* product 模块,product 模块,主要实现商品相关功能
|
||||
* 例如:品牌、商品分类、spu、sku等功能。
|
||||
*
|
||||
* 1. Controller URL:以 /product/ 开头,避免和其它 Module 冲突
|
||||
* 2. DataObject 表名:以 product_ 开头,方便在数据库中区分
|
||||
*/
|
||||
package cn.iocoder.yudao.module.trade;
|
@@ -1,94 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
|
||||
|
||||
/**
|
||||
* 交易售后 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface TradeAfterSaleService {
|
||||
|
||||
/**
|
||||
* 获得交易售后分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 交易售后分页
|
||||
*/
|
||||
PageResult<TradeAfterSaleDO> getAfterSalePage(TradeAfterSalePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 【会员】创建交易售后
|
||||
* <p>
|
||||
* 一般是用户发起售后请求
|
||||
*
|
||||
* @param userId 会员用户编号
|
||||
* @param createReqVO 创建 Request 信息
|
||||
* @return 交易售后编号
|
||||
*/
|
||||
Long createAfterSale(Long userId, AppTradeAfterSaleCreateReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 【管理员】同意交易售后
|
||||
*
|
||||
* @param userId 管理员用户编号
|
||||
* @param id 交易售后编号
|
||||
*/
|
||||
void agreeAfterSale(Long userId, Long id);
|
||||
|
||||
/**
|
||||
* 【管理员】拒绝交易售后
|
||||
*
|
||||
* @param userId 管理员用户编号
|
||||
* @param auditReqVO 审批 Request 信息
|
||||
*/
|
||||
void disagreeAfterSale(Long userId, TradeAfterSaleDisagreeReqVO auditReqVO);
|
||||
|
||||
/**
|
||||
* 【会员】退回货物
|
||||
*
|
||||
* @param userId 会员用户编号
|
||||
* @param deliveryReqVO 退货 Request 信息
|
||||
*/
|
||||
void deliveryAfterSale(Long userId, AppTradeAfterSaleDeliveryReqVO deliveryReqVO);
|
||||
|
||||
/**
|
||||
* 【管理员】确认收货
|
||||
*
|
||||
* @param userId 管理员编号
|
||||
* @param id 交易售后编号
|
||||
*/
|
||||
void receiveAfterSale(Long userId, Long id);
|
||||
|
||||
/**
|
||||
* 【管理员】拒绝收货
|
||||
*
|
||||
* @param userId 管理员用户编号
|
||||
* @param refuseReqVO 拒绝收货 Request 信息
|
||||
*/
|
||||
void refuseAfterSale(Long userId, TradeAfterSaleRefuseReqVO refuseReqVO);
|
||||
|
||||
/**
|
||||
* 【管理员】确认退款
|
||||
*
|
||||
* @param userId 管理员用户编号
|
||||
* @param userIp 管理员用户 IP
|
||||
* @param id 售后编号
|
||||
*/
|
||||
void refundAfterSale(Long userId, String userIp, Long id);
|
||||
|
||||
/**
|
||||
* 【会员】取消售后
|
||||
*
|
||||
* @param userId 会员用户编号
|
||||
* @param id 交易售后编号
|
||||
*/
|
||||
void cancelAfterSale(Long userId, Long id);
|
||||
|
||||
}
|
@@ -1,392 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.aftersale;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
|
||||
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleLogDO;
|
||||
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.mysql.aftersale.TradeAfterSaleLogMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 交易售后 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class TradeAfterSaleServiceImpl implements TradeAfterSaleService {
|
||||
|
||||
@Resource
|
||||
private TradeOrderService tradeOrderService;
|
||||
|
||||
@Resource
|
||||
private TradeAfterSaleMapper tradeAfterSaleMapper;
|
||||
@Resource
|
||||
private TradeAfterSaleLogMapper tradeAfterSaleLogMapper;
|
||||
|
||||
@Resource
|
||||
private PayRefundApi payRefundApi;
|
||||
|
||||
@Resource
|
||||
private TradeOrderProperties tradeOrderProperties;
|
||||
|
||||
@Override
|
||||
public PageResult<TradeAfterSaleDO> getAfterSalePage(TradeAfterSalePageReqVO pageReqVO) {
|
||||
return tradeAfterSaleMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createAfterSale(Long userId, AppTradeAfterSaleCreateReqVO createReqVO) {
|
||||
// 第一步,前置校验
|
||||
TradeOrderItemDO tradeOrderItem = validateOrderItemApplicable(userId, createReqVO);
|
||||
|
||||
// 第二步,存储交易售后
|
||||
TradeAfterSaleDO afterSale = createAfterSale(createReqVO, tradeOrderItem);
|
||||
return afterSale.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验交易订单项是否可以申请售后
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param createReqVO 售后创建信息
|
||||
* @return 交易订单项
|
||||
*/
|
||||
private TradeOrderItemDO validateOrderItemApplicable(Long userId, AppTradeAfterSaleCreateReqVO createReqVO) {
|
||||
// 校验订单项存在
|
||||
TradeOrderItemDO orderItem = tradeOrderService.getOrderItem(userId, createReqVO.getOrderItemId());
|
||||
if (orderItem == null) {
|
||||
throw exception(ORDER_ITEM_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 已申请售后,不允许再发起售后申请
|
||||
if (!TradeOrderItemAfterSaleStatusEnum.isNone(orderItem.getAfterSaleStatus())) {
|
||||
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_ITEM_APPLIED);
|
||||
}
|
||||
|
||||
// 申请的退款金额,不能超过商品的价格
|
||||
if (createReqVO.getRefundPrice() > orderItem.getOrderDividePrice()) {
|
||||
throw exception(AFTER_SALE_CREATE_FAIL_REFUND_PRICE_ERROR);
|
||||
}
|
||||
|
||||
// 校验订单存在
|
||||
TradeOrderDO order = tradeOrderService.getOrder(userId, orderItem.getOrderId());
|
||||
if (order == null) {
|
||||
throw exception(ORDER_NOT_FOUND);
|
||||
}
|
||||
// TODO 芋艿:超过一定时间,不允许售后
|
||||
// 已取消,无法发起售后
|
||||
if (TradeOrderStatusEnum.isCanceled(order.getStatus())) {
|
||||
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_CANCELED);
|
||||
}
|
||||
// 未支付,无法发起售后
|
||||
if (!TradeOrderStatusEnum.havePaid(order.getStatus())) {
|
||||
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_PAID);
|
||||
}
|
||||
// 如果是【退货退款】的情况,需要额外校验是否发货
|
||||
if (createReqVO.getWay().equals(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay())
|
||||
&& !TradeOrderStatusEnum.haveDelivered(order.getStatus())) {
|
||||
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED);
|
||||
}
|
||||
return orderItem;
|
||||
}
|
||||
|
||||
private TradeAfterSaleDO createAfterSale(AppTradeAfterSaleCreateReqVO createReqVO,
|
||||
TradeOrderItemDO orderItem) {
|
||||
// 创建售后单
|
||||
TradeAfterSaleDO afterSale = TradeAfterSaleConvert.INSTANCE.convert(createReqVO, orderItem);
|
||||
afterSale.setNo(RandomUtil.randomString(10)); // TODO 芋艿:优化 no 生成逻辑
|
||||
afterSale.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
|
||||
// 标记是售中还是售后
|
||||
TradeOrderDO order = tradeOrderService.getOrder(orderItem.getUserId(), orderItem.getOrderId());
|
||||
afterSale.setOrderNo(order.getNo()); // 记录 orderNo 订单流水,方便后续检索
|
||||
afterSale.setType(TradeOrderStatusEnum.isCompleted(order.getStatus())
|
||||
? TradeAfterSaleTypeEnum.AFTER_SALE.getType() : TradeAfterSaleTypeEnum.IN_SALE.getType());
|
||||
// TODO 退还积分
|
||||
tradeAfterSaleMapper.insert(afterSale);
|
||||
|
||||
// 更新交易订单项的售后状态
|
||||
tradeOrderService.updateOrderItemAfterSaleStatus(orderItem.getId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), null);
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(orderItem.getUserId(), UserTypeEnum.MEMBER.getValue(),
|
||||
afterSale, null, afterSale.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
return afterSale;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void agreeAfterSale(Long userId, Long id) {
|
||||
// 校验售后单存在,并状态未审批
|
||||
TradeAfterSaleDO afterSale = validateAfterSaleAuditable(id);
|
||||
|
||||
// 更新售后单的状态
|
||||
// 情况一:退款:标记为 WAIT_REFUND 状态。后续等退款发起成功后,在标记为 COMPLETE 状态
|
||||
// 情况二:退货退款:需要等用户退货后,才能发起退款
|
||||
Integer newStatus = afterSale.getType().equals(TradeAfterSaleWayEnum.REFUND.getWay()) ?
|
||||
TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus() : TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus();
|
||||
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO()
|
||||
.setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), newStatus);
|
||||
|
||||
// TODO 发送售后消息
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void disagreeAfterSale(Long userId, TradeAfterSaleDisagreeReqVO auditReqVO) {
|
||||
// 校验售后单存在,并状态未审批
|
||||
TradeAfterSaleDO afterSale = validateAfterSaleAuditable(auditReqVO.getId());
|
||||
|
||||
// 更新售后单的状态
|
||||
Integer newStatus = TradeAfterSaleStatusEnum.SELLER_DISAGREE.getStatus();
|
||||
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.APPLY.getStatus(), new TradeAfterSaleDO()
|
||||
.setStatus(newStatus).setAuditUserId(userId).setAuditTime(LocalDateTime.now())
|
||||
.setAuditReason(auditReqVO.getAuditReason()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), newStatus);
|
||||
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【未申请】
|
||||
tradeOrderService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验售后单是否可审批(同意售后、拒绝售后)
|
||||
*
|
||||
* @param id 售后编号
|
||||
* @return 售后单
|
||||
*/
|
||||
private TradeAfterSaleDO validateAfterSaleAuditable(Long id) {
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
|
||||
if (afterSale == null) {
|
||||
throw exception(AFTER_SALE_NOT_FOUND);
|
||||
}
|
||||
if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus())) {
|
||||
throw exception(AFTER_SALE_AUDIT_FAIL_STATUS_NOT_APPLY);
|
||||
}
|
||||
return afterSale;
|
||||
}
|
||||
|
||||
private void updateAfterSaleStatus(Long id, Integer status, TradeAfterSaleDO updateObj) {
|
||||
int updateCount = tradeAfterSaleMapper.updateByIdAndStatus(id, status, updateObj);
|
||||
if (updateCount == 0) {
|
||||
throw exception(AFTER_SALE_UPDATE_STATUS_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deliveryAfterSale(Long userId, AppTradeAfterSaleDeliveryReqVO deliveryReqVO) {
|
||||
// 校验售后单存在,并状态未退货
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(deliveryReqVO.getId());
|
||||
if (afterSale == null) {
|
||||
throw exception(AFTER_SALE_NOT_FOUND);
|
||||
}
|
||||
if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus())) {
|
||||
throw exception(AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE);
|
||||
}
|
||||
|
||||
// 更新售后单的物流信息
|
||||
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus(), new TradeAfterSaleDO()
|
||||
.setStatus(TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus())
|
||||
.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo())
|
||||
.setDeliveryTime(deliveryReqVO.getDeliveryTime()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.MEMBER.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void receiveAfterSale(Long userId, Long id) {
|
||||
// 校验售后单存在,并状态为已退货
|
||||
TradeAfterSaleDO afterSale = validateAfterSaleReceivable(id);
|
||||
|
||||
// 更新售后单的状态
|
||||
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus(), new TradeAfterSaleDO()
|
||||
.setStatus(TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus()).setReceiveTime(LocalDateTime.now()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void refuseAfterSale(Long userId, TradeAfterSaleRefuseReqVO refuseReqVO) {
|
||||
// 校验售后单存在,并状态为已退货
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(refuseReqVO.getId());
|
||||
if (afterSale == null) {
|
||||
throw exception(AFTER_SALE_NOT_FOUND);
|
||||
}
|
||||
if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus())) {
|
||||
throw exception(AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY);
|
||||
}
|
||||
|
||||
// 更新售后单的状态
|
||||
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus(), new TradeAfterSaleDO()
|
||||
.setStatus(TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus()).setReceiveTime(LocalDateTime.now())
|
||||
.setReceiveReason(refuseReqVO.getRefuseMemo()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【未申请】
|
||||
tradeOrderService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验售后单是否可收货,即处于买家已发货
|
||||
*
|
||||
* @param id 售后编号
|
||||
* @return 售后单
|
||||
*/
|
||||
private TradeAfterSaleDO validateAfterSaleReceivable(Long id) {
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
|
||||
if (afterSale == null) {
|
||||
throw exception(AFTER_SALE_NOT_FOUND);
|
||||
}
|
||||
if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_DELIVERY.getStatus())) {
|
||||
throw exception(AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY);
|
||||
}
|
||||
return afterSale;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void refundAfterSale(Long userId, String userIp, Long id) {
|
||||
// 校验售后单的状态,并状态待退款
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectByPayRefundId(id);
|
||||
if (afterSale == null) {
|
||||
throw exception(AFTER_SALE_NOT_FOUND);
|
||||
}
|
||||
if (ObjectUtil.notEqual(afterSale.getStatus(), TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus())) {
|
||||
throw exception(AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND);
|
||||
}
|
||||
|
||||
// 发起退款单。注意,需要在事务提交后,再进行发起,避免重复发起
|
||||
createPayRefund(userIp, afterSale);
|
||||
|
||||
// 更新售后单的状态为【已完成】
|
||||
updateAfterSaleStatus(afterSale.getId(), TradeAfterSaleStatusEnum.WAIT_REFUND.getStatus(), new TradeAfterSaleDO()
|
||||
.setStatus(TradeAfterSaleStatusEnum.COMPLETE.getStatus()).setRefundTime(LocalDateTime.now()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.ADMIN.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.COMPLETE.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【已完成】
|
||||
tradeOrderService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), afterSale.getRefundPrice());
|
||||
}
|
||||
|
||||
private void createPayRefund(String userIp, TradeAfterSaleDO afterSale) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
// 创建退款单
|
||||
PayRefundCreateReqDTO createReqDTO = TradeAfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties);
|
||||
Long payRefundId = payRefundApi.createPayRefund(createReqDTO);
|
||||
// 更新售后单的退款单号
|
||||
tradeAfterSaleMapper.updateById(new TradeAfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelAfterSale(Long userId, Long id) {
|
||||
// 校验售后单的状态,并状态待退款
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectByPayRefundId(id);
|
||||
if (afterSale == null) {
|
||||
throw exception(AFTER_SALE_NOT_FOUND);
|
||||
}
|
||||
if (ObjectUtils.equalsAny(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
TradeAfterSaleStatusEnum.SELLER_AGREE.getStatus())) {
|
||||
throw exception(AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE);
|
||||
}
|
||||
|
||||
// 更新售后单的状态为【已取消】
|
||||
updateAfterSaleStatus(afterSale.getId(), afterSale.getStatus(), new TradeAfterSaleDO()
|
||||
.setStatus(TradeAfterSaleStatusEnum.BUYER_CANCEL.getStatus()));
|
||||
|
||||
// 记录售后日志
|
||||
createAfterSaleLog(userId, UserTypeEnum.MEMBER.getValue(),
|
||||
afterSale, afterSale.getStatus(), TradeAfterSaleStatusEnum.BUYER_CANCEL.getStatus());
|
||||
|
||||
// TODO 发送售后消息
|
||||
|
||||
// 更新交易订单项的售后状态为【未申请】
|
||||
tradeOrderService.updateOrderItemAfterSaleStatus(afterSale.getOrderItemId(),
|
||||
TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
|
||||
}
|
||||
|
||||
private void createAfterSaleLog(Long userId, Integer userType, TradeAfterSaleDO afterSale,
|
||||
Integer beforeStatus, Integer afterStatus) {
|
||||
TradeAfterSaleLogDO afterSaleLog = new TradeAfterSaleLogDO().setUserId(userId).setUserType(userType)
|
||||
.setAfterSaleId(afterSale.getId()).setOrderId(afterSale.getOrderId())
|
||||
.setOrderItemId(afterSale.getOrderItemId()).setBeforeStatus(beforeStatus).setAfterStatus(afterStatus)
|
||||
.setContent(TradeAfterSaleStatusEnum.valueOf(afterStatus).getContent());
|
||||
tradeAfterSaleLogMapper.insert(afterSaleLog);
|
||||
}
|
||||
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.cart;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 购物车 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface TradeCartService {
|
||||
|
||||
/**
|
||||
* 添加商品到购物车
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param addCountReqVO 添加信息
|
||||
*/
|
||||
void addCartItemCount(Long userId, @Valid AppTradeCartItemAddCountReqVO addCountReqVO);
|
||||
|
||||
/**
|
||||
* 更新购物车商品数量
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param updateCountReqVO 更新信息
|
||||
*/
|
||||
void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO);
|
||||
|
||||
/**
|
||||
* 更新购物车商品是否选中
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param updateSelectedReqVO 更新信息
|
||||
*/
|
||||
void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO);
|
||||
|
||||
/**
|
||||
* 删除购物车商品
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param skuIds SKU 编号的数组
|
||||
*/
|
||||
void deleteCartItems(Long userId, Collection<Long> skuIds);
|
||||
|
||||
/**
|
||||
* 查询用户在购物车中的商品数量
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 商品数量
|
||||
*/
|
||||
Integer getCartCount(Long userId);
|
||||
|
||||
/**
|
||||
* 查询用户的购物车详情
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 购物车详情
|
||||
*/
|
||||
AppTradeCartDetailRespVO getCartDetail(Long userId);
|
||||
|
||||
}
|
@@ -1,184 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.cart;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
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.enums.common.PromotionLevelEnum;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.cart.TradeCartConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.cart.TradeCartItemMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* 购物车 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class TradeCartServiceImpl implements TradeCartService {
|
||||
|
||||
@Resource
|
||||
private TradeCartItemMapper cartItemMapper;
|
||||
|
||||
@Resource
|
||||
private ProductSkuApi productSkuApi;
|
||||
@Resource
|
||||
private PriceApi priceApi;
|
||||
|
||||
@Override
|
||||
public void addCartItemCount(Long userId, AppTradeCartItemAddCountReqVO addCountReqVO) {
|
||||
Long skuId = addCountReqVO.getSkuId();
|
||||
Integer count = addCountReqVO.getCount();
|
||||
// 查询 CartItemDO
|
||||
TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, addCountReqVO.getSkuId());
|
||||
|
||||
// 存在,则进行数量更新
|
||||
if (tradeItem != null) {
|
||||
checkProductSku(skuId, tradeItem.getCount() + count);
|
||||
cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId())
|
||||
.setSelected(true).setCount(tradeItem.getCount() + count));
|
||||
return;
|
||||
}
|
||||
|
||||
// 不存在,则进行插入
|
||||
ProductSkuRespDTO sku = checkProductSku(skuId, count);
|
||||
cartItemMapper.insert(new TradeCartItemDO().setUserId(userId).setSpuId(sku.getSpuId()).setSkuId(sku.getId())
|
||||
.setSelected(true).setCount(count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO) {
|
||||
// 校验 TradeCartItemDO 存在
|
||||
TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, updateCountReqVO.getSkuId());
|
||||
if (tradeItem == null) {
|
||||
throw exception(CARD_ITEM_NOT_FOUND);
|
||||
}
|
||||
// 校验商品 SKU
|
||||
checkProductSku(updateCountReqVO.getSkuId(), updateCountReqVO.getCount());
|
||||
|
||||
// 更新数量
|
||||
cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId()).setCount(updateCountReqVO.getCount()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) {
|
||||
// 查询 CartItemDO 列表
|
||||
List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, updateSelectedReqVO.getSkuIds());
|
||||
if (CollUtil.isEmpty(cartItems)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新选中
|
||||
cartItemMapper.updateByIds(CollectionUtils.convertList(cartItems, TradeCartItemDO::getId),
|
||||
new TradeCartItemDO().setSelected(updateSelectedReqVO.getSelected()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 购物车删除商品
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param skuIds 商品 SKU 编号的数组
|
||||
*/
|
||||
@Override
|
||||
public void deleteCartItems(Long userId, Collection<Long> skuIds) {
|
||||
// 查询 CartItemDO 列表
|
||||
List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, skuIds);
|
||||
if (CollUtil.isEmpty(cartItems)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 批量标记删除
|
||||
cartItemMapper.deleteBatchIds(CollectionUtils.convertSet(cartItems, TradeCartItemDO::getId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCartCount(Long userId) {
|
||||
return cartItemMapper.selectSumByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppTradeCartDetailRespVO getCartDetail(Long userId) {
|
||||
// 获得购物车的商品
|
||||
List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserId(userId, null);
|
||||
// 如果未空,则返回空结果
|
||||
if (CollUtil.isEmpty(cartItems)) {
|
||||
return TradeCartConvert.INSTANCE.buildEmptyAppTradeCartDetailRespVO();
|
||||
}
|
||||
|
||||
// 调用价格服务,计算价格
|
||||
PriceCalculateRespDTO priceCalculate = priceApi.calculatePrice(TradeCartConvert.INSTANCE.convert(userId, cartItems));
|
||||
|
||||
// 转换返回
|
||||
Map<Long, TradeCartItemDO> cartItemMap = convertMap(cartItems, TradeCartItemDO::getSkuId);
|
||||
Map<Long, PriceCalculateRespDTO.OrderItem> orderItemMap = convertMap(priceCalculate.getOrder().getItems(),
|
||||
PriceCalculateRespDTO.OrderItem::getSkuId);
|
||||
List<AppTradeCartDetailRespVO.ItemGroup> itemGroups = new ArrayList<>(cartItems.size());
|
||||
// ① 场景一,营销活动,订单级别 TODO 芋艿:待测试
|
||||
priceCalculate.getPromotions().stream().filter(promotion -> PromotionLevelEnum.ORDER.getLevel().equals(promotion.getLevel()))
|
||||
.forEach(promotion -> {
|
||||
AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>())
|
||||
.setPromotion(TradeCartConvert.INSTANCE.convert(promotion));
|
||||
itemGroups.add(itemGroup);
|
||||
promotion.getItems().forEach(promotionItem -> {
|
||||
PriceCalculateRespDTO.OrderItem orderItem = orderItemMap.remove(promotionItem.getSkuId());
|
||||
Assert.notNull(orderItem, "商品 SKU({}) 对应的订单项不能为空", promotionItem.getSkuId());
|
||||
TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
|
||||
itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
|
||||
});
|
||||
});
|
||||
// ② 场景二,营销活动,商品级别
|
||||
orderItemMap.values().forEach(orderItem -> {
|
||||
AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>(1)).setPromotion(null);
|
||||
itemGroups.add(itemGroup);
|
||||
TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
|
||||
itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
|
||||
});
|
||||
return new AppTradeCartDetailRespVO().setItemGroups(itemGroups)
|
||||
.setOrder(TradeCartConvert.INSTANCE.convert(priceCalculate.getOrder()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验商品 SKU 是否合法
|
||||
* 1. 是否存在
|
||||
* 2. 是否下架
|
||||
* 3. 库存不足
|
||||
*
|
||||
* @param skuId 商品 SKU 编号
|
||||
* @param count 商品数量
|
||||
* @return 商品 SKU
|
||||
*/
|
||||
private ProductSkuRespDTO checkProductSku(Long skuId, Integer count) {
|
||||
ProductSkuRespDTO sku = productSkuApi.getSku(skuId);
|
||||
if (sku == null || CommonStatusEnum.DISABLE.getStatus().equals(sku.getStatus())) {
|
||||
throw exception(SKU_NOT_EXISTS);
|
||||
}
|
||||
if (count > sku.getStock()) {
|
||||
throw exception(SKU_STOCK_NOT_ENOUGH);
|
||||
}
|
||||
return sku;
|
||||
}
|
||||
|
||||
}
|
@@ -1,142 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
|
||||
/**
|
||||
* 交易订单 Service 接口
|
||||
*
|
||||
* @author LeeYan9
|
||||
* @since 2022-08-26
|
||||
*/
|
||||
public interface TradeOrderService {
|
||||
|
||||
// =================== Order ===================
|
||||
|
||||
/**
|
||||
* 【会员】创建交易订单
|
||||
*
|
||||
* @param userId 登录用户
|
||||
* @param userIp 用户 IP 地址
|
||||
* @param createReqVO 创建交易订单请求模型
|
||||
* @return 交易订单的编号
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* 【会员】收货交易订单
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param id 订单编号
|
||||
*/
|
||||
void receiveOrder(Long userId, Long id);
|
||||
|
||||
/**
|
||||
* 获得指定编号的交易订单
|
||||
*
|
||||
* @param id 交易订单编号
|
||||
* @return 交易订单
|
||||
*/
|
||||
TradeOrderDO getOrder(Long id);
|
||||
|
||||
/**
|
||||
* 获得指定用户,指定的交易订单
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param id 交易订单编号
|
||||
* @return 交易订单
|
||||
*/
|
||||
TradeOrderDO getOrder(Long userId, Long id);
|
||||
|
||||
/**
|
||||
* 【管理员】获得交易订单分页
|
||||
*
|
||||
* @param reqVO 分页请求
|
||||
* @return 交易订单
|
||||
*/
|
||||
PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 【会员】获得交易订单分页
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 分页请求
|
||||
* @return 交易订单
|
||||
*/
|
||||
PageResult<TradeOrderDO> getOrderPage(Long userId, AppTradeOrderPageReqVO reqVO);
|
||||
|
||||
// =================== Order Item ===================
|
||||
|
||||
/**
|
||||
* 获得指定用户,指定的交易订单项
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param itemId 交易订单项编号
|
||||
* @return 交易订单项
|
||||
*/
|
||||
TradeOrderItemDO getOrderItem(Long userId, Long itemId);
|
||||
|
||||
/**
|
||||
* 更新交易订单项的售后状态
|
||||
*
|
||||
* @param id 交易订单项编号
|
||||
* @param oldAfterSaleStatus 当前售后状态;如果不符,更新后会抛出异常
|
||||
* @param newAfterSaleStatus 目标售后状态
|
||||
* @param refundPrice 退款金额;当订单项退款成功时,必须传递该值
|
||||
*/
|
||||
void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus,
|
||||
Integer newAfterSaleStatus, Integer refundPrice);
|
||||
|
||||
/**
|
||||
* 根据交易订单项编号数组,查询交易订单项
|
||||
*
|
||||
* @param ids 交易订单项编号数组
|
||||
* @return 交易订单项数组
|
||||
*/
|
||||
List<TradeOrderItemDO> getOrderItemList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 根据交易订单编号,查询交易订单项
|
||||
*
|
||||
* @param orderId 交易订单编号
|
||||
* @return 交易订单项数组
|
||||
*/
|
||||
default List<TradeOrderItemDO> getOrderItemListByOrderId(Long orderId) {
|
||||
return getOrderItemListByOrderId(singleton(orderId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据交易订单编号数组,查询交易订单项
|
||||
*
|
||||
* @param orderIds 交易订单编号数组
|
||||
* @return 交易订单项数组
|
||||
*/
|
||||
List<TradeOrderItemDO> getOrderItemListByOrderId(Collection<Long> orderIds);
|
||||
|
||||
}
|
@@ -1,530 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.module.member.api.address.AddressApi;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
|
||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
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.dto.CouponUseReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
|
||||
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.admin.order.vo.TradeOrderPageReqVO;
|
||||
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.AppTradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
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.mysql.order.TradeOrderItemMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.*;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_ORDER_NOT_FOUND;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 交易订单 Service 实现类
|
||||
*
|
||||
* @author LeeYan9
|
||||
* @since 2022-08-26
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
|
||||
@Resource
|
||||
private TradeOrderMapper tradeOrderMapper;
|
||||
@Resource
|
||||
private TradeOrderItemMapper tradeOrderItemMapper;
|
||||
|
||||
@Resource
|
||||
private PriceApi priceApi;
|
||||
@Resource
|
||||
private ProductSkuApi productSkuApi;
|
||||
@Resource
|
||||
private ProductSpuApi productSpuApi;
|
||||
@Resource
|
||||
private PayOrderApi payOrderApi;
|
||||
@Resource
|
||||
private AddressApi addressApi;
|
||||
@Resource
|
||||
private CouponApi couponApi;
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@Resource
|
||||
private TradeOrderProperties tradeOrderProperties;
|
||||
|
||||
// =================== Order ===================
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) {
|
||||
// 商品 SKU 检查:可售状态、库存
|
||||
List<ProductSkuRespDTO> skus = validateSkuSaleable(createReqVO.getItems());
|
||||
// 商品 SPU 检查:可售状态
|
||||
List<ProductSpuRespDTO> spus = validateSpuSaleable(convertSet(skus, ProductSkuRespDTO::getSpuId));
|
||||
// 用户收件地址的校验
|
||||
AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId());
|
||||
|
||||
// 价格计算
|
||||
PriceCalculateRespDTO priceResp = priceApi.calculatePrice(TradeOrderConvert.INSTANCE.convert(createReqVO, userId));
|
||||
|
||||
// 插入 TradeOrderDO 订单
|
||||
TradeOrderDO tradeOrderDO = createTradeOrder(userId, userIp, createReqVO, priceResp.getOrder(), address);
|
||||
// 插入 TradeOrderItemDO 订单项
|
||||
List<TradeOrderItemDO> tradeOrderItems = createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus);
|
||||
|
||||
// 订单创建完后的逻辑
|
||||
afterCreateTradeOrder(userId, createReqVO, tradeOrderDO, tradeOrderItems, spus);
|
||||
// TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
|
||||
return tradeOrderDO.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验商品 SKU 是否可出售
|
||||
*
|
||||
* @param items 商品 SKU
|
||||
* @return 商品 SKU 数组
|
||||
*/
|
||||
private List<ProductSkuRespDTO> validateSkuSaleable(List<Item> items) {
|
||||
List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(convertSet(items, Item::getSkuId));
|
||||
// SKU 不存在
|
||||
if (items.size() != skus.size()) {
|
||||
throw exception(ORDER_CREATE_SKU_NOT_FOUND);
|
||||
}
|
||||
// 校验是否禁用 or 库存不足
|
||||
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
|
||||
items.forEach(item -> {
|
||||
ProductSkuRespDTO sku = skuMap.get(item.getSkuId());
|
||||
// SKU 禁用
|
||||
if (ObjectUtil.notEqual(CommonStatusEnum.ENABLE.getStatus(), sku.getStatus())) {
|
||||
throw exception(ORDER_CREATE_SKU_NOT_SALE);
|
||||
}
|
||||
// SKU 库存不足
|
||||
if (item.getCount() > sku.getStock()) {
|
||||
throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_STOCK_NOT_ENOUGH);
|
||||
}
|
||||
});
|
||||
return skus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验商品 SPU 是否可出售
|
||||
*
|
||||
* @param spuIds 商品 SPU 编号数组
|
||||
* @return 商品 SPU 数组
|
||||
*/
|
||||
private List<ProductSpuRespDTO> validateSpuSaleable(Set<Long> spuIds) {
|
||||
List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(spuIds);
|
||||
// SPU 不存在
|
||||
if (spus.size() != spuIds.size()) {
|
||||
throw exception(ORDER_CREATE_SPU_NOT_FOUND);
|
||||
}
|
||||
// 校验是否存在禁用的 SPU
|
||||
ProductSpuRespDTO spu = CollectionUtils.findFirst(spus,
|
||||
spuDTO -> ObjectUtil.notEqual(ProductSpuStatusEnum.ENABLE.getStatus(), spuDTO.getStatus()));
|
||||
if (spu != null) {
|
||||
throw exception(ErrorCodeConstants.ORDER_CREATE_SPU_NOT_SALE);
|
||||
}
|
||||
return spus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验收件地址是否存在
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param addressId 收件地址编号
|
||||
* @return 收件地址
|
||||
*/
|
||||
private AddressRespDTO validateAddress(Long userId, Long addressId) {
|
||||
AddressRespDTO address = addressApi.getAddress(addressId, userId);
|
||||
if (Objects.isNull(address)) {
|
||||
throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO,
|
||||
PriceCalculateRespDTO.Order order, AddressRespDTO address) {
|
||||
TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, order, address);
|
||||
tradeOrderDO.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的;
|
||||
tradeOrderDO.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
|
||||
tradeOrderDO.setType(TradeOrderTypeEnum.NORMAL.getType());
|
||||
tradeOrderDO.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.NONE.getStatus());
|
||||
tradeOrderDO.setProductCount(getSumValue(order.getItems(), PriceCalculateRespDTO.OrderItem::getCount, Integer::sum));
|
||||
tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
|
||||
tradeOrderDO.setAdjustPrice(0).setPayed(false); // 支付信息
|
||||
tradeOrderDO.setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus()); // 物流信息
|
||||
tradeOrderDO.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息
|
||||
tradeOrderMapper.insert(tradeOrderDO);
|
||||
return tradeOrderDO;
|
||||
}
|
||||
|
||||
private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO,
|
||||
List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) {
|
||||
List<TradeOrderItemDO> tradeOrderItemDOs = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, orderItems, skus);
|
||||
tradeOrderItemMapper.insertBatch(tradeOrderItemDOs);
|
||||
return tradeOrderItemDOs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行创建完创建完订单后的逻辑
|
||||
*
|
||||
* 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param createReqVO 创建订单请求
|
||||
* @param tradeOrderDO 交易订单
|
||||
*/
|
||||
private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
|
||||
TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs,
|
||||
List<ProductSpuRespDTO> spus) {
|
||||
// 下单时扣减商品库存
|
||||
productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(tradeOrderItemDOs)));
|
||||
|
||||
// 删除购物车商品 TODO 芋艿:待实现
|
||||
|
||||
// 扣减积分,抵扣金额 TODO 芋艿:待实现
|
||||
|
||||
// 有使用优惠券时更新
|
||||
if (createReqVO.getCouponId() != null) {
|
||||
couponApi.useCoupon(new CouponUseReqDTO().setId(createReqVO.getCouponId()).setUserId(userId)
|
||||
.setOrderId(tradeOrderDO.getId()));
|
||||
}
|
||||
|
||||
// 生成预支付
|
||||
createPayOrder(tradeOrderDO, tradeOrderItemDOs, spus);
|
||||
|
||||
// 增加订单日志 TODO 芋艿:待实现
|
||||
}
|
||||
|
||||
private void createPayOrder(TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs,
|
||||
List<ProductSpuRespDTO> spus) {
|
||||
// 创建支付单,用于后续的支付
|
||||
PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
|
||||
tradeOrderDO, tradeOrderItemDOs, spus, tradeOrderProperties);
|
||||
Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO);
|
||||
|
||||
// 更新到交易单上
|
||||
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);
|
||||
}
|
||||
|
||||
// TODO 芋艿:如果无需发货,需要怎么存储?
|
||||
@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
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void receiveOrder(Long userId, Long id) {
|
||||
// 校验并获得交易订单(可收货)
|
||||
TradeOrderDO order = validateOrderReceivable(userId, id);
|
||||
|
||||
// 更新 TradeOrderDO 状态为已完成
|
||||
int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
|
||||
new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus())
|
||||
.setDeliveryStatus(TradeOrderDeliveryStatusEnum.RECEIVED.getStatus()).setReceiveTime(LocalDateTime.now()));
|
||||
if (updateCount == 0) {
|
||||
throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
|
||||
}
|
||||
|
||||
// TODO 芋艿:OrderLog
|
||||
|
||||
// TODO 芋艿:lili 发送订单变化的消息
|
||||
|
||||
// TODO 芋艿:lili 发送商品被购买完成的数据
|
||||
}
|
||||
|
||||
@Override
|
||||
public TradeOrderDO getOrder(Long id) {
|
||||
return tradeOrderMapper.selectById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验交易订单满足可售货的条件
|
||||
*
|
||||
* 1. 交易订单待收货
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param id 交易订单编号
|
||||
* @return 交易订单
|
||||
*/
|
||||
private TradeOrderDO validateOrderReceivable(Long userId, Long id) {
|
||||
// 校验订单是否存在
|
||||
TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId);
|
||||
if (order == null) {
|
||||
throw exception(ORDER_NOT_FOUND);
|
||||
}
|
||||
// 校验订单是否是待收货状态
|
||||
if (!TradeOrderStatusEnum.isDelivered(order.getStatus())
|
||||
|| ObjectUtil.notEqual(order.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.DELIVERED.getStatus())) {
|
||||
throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TradeOrderDO getOrder(Long userId, Long id) {
|
||||
TradeOrderDO order = tradeOrderMapper.selectById(id);
|
||||
if (order != null
|
||||
&& ObjectUtil.notEqual(order.getUserId(), userId)) {
|
||||
return null;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO) {
|
||||
// 获得 userId 相关的查询
|
||||
Set<Long> userIds = new HashSet<>();
|
||||
if (StrUtil.isNotEmpty(reqVO.getUserMobile())) {
|
||||
MemberUserRespDTO user = memberUserApi.getUserByMobile(reqVO.getUserMobile());
|
||||
if (user == null) { // 没查询到用户,说明肯定也没他的订单
|
||||
return new PageResult<>();
|
||||
}
|
||||
userIds.add(user.getId());
|
||||
}
|
||||
if (StrUtil.isNotEmpty(reqVO.getUserNickname())) {
|
||||
List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(reqVO.getUserNickname());
|
||||
if (CollUtil.isEmpty(users)) { // 没查询到用户,说明肯定也没他的订单
|
||||
return new PageResult<>();
|
||||
}
|
||||
userIds.addAll(convertSet(users, MemberUserRespDTO::getId));
|
||||
}
|
||||
// 分页查询
|
||||
return tradeOrderMapper.selectPage(reqVO, userIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<TradeOrderDO> getOrderPage(Long userId, AppTradeOrderPageReqVO reqVO) {
|
||||
return tradeOrderMapper.selectPage(reqVO, userId);
|
||||
}
|
||||
|
||||
// =================== Order Item ===================
|
||||
|
||||
@Override
|
||||
public TradeOrderItemDO getOrderItem(Long userId, Long itemId) {
|
||||
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(itemId);
|
||||
if (orderItem != null
|
||||
&& ObjectUtil.notEqual(orderItem.getUserId(), userId)) {
|
||||
return null;
|
||||
}
|
||||
return orderItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus, Integer refundPrice) {
|
||||
// 如果退款成功,则 refundPrice 非空
|
||||
if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())
|
||||
&& refundPrice == null) {
|
||||
throw new IllegalArgumentException(StrUtil.format("id({}) 退款成功,退款金额不能为空", id));
|
||||
}
|
||||
|
||||
// 更新订单项
|
||||
int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus);
|
||||
if (updateCount <= 0) {
|
||||
throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
|
||||
}
|
||||
|
||||
// 如果有退款金额,则需要更新订单
|
||||
if (refundPrice == null) {
|
||||
return;
|
||||
}
|
||||
// 计算总的退款金额
|
||||
TradeOrderDO order = tradeOrderMapper.selectById(tradeOrderItemMapper.selectById(id).getOrderId());
|
||||
Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
|
||||
if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice)
|
||||
.setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
|
||||
|
||||
// TODO 芋艿:记录订单日志
|
||||
|
||||
// TODO 芋艿:站内信?
|
||||
} else { // 如果部分售后,则更新退款金额
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
|
||||
.setAfterSaleStatus(TradeOrderAfterSaleStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice));
|
||||
}
|
||||
|
||||
// TODO 芋艿:未来如果有分佣,需要更新相关分佣订单为已失效
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TradeOrderItemDO> getOrderItemList(Collection<Long> ids) {
|
||||
return tradeOrderItemMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TradeOrderItemDO> getOrderItemListByOrderId(Collection<Long> orderIds) {
|
||||
return tradeOrderItemMapper.selectListByOrderId(orderIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定订单的所有订单项,是不是都售后成功
|
||||
*
|
||||
* @param id 订单编号
|
||||
* @return 是否都售后成功
|
||||
*/
|
||||
private boolean isAllOrderItemAfterSaleSuccess(Long id) {
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
|
||||
return orderItems.stream().allMatch(orderItem -> Objects.equals(orderItem.getAfterSaleStatus(),
|
||||
TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus()));
|
||||
}
|
||||
|
||||
}
|
@@ -1,154 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleLogDO;
|
||||
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.mysql.aftersale.TradeAfterSaleLogMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.TradeAfterSaleMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link TradeAfterSaleService} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(TradeAfterSaleServiceImpl.class)
|
||||
public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private TradeAfterSaleServiceImpl tradeAfterSaleService;
|
||||
|
||||
@Resource
|
||||
private TradeAfterSaleMapper tradeAfterSaleMapper;
|
||||
@Resource
|
||||
private TradeAfterSaleLogMapper tradeAfterSaleLogMapper;
|
||||
|
||||
@MockBean
|
||||
private TradeOrderService tradeOrderService;
|
||||
@MockBean
|
||||
private PayRefundApi payRefundApi;
|
||||
|
||||
@MockBean
|
||||
private TradeOrderProperties tradeOrderProperties;
|
||||
|
||||
@Test
|
||||
public void testCreateAfterSale() {
|
||||
// 准备参数
|
||||
Long userId = 1024L;
|
||||
AppTradeAfterSaleCreateReqVO createReqVO = new AppTradeAfterSaleCreateReqVO()
|
||||
.setOrderItemId(1L).setRefundPrice(100).setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay())
|
||||
.setApplyReason("退钱").setApplyDescription("快退")
|
||||
.setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png"));
|
||||
// mock 方法(交易订单项)
|
||||
TradeOrderItemDO orderItem = randomPojo(TradeOrderItemDO.class, o -> {
|
||||
o.setOrderId(111L).setUserId(userId).setOrderDividePrice(200);
|
||||
o.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
});
|
||||
when(tradeOrderService.getOrderItem(eq(1024L), eq(1L)))
|
||||
.thenReturn(orderItem);
|
||||
// mock 方法(交易订单)
|
||||
TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> o.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus())
|
||||
.setNo("202211301234"));
|
||||
when(tradeOrderService.getOrder(eq(1024L), eq(111L))).thenReturn(order);
|
||||
|
||||
// 调用
|
||||
Long afterSaleId = tradeAfterSaleService.createAfterSale(userId, createReqVO);
|
||||
// 断言(TradeAfterSaleDO)
|
||||
TradeAfterSaleDO afterSale = tradeAfterSaleMapper.selectById(afterSaleId);
|
||||
assertNotNull(afterSale.getNo());
|
||||
assertEquals(afterSale.getStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus());
|
||||
assertEquals(afterSale.getType(), TradeAfterSaleTypeEnum.IN_SALE.getType());
|
||||
assertPojoEquals(afterSale, createReqVO);
|
||||
assertEquals(afterSale.getUserId(), 1024L);
|
||||
assertPojoEquals(afterSale, orderItem, "id", "creator", "createTime", "updater", "updateTime");
|
||||
assertEquals(afterSale.getOrderNo(), "202211301234");
|
||||
assertNull(afterSale.getPayRefundId());
|
||||
assertNull(afterSale.getRefundTime());
|
||||
assertNull(afterSale.getLogisticsId());
|
||||
assertNull(afterSale.getLogisticsNo());
|
||||
assertNull(afterSale.getDeliveryTime());
|
||||
assertNull(afterSale.getReceiveReason());
|
||||
// 断言(TradeAfterSaleLogDO)
|
||||
TradeAfterSaleLogDO afterSaleLog = tradeAfterSaleLogMapper.selectList().get(0);
|
||||
assertEquals(afterSaleLog.getUserId(), userId);
|
||||
assertEquals(afterSaleLog.getUserType(), UserTypeEnum.MEMBER.getValue());
|
||||
assertEquals(afterSaleLog.getAfterSaleId(), afterSaleId);
|
||||
assertPojoEquals(afterSale, orderItem, "id", "creator", "createTime", "updater", "updateTime");
|
||||
assertNull(afterSaleLog.getBeforeStatus());
|
||||
assertEquals(afterSaleLog.getAfterStatus(), TradeAfterSaleStatusEnum.APPLY.getStatus());
|
||||
assertEquals(afterSaleLog.getContent(), TradeAfterSaleStatusEnum.APPLY.getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAfterSalePage() {
|
||||
// mock 数据
|
||||
TradeAfterSaleDO dbAfterSale = randomPojo(TradeAfterSaleDO.class, o -> { // 等会查询到
|
||||
o.setNo("202211190847450020500077");
|
||||
o.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
|
||||
o.setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay());
|
||||
o.setType(TradeAfterSaleTypeEnum.IN_SALE.getType());
|
||||
o.setOrderNo("202211190847450020500011");
|
||||
o.setSpuName("芋艿");
|
||||
o.setCreateTime(buildTime(2022, 1, 15));
|
||||
});
|
||||
tradeAfterSaleMapper.insert(dbAfterSale);
|
||||
// 测试 no 不匹配
|
||||
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setNo("202211190847450020500066")));
|
||||
// 测试 status 不匹配
|
||||
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setStatus(TradeAfterSaleStatusEnum.SELLER_REFUSE.getStatus())));
|
||||
// 测试 way 不匹配
|
||||
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setWay(TradeAfterSaleWayEnum.REFUND.getWay())));
|
||||
// 测试 type 不匹配
|
||||
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setType(TradeAfterSaleTypeEnum.AFTER_SALE.getType())));
|
||||
// 测试 orderNo 不匹配
|
||||
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setOrderNo("202211190847450020500022")));
|
||||
// 测试 spuName 不匹配
|
||||
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setSpuName("土豆")));
|
||||
// 测试 createTime 不匹配
|
||||
tradeAfterSaleMapper.insert(cloneIgnoreId(dbAfterSale, o -> o.setCreateTime(buildTime(2022, 1, 20))));
|
||||
// 准备参数
|
||||
TradeAfterSalePageReqVO reqVO = new TradeAfterSalePageReqVO();
|
||||
reqVO.setNo("20221119084745002050007");
|
||||
reqVO.setStatus(TradeAfterSaleStatusEnum.APPLY.getStatus());
|
||||
reqVO.setWay(TradeAfterSaleWayEnum.RETURN_AND_REFUND.getWay());
|
||||
reqVO.setType(TradeAfterSaleTypeEnum.IN_SALE.getType());
|
||||
reqVO.setOrderNo("20221119084745002050001");
|
||||
reqVO.setSpuName("芋");
|
||||
reqVO.setCreateTime(new LocalDateTime[]{buildTime(2022, 1, 1), buildTime(2022, 1, 16)});
|
||||
|
||||
// 调用
|
||||
PageResult<TradeAfterSaleDO> pageResult = tradeAfterSaleService.getAfterSalePage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbAfterSale, pageResult.getList().get(0));
|
||||
}
|
||||
}
|
@@ -1,320 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.member.api.address.AddressApi;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
|
||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
|
||||
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.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.dal.dataobject.order.TradeOrderDO;
|
||||
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.TradeOrderMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.*;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
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 java.util.Collections.singletonList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link TradeOrderServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-07
|
||||
*/
|
||||
@Import({TradeOrderServiceImpl.class, TradeOrderConfig.class})
|
||||
public class TradeOrderServiceTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private TradeOrderServiceImpl tradeOrderService;
|
||||
|
||||
@Resource
|
||||
private TradeOrderMapper tradeOrderMapper;
|
||||
@Resource
|
||||
private TradeOrderItemMapper tradeOrderItemMapper;
|
||||
|
||||
@MockBean
|
||||
private ProductSpuApi productSpuApi;
|
||||
@MockBean
|
||||
private ProductSkuApi productSkuApi;
|
||||
@MockBean
|
||||
private PriceApi priceApi;
|
||||
@MockBean
|
||||
private PayOrderApi payOrderApi;
|
||||
@MockBean
|
||||
private AddressApi addressApi;
|
||||
@MockBean
|
||||
private CouponApi couponApi;
|
||||
|
||||
@MockBean
|
||||
private TradeOrderProperties tradeOrderProperties;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
when(tradeOrderProperties.getAppId()).thenReturn(888L);
|
||||
when(tradeOrderProperties.getExpireTime()).thenReturn(Duration.ofDays(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTradeOrder_success() {
|
||||
// 准备参数
|
||||
Long userId = 100L;
|
||||
String userIp = "127.0.0.1";
|
||||
AppTradeOrderCreateReqVO reqVO = new AppTradeOrderCreateReqVO()
|
||||
.setAddressId(10L).setCouponId(101L).setRemark("我是备注").setFromCart(true)
|
||||
.setItems(Arrays.asList(new AppTradeOrderCreateReqVO.Item().setSkuId(1L).setCount(3),
|
||||
new AppTradeOrderCreateReqVO.Item().setSkuId(2L).setCount(4)));
|
||||
// mock 方法(商品 SKU 检查)
|
||||
ProductSkuRespDTO sku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(1L).setSpuId(11L)
|
||||
.setPrice(50).setStock(100).setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||
.setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(111L).setValueId(222L))));
|
||||
ProductSkuRespDTO sku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(2L).setSpuId(21L)
|
||||
.setPrice(20).setStock(50).setStatus(CommonStatusEnum.ENABLE.getStatus()))
|
||||
.setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(333L).setValueId(444L)));
|
||||
when(productSkuApi.getSkuList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(sku01, sku02));
|
||||
// mock 方法(商品 SPU 检查)
|
||||
ProductSpuRespDTO spu01 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(11L)
|
||||
.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()).setName("商品 1"));
|
||||
ProductSpuRespDTO spu02 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(21L)
|
||||
.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()));
|
||||
when(productSpuApi.getSpuList(eq(asSet(11L, 21L)))).thenReturn(Arrays.asList(spu01, spu02));
|
||||
// mock 方法(用户收件地址的校验)
|
||||
AddressRespDTO addressRespDTO = new AddressRespDTO().setId(10L).setUserId(userId).setName("芋艿")
|
||||
.setMobile("15601691300").setAreaId(3306L).setPostCode("85757").setDetailAddress("土豆村");
|
||||
when(addressApi.getAddress(eq(10L), eq(userId))).thenReturn(addressRespDTO);
|
||||
// mock 方法(价格计算)
|
||||
PriceCalculateRespDTO.OrderItem priceOrderItem01 = new PriceCalculateRespDTO.OrderItem()
|
||||
.setSpuId(11L).setSkuId(1L).setCount(3).setOriginalPrice(150).setOriginalUnitPrice(50)
|
||||
.setDiscountPrice(20).setPayPrice(130).setOrderPartPrice(7).setOrderDividePrice(35);
|
||||
PriceCalculateRespDTO.OrderItem priceOrderItem02 = new PriceCalculateRespDTO.OrderItem()
|
||||
.setSpuId(21L).setSkuId(2L).setCount(4).setOriginalPrice(80).setOriginalUnitPrice(20)
|
||||
.setDiscountPrice(40).setPayPrice(40).setOrderPartPrice(15).setOrderDividePrice(25);
|
||||
PriceCalculateRespDTO.Order priceOrder = new PriceCalculateRespDTO.Order()
|
||||
.setOriginalPrice(230).setOrderPrice(100).setDiscountPrice(0).setCouponPrice(30)
|
||||
.setPointPrice(10).setDeliveryPrice(20).setPayPrice(80).setCouponId(101L).setCouponPrice(30)
|
||||
.setItems(Arrays.asList(priceOrderItem01, priceOrderItem02));
|
||||
when(priceApi.calculatePrice(argThat(priceCalculateReqDTO -> {
|
||||
assertEquals(priceCalculateReqDTO.getUserId(), 100L);
|
||||
assertEquals(priceCalculateReqDTO.getCouponId(), 101L);
|
||||
assertEquals(priceCalculateReqDTO.getItems().get(0).getSkuId(), 1L);
|
||||
assertEquals(priceCalculateReqDTO.getItems().get(0).getCount(), 3);
|
||||
assertEquals(priceCalculateReqDTO.getItems().get(1).getSkuId(), 2L);
|
||||
assertEquals(priceCalculateReqDTO.getItems().get(1).getCount(), 4);
|
||||
return true;
|
||||
}))).thenReturn(new PriceCalculateRespDTO().setOrder(priceOrder));
|
||||
// mock 方法(创建支付单)
|
||||
when(payOrderApi.createOrder(argThat(createReqDTO -> {
|
||||
assertEquals(createReqDTO.getAppId(), 888L);
|
||||
assertEquals(createReqDTO.getUserIp(), userIp);
|
||||
assertNotNull(createReqDTO.getMerchantOrderId()); // 由于 tradeOrderId 后生成,只能校验非空
|
||||
assertEquals(createReqDTO.getSubject(), "商品 1 等多件");
|
||||
assertNull(createReqDTO.getBody());
|
||||
assertEquals(createReqDTO.getAmount(), 80);
|
||||
assertNotNull(createReqDTO.getExpireTime());
|
||||
return true;
|
||||
}))).thenReturn(1000L);
|
||||
|
||||
// 调用方法
|
||||
Long tradeOrderId = tradeOrderService.createOrder(userId, userIp, reqVO);
|
||||
// 断言 TradeOrderDO 订单
|
||||
List<TradeOrderDO> tradeOrderDOs = tradeOrderMapper.selectList();
|
||||
assertEquals(tradeOrderDOs.size(), 1);
|
||||
TradeOrderDO tradeOrderDO = tradeOrderDOs.get(0);
|
||||
assertEquals(tradeOrderDO.getId(), tradeOrderId);
|
||||
assertNotNull(tradeOrderDO.getNo());
|
||||
assertEquals(tradeOrderDO.getType(), TradeOrderTypeEnum.NORMAL.getType());
|
||||
assertEquals(tradeOrderDO.getTerminal(), TerminalEnum.H5.getTerminal());
|
||||
assertEquals(tradeOrderDO.getUserId(), userId);
|
||||
assertEquals(tradeOrderDO.getUserIp(), userIp);
|
||||
assertEquals(tradeOrderDO.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus());
|
||||
assertEquals(tradeOrderDO.getProductCount(), 7);
|
||||
assertNull(tradeOrderDO.getFinishTime());
|
||||
assertNull(tradeOrderDO.getCancelTime());
|
||||
assertNull(tradeOrderDO.getCancelType());
|
||||
assertEquals(tradeOrderDO.getUserRemark(), "我是备注");
|
||||
assertNull(tradeOrderDO.getRemark());
|
||||
assertFalse(tradeOrderDO.getPayed());
|
||||
assertNull(tradeOrderDO.getPayTime());
|
||||
assertEquals(tradeOrderDO.getOriginalPrice(), 230);
|
||||
assertEquals(tradeOrderDO.getOrderPrice(), 100);
|
||||
assertEquals(tradeOrderDO.getDiscountPrice(), 0);
|
||||
assertEquals(tradeOrderDO.getAdjustPrice(), 0);
|
||||
assertEquals(tradeOrderDO.getPayPrice(), 80);
|
||||
assertEquals(tradeOrderDO.getPayOrderId(), 1000L);
|
||||
assertNull(tradeOrderDO.getPayChannelCode());
|
||||
assertNull(tradeOrderDO.getDeliveryTemplateId());
|
||||
assertNull(tradeOrderDO.getLogisticsId());
|
||||
assertEquals(tradeOrderDO.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus());
|
||||
assertNull(tradeOrderDO.getDeliveryTime());
|
||||
assertNull(tradeOrderDO.getReceiveTime());
|
||||
assertEquals(tradeOrderDO.getReceiverName(), "芋艿");
|
||||
assertEquals(tradeOrderDO.getReceiverMobile(), "15601691300");
|
||||
assertEquals(tradeOrderDO.getReceiverAreaId(), 3306);
|
||||
assertEquals(tradeOrderDO.getReceiverPostCode(), 85757);
|
||||
assertEquals(tradeOrderDO.getReceiverDetailAddress(), "土豆村");
|
||||
assertEquals(tradeOrderDO.getAfterSaleStatus(), TradeOrderAfterSaleStatusEnum.NONE.getStatus());
|
||||
assertEquals(tradeOrderDO.getRefundPrice(), 0);
|
||||
assertEquals(tradeOrderDO.getCouponPrice(), 30);
|
||||
assertEquals(tradeOrderDO.getPointPrice(), 10);
|
||||
// 断言 TradeOrderItemDO 订单(第 1 个)
|
||||
List<TradeOrderItemDO> tradeOrderItemDOs = tradeOrderItemMapper.selectList();
|
||||
assertEquals(tradeOrderItemDOs.size(), 2);
|
||||
TradeOrderItemDO tradeOrderItemDO01 = tradeOrderItemDOs.get(0);
|
||||
assertNotNull(tradeOrderItemDO01.getId());
|
||||
assertEquals(tradeOrderItemDO01.getUserId(), userId);
|
||||
assertEquals(tradeOrderItemDO01.getOrderId(), tradeOrderId);
|
||||
assertEquals(tradeOrderItemDO01.getSpuId(), 11L);
|
||||
assertEquals(tradeOrderItemDO01.getSkuId(), 1L);
|
||||
assertEquals(tradeOrderItemDO01.getProperties().size(), 1);
|
||||
assertEquals(tradeOrderItemDO01.getProperties().get(0).getPropertyId(), 111L);
|
||||
assertEquals(tradeOrderItemDO01.getProperties().get(0).getValueId(), 222L);
|
||||
assertEquals(tradeOrderItemDO01.getSpuName(), sku01.getSpuName());
|
||||
assertEquals(tradeOrderItemDO01.getPicUrl(), sku01.getPicUrl());
|
||||
assertEquals(tradeOrderItemDO01.getCount(), 3);
|
||||
assertEquals(tradeOrderItemDO01.getOriginalPrice(), 150);
|
||||
assertEquals(tradeOrderItemDO01.getOriginalUnitPrice(), 50);
|
||||
assertEquals(tradeOrderItemDO01.getDiscountPrice(), 20);
|
||||
assertEquals(tradeOrderItemDO01.getPayPrice(), 130);
|
||||
assertEquals(tradeOrderItemDO01.getOrderPartPrice(), 7);
|
||||
assertEquals(tradeOrderItemDO01.getOrderDividePrice(), 35);
|
||||
assertEquals(tradeOrderItemDO01.getAfterSaleStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
// 断言 TradeOrderItemDO 订单(第 2 个)
|
||||
TradeOrderItemDO tradeOrderItemDO02 = tradeOrderItemDOs.get(1);
|
||||
assertNotNull(tradeOrderItemDO02.getId());
|
||||
assertEquals(tradeOrderItemDO02.getUserId(), userId);
|
||||
assertEquals(tradeOrderItemDO02.getOrderId(), tradeOrderId);
|
||||
assertEquals(tradeOrderItemDO02.getSpuId(), 21L);
|
||||
assertEquals(tradeOrderItemDO02.getSkuId(), 2L);
|
||||
assertEquals(tradeOrderItemDO02.getProperties().size(), 1);
|
||||
assertEquals(tradeOrderItemDO02.getProperties().get(0).getPropertyId(), 333L);
|
||||
assertEquals(tradeOrderItemDO02.getProperties().get(0).getValueId(), 444L);
|
||||
assertEquals(tradeOrderItemDO02.getSpuName(), sku02.getSpuName());
|
||||
assertEquals(tradeOrderItemDO02.getPicUrl(), sku02.getPicUrl());
|
||||
assertEquals(tradeOrderItemDO02.getCount(), 4);
|
||||
assertEquals(tradeOrderItemDO02.getOriginalPrice(), 80);
|
||||
assertEquals(tradeOrderItemDO02.getOriginalUnitPrice(), 20);
|
||||
assertEquals(tradeOrderItemDO02.getDiscountPrice(), 40);
|
||||
assertEquals(tradeOrderItemDO02.getPayPrice(), 40);
|
||||
assertEquals(tradeOrderItemDO02.getOrderPartPrice(), 15);
|
||||
assertEquals(tradeOrderItemDO02.getOrderDividePrice(), 25);
|
||||
assertEquals(tradeOrderItemDO02.getAfterSaleStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
// 校验调用
|
||||
verify(productSkuApi).updateSkuStock(argThat(updateStockReqDTO -> {
|
||||
assertEquals(updateStockReqDTO.getItems().size(), 2);
|
||||
assertEquals(updateStockReqDTO.getItems().get(0).getId(), 1L);
|
||||
assertEquals(updateStockReqDTO.getItems().get(0).getIncrCount(), 3);
|
||||
assertEquals(updateStockReqDTO.getItems().get(1).getId(), 2L);
|
||||
assertEquals(updateStockReqDTO.getItems().get(1).getIncrCount(), 4);
|
||||
return true;
|
||||
}));
|
||||
verify(couponApi).useCoupon(argThat(reqDTO -> {
|
||||
assertEquals(reqDTO.getId(), reqVO.getCouponId());
|
||||
assertEquals(reqDTO.getUserId(), userId);
|
||||
assertEquals(reqDTO.getOrderId(), tradeOrderId);
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateOrderPaid() {
|
||||
// mock 数据(TradeOrder)
|
||||
TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> {
|
||||
o.setId(1L).setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
|
||||
o.setPayOrderId(10L).setPayed(false).setPayPrice(100).setPayTime(null);
|
||||
});
|
||||
tradeOrderMapper.insert(order);
|
||||
// 准备参数
|
||||
Long id = 1L;
|
||||
Long payOrderId = 10L;
|
||||
// mock 方法(支付单)
|
||||
when(payOrderApi.getOrder(eq(10L))).thenReturn(randomPojo(PayOrderRespDTO.class,
|
||||
o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()).setChannelCode("wx_pub")
|
||||
.setMerchantOrderId("1")).setAmount(100));
|
||||
|
||||
// 调用
|
||||
tradeOrderService.updateOrderPaid(id, payOrderId);
|
||||
// 断言
|
||||
TradeOrderDO dbOrder = tradeOrderMapper.selectById(id);
|
||||
assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
|
||||
assertTrue(dbOrder.getPayed());
|
||||
assertNotNull(dbOrder.getPayTime());
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveOrder() {
|
||||
// mock 数据(TradeOrder)
|
||||
TradeOrderDO order = randomPojo(TradeOrderDO.class, o -> {
|
||||
o.setId(1L).setUserId(10L).setStatus(TradeOrderStatusEnum.DELIVERED.getStatus());
|
||||
o.setDeliveryStatus(TradeOrderDeliveryStatusEnum.DELIVERED.getStatus()).setReceiveTime(null);
|
||||
});
|
||||
tradeOrderMapper.insert(order);
|
||||
// 准备参数
|
||||
Long id = 1L;
|
||||
Long userId = 10L;
|
||||
// mock 方法(支付单)
|
||||
|
||||
// 调用
|
||||
tradeOrderService.receiveOrder(userId, id);
|
||||
// 断言
|
||||
TradeOrderDO dbOrder = tradeOrderMapper.selectById(1L);
|
||||
assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
|
||||
assertEquals(dbOrder.getDeliveryStatus(), TradeOrderDeliveryStatusEnum.RECEIVED.getStatus());
|
||||
assertNotNull(dbOrder.getReceiveTime());
|
||||
}
|
||||
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
spring:
|
||||
main:
|
||||
lazy-initialization: true # 开启懒加载,加快速度
|
||||
banner-mode: off # 单元测试,禁用 Banner
|
||||
|
||||
--- #################### 数据库相关配置 ####################
|
||||
|
||||
spring:
|
||||
# 数据源配置项
|
||||
datasource:
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
|
||||
driver-class-name: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
druid:
|
||||
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
|
||||
initial-size: 1 # 单元测试,配置为 1,提升启动速度
|
||||
sql:
|
||||
init:
|
||||
schema-locations: classpath:/sql/create_tables.sql
|
||||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
redis:
|
||||
host: 127.0.0.1 # 地址
|
||||
port: 16379 # 端口(单元测试,使用 16379 端口)
|
||||
database: 0 # 数据库索引
|
||||
|
||||
mybatis:
|
||||
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
|
||||
--- #################### 配置中心相关配置 ####################
|
||||
|
||||
--- #################### 服务保障相关配置 ####################
|
||||
|
||||
# Lock4j 配置项(单元测试,禁用 Lock4j)
|
||||
|
||||
# Resilience4j 配置项
|
||||
|
||||
--- #################### 监控相关配置 ####################
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
info:
|
||||
base-package: cn.iocoder.yudao.module
|
||||
trade:
|
||||
order:
|
||||
app-id: 1
|
||||
merchant-order-id: 1
|
@@ -1,4 +0,0 @@
|
||||
<configuration>
|
||||
<!-- 引用 Spring Boot 的 logback 基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||
</configuration>
|
@@ -1,4 +0,0 @@
|
||||
DELETE FROM trade_order;
|
||||
DELETE FROM trade_order_item;
|
||||
DELETE FROM trade_after_sale;
|
||||
DELETE FROM trade_after_sale_log;
|
@@ -1,128 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS "trade_order" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"no" varchar NOT NULL,
|
||||
"type" int NOT NULL,
|
||||
"terminal" int NOT NULL,
|
||||
"user_id" bigint NOT NULL,
|
||||
"user_ip" varchar NOT NULL,
|
||||
"user_remark" varchar,
|
||||
"status" int NOT NULL,
|
||||
"product_count" int NOT NULL,
|
||||
"cancel_type" int,
|
||||
"remark" varchar,
|
||||
"payed" bit NOT NULL,
|
||||
"pay_time" datetime,
|
||||
"finish_time" datetime,
|
||||
"cancel_time" datetime,
|
||||
"original_price" int NOT NULL,
|
||||
"order_price" int NOT NULL,
|
||||
"discount_price" int NOT NULL,
|
||||
"delivery_price" int NOT NULL,
|
||||
"adjust_price" int NOT NULL,
|
||||
"pay_price" int NOT NULL,
|
||||
"pay_order_id" bigint,
|
||||
"pay_channel_code" varchar,
|
||||
"delivery_template_id" bigint,
|
||||
"logistics_id" bigint,
|
||||
"logistics_no" varchar,
|
||||
"delivery_status" smallint NOT NULL,
|
||||
"delivery_time" datetime,
|
||||
"receive_time" datetime,
|
||||
"receiver_name" varchar NOT NULL,
|
||||
"receiver_mobile" varchar NOT NULL,
|
||||
"receiver_area_id" int NOT NULL,
|
||||
"receiver_post_code" int,
|
||||
"receiver_detail_address" varchar NOT NULL,
|
||||
"after_sale_status" int NOT NULL,
|
||||
"refund_price" int NOT NULL,
|
||||
"coupon_id" bigint NOT NULL,
|
||||
"coupon_price" int NOT NULL,
|
||||
"point_price" int NOT NULL,
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '交易订单表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "trade_order_item" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"user_id" bigint NOT NULL,
|
||||
"order_id" bigint NOT NULL,
|
||||
"spu_id" bigint NOT NULL,
|
||||
"spu_name" varchar NOT NULL,
|
||||
"sku_id" bigint NOT NULL,
|
||||
"properties" varchar,
|
||||
"pic_url" varchar,
|
||||
"count" int NOT NULL,
|
||||
"original_price" int NOT NULL,
|
||||
"original_unit_price" int NOT NULL,
|
||||
"discount_price" int NOT NULL,
|
||||
"pay_price" int NOT NULL,
|
||||
"order_part_price" int NOT NULL,
|
||||
"order_divide_price" int NOT NULL,
|
||||
"after_sale_status" int NOT NULL,
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '交易订单明细表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "trade_after_sale" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"no" varchar NOT NULL,
|
||||
"status" int NOT NULL,
|
||||
"type" int NOT NULL,
|
||||
"way" int NOT NULL,
|
||||
"user_id" bigint NOT NULL,
|
||||
"apply_reason" varchar NOT NULL,
|
||||
"apply_description" varchar,
|
||||
"apply_pic_urls" varchar,
|
||||
"order_id" bigint NOT NULL,
|
||||
"order_no" varchar NOT NULL,
|
||||
"order_item_id" bigint NOT NULL,
|
||||
"spu_id" bigint NOT NULL,
|
||||
"spu_name" varchar NOT NULL,
|
||||
"sku_id" bigint NOT NULL,
|
||||
"properties" varchar,
|
||||
"pic_url" varchar,
|
||||
"count" int NOT NULL,
|
||||
"audit_time" varchar,
|
||||
"audit_user_id" bigint,
|
||||
"audit_reason" varchar,
|
||||
"refund_price" int NOT NULL,
|
||||
"pay_refund_id" bigint,
|
||||
"refund_time" varchar,
|
||||
"logistics_id" bigint,
|
||||
"logistics_no" varchar,
|
||||
"delivery_time" varchar,
|
||||
"receive_time" varchar,
|
||||
"receive_reason" varchar,
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '交易售后表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "trade_after_sale_log" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"user_id" bigint NOT NULL,
|
||||
"user_type" int NOT NULL,
|
||||
"after_sale_id" bigint NOT NULL,
|
||||
"order_id" bigint NOT NULL,
|
||||
"order_item_id" bigint NOT NULL,
|
||||
"before_status" int,
|
||||
"after_status" int NOT NULL,
|
||||
"content" varchar NOT NULL,
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '交易售后日志';
|
Reference in New Issue
Block a user