feat(payment): 初始化支付模块核心代码
- 添加支付常量类PaymentConstants,定义支付状态、微信、支付宝、银联等相关常量 - 创建微信支付类型常量类WechatPayType,支持JSAPI、NATIVE、H5、APP支付方式 - 新增支付控制器PaymentController,提供创建支付、查询状态、退款等统一接口 - 实现支付回调控制器PaymentNotifyController,处理微信、支付宝、银联异步通知 - 添加支付请求数据传输对象PaymentRequest,支持多种支付方式参数校验 - 定义支付响应、状态更新请求等相关DTO类- 集成Swagger注解,完善接口文档说明- 添加参数校验和异常处理机制,确保支付流程安全可靠
This commit is contained in:
207
java/payment/dto/PaymentRequest.java
Normal file
207
java/payment/dto/PaymentRequest.java
Normal file
@@ -0,0 +1,207 @@
|
||||
package com.gxwebsoft.payment.dto;
|
||||
|
||||
import com.gxwebsoft.payment.enums.PaymentChannel;
|
||||
import com.gxwebsoft.payment.enums.PaymentType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 统一支付请求DTO
|
||||
* 支持所有支付方式的统一请求格式
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2025-01-26
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "统一支付请求", description = "支持所有支付方式的统一支付请求参数")
|
||||
public class PaymentRequest {
|
||||
|
||||
@Schema(description = "租户ID", required = true)
|
||||
@NotNull(message = "租户ID不能为空")
|
||||
@Positive(message = "租户ID必须为正数")
|
||||
private Integer tenantId;
|
||||
|
||||
@Schema(description = "用户ID", required = true)
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
@Positive(message = "用户ID必须为正数")
|
||||
private Integer userId;
|
||||
|
||||
@Schema(description = "支付类型", required = true, example = "WECHAT")
|
||||
@NotNull(message = "支付类型不能为空")
|
||||
private PaymentType paymentType;
|
||||
|
||||
@Schema(description = "支付渠道", example = "wechat_native")
|
||||
private PaymentChannel paymentChannel;
|
||||
|
||||
@Schema(description = "支付金额", required = true, example = "0.01")
|
||||
@NotNull(message = "支付金额不能为空")
|
||||
@DecimalMin(value = "0.01", message = "支付金额必须大于0.01元")
|
||||
@DecimalMax(value = "999999.99", message = "支付金额不能超过999999.99元")
|
||||
@Digits(integer = 6, fraction = 2, message = "支付金额格式不正确,最多6位整数2位小数")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "订单号(可选,不提供则自动生成)")
|
||||
@Size(max = 32, message = "订单号不能超过32个字符")
|
||||
@Pattern(regexp = "^[a-zA-Z0-9_-]*$", message = "订单号只能包含字母、数字、下划线和横线")
|
||||
private String orderNo;
|
||||
|
||||
@Schema(description = "订单标题", required = true)
|
||||
@NotBlank(message = "订单标题不能为空")
|
||||
@Size(max = 127, message = "订单标题不能超过127个字符")
|
||||
private String subject;
|
||||
|
||||
@Schema(description = "订单描述")
|
||||
@Size(max = 500, message = "订单描述不能超过500个字符")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "商品ID")
|
||||
@Positive(message = "商品ID必须为正数")
|
||||
private Integer goodsId;
|
||||
|
||||
@Schema(description = "购买数量", example = "1")
|
||||
@Min(value = 1, message = "购买数量必须大于0")
|
||||
@Max(value = 9999, message = "购买数量不能超过9999")
|
||||
private Integer quantity = 1;
|
||||
|
||||
@Schema(description = "订单类型", example = "0")
|
||||
@Min(value = 0, message = "订单类型不能为负数")
|
||||
private Integer orderType = 0;
|
||||
|
||||
@Schema(description = "客户端IP地址")
|
||||
private String clientIp;
|
||||
|
||||
@Schema(description = "用户代理")
|
||||
private String userAgent;
|
||||
|
||||
@Schema(description = "回调通知URL")
|
||||
private String notifyUrl;
|
||||
|
||||
@Schema(description = "支付成功跳转URL")
|
||||
private String returnUrl;
|
||||
|
||||
@Schema(description = "支付取消跳转URL")
|
||||
private String cancelUrl;
|
||||
|
||||
@Schema(description = "订单超时时间(分钟)", example = "30")
|
||||
@Min(value = 1, message = "订单超时时间必须大于0分钟")
|
||||
@Max(value = 1440, message = "订单超时时间不能超过1440分钟(24小时)")
|
||||
private Integer timeoutMinutes = 30;
|
||||
|
||||
@Schema(description = "买家备注")
|
||||
@Size(max = 500, message = "买家备注不能超过500个字符")
|
||||
private String buyerRemarks;
|
||||
|
||||
@Schema(description = "商户备注")
|
||||
@Size(max = 500, message = "商户备注不能超过500个字符")
|
||||
private String merchantRemarks;
|
||||
|
||||
@Schema(description = "收货地址ID")
|
||||
@Positive(message = "收货地址ID必须为正数")
|
||||
private Integer addressId;
|
||||
|
||||
@Schema(description = "扩展参数")
|
||||
private Map<String, Object> extraParams;
|
||||
|
||||
// 微信支付特有参数
|
||||
@Schema(description = "微信OpenID(JSAPI支付必填)")
|
||||
private String openId;
|
||||
|
||||
@Schema(description = "微信UnionID")
|
||||
private String unionId;
|
||||
|
||||
// 支付宝特有参数
|
||||
@Schema(description = "支付宝用户ID")
|
||||
private String alipayUserId;
|
||||
|
||||
@Schema(description = "花呗分期数")
|
||||
private Integer hbFqNum;
|
||||
|
||||
// 银联支付特有参数
|
||||
@Schema(description = "银行卡号")
|
||||
private String cardNo;
|
||||
|
||||
@Schema(description = "银行代码")
|
||||
private String bankCode;
|
||||
|
||||
/**
|
||||
* 获取有效的支付渠道
|
||||
*/
|
||||
public PaymentChannel getEffectivePaymentChannel() {
|
||||
if (paymentChannel != null) {
|
||||
return paymentChannel;
|
||||
}
|
||||
return PaymentChannel.getDefaultByPaymentType(paymentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有效的订单描述
|
||||
*/
|
||||
public String getEffectiveDescription() {
|
||||
if (description != null && !description.trim().isEmpty()) {
|
||||
return description.trim();
|
||||
}
|
||||
return subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格式化的金额字符串
|
||||
*/
|
||||
public String getFormattedAmount() {
|
||||
if (amount == null) {
|
||||
return "0.00";
|
||||
}
|
||||
return String.format("%.2f", amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为分(微信支付API需要)
|
||||
*/
|
||||
public Integer getAmountInCents() {
|
||||
if (amount == null) {
|
||||
return 0;
|
||||
}
|
||||
return amount.multiply(new BigDecimal(100)).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证必要参数是否完整
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return tenantId != null && tenantId > 0
|
||||
&& userId != null && userId > 0
|
||||
&& paymentType != null
|
||||
&& amount != null && amount.compareTo(BigDecimal.ZERO) > 0
|
||||
&& subject != null && !subject.trim().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证微信JSAPI支付参数
|
||||
*/
|
||||
public boolean isValidForWechatJsapi() {
|
||||
return isValid() && paymentType.isWechatPay() && openId != null && !openId.trim().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证支付宝支付参数
|
||||
*/
|
||||
public boolean isValidForAlipay() {
|
||||
return isValid() && paymentType == PaymentType.ALIPAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单超时时间(秒)
|
||||
*/
|
||||
public long getTimeoutSeconds() {
|
||||
return timeoutMinutes * 60L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PaymentRequest{tenantId=%d, userId=%d, paymentType=%s, amount=%s, orderNo='%s', subject='%s'}",
|
||||
tenantId, userId, paymentType, getFormattedAmount(), orderNo, subject);
|
||||
}
|
||||
}
|
||||
294
java/payment/dto/PaymentResponse.java
Normal file
294
java/payment/dto/PaymentResponse.java
Normal file
@@ -0,0 +1,294 @@
|
||||
package com.gxwebsoft.payment.dto;
|
||||
|
||||
import com.gxwebsoft.payment.enums.PaymentChannel;
|
||||
import com.gxwebsoft.payment.enums.PaymentStatus;
|
||||
import com.gxwebsoft.payment.enums.PaymentType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 统一支付响应DTO
|
||||
* 支持所有支付方式的统一响应格式
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2025-01-26
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "统一支付响应", description = "支持所有支付方式的统一支付响应")
|
||||
public class PaymentResponse {
|
||||
|
||||
@Schema(description = "是否成功")
|
||||
private Boolean success;
|
||||
|
||||
@Schema(description = "错误代码")
|
||||
private String errorCode;
|
||||
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMessage;
|
||||
|
||||
@Schema(description = "订单号")
|
||||
private String orderNo;
|
||||
|
||||
@Schema(description = "第三方交易号")
|
||||
private String transactionId;
|
||||
|
||||
@Schema(description = "支付类型")
|
||||
private PaymentType paymentType;
|
||||
|
||||
@Schema(description = "支付渠道")
|
||||
private PaymentChannel paymentChannel;
|
||||
|
||||
@Schema(description = "支付状态")
|
||||
private PaymentStatus paymentStatus;
|
||||
|
||||
@Schema(description = "支付金额")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "实际支付金额")
|
||||
private BigDecimal paidAmount;
|
||||
|
||||
@Schema(description = "货币类型")
|
||||
private String currency;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
private Integer tenantId;
|
||||
|
||||
@Schema(description = "用户ID")
|
||||
private Integer userId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "支付时间")
|
||||
private LocalDateTime payTime;
|
||||
|
||||
@Schema(description = "过期时间")
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
// 微信支付特有字段
|
||||
@Schema(description = "微信支付二维码URL(Native支付)")
|
||||
private String codeUrl;
|
||||
|
||||
@Schema(description = "微信支付参数(JSAPI支付)")
|
||||
private WechatPayParams wechatPayParams;
|
||||
|
||||
@Schema(description = "微信H5支付URL")
|
||||
private String h5Url;
|
||||
|
||||
// 支付宝特有字段
|
||||
@Schema(description = "支付宝支付表单(网页支付)")
|
||||
private String alipayForm;
|
||||
|
||||
@Schema(description = "支付宝支付URL(手机网站支付)")
|
||||
private String alipayUrl;
|
||||
|
||||
@Schema(description = "支付宝支付参数(APP支付)")
|
||||
private String alipayParams;
|
||||
|
||||
// 银联支付特有字段
|
||||
@Schema(description = "银联支付表单")
|
||||
private String unionPayForm;
|
||||
|
||||
@Schema(description = "银联支付URL")
|
||||
private String unionPayUrl;
|
||||
|
||||
@Schema(description = "扩展参数")
|
||||
private Map<String, Object> extraParams;
|
||||
|
||||
/**
|
||||
* 微信支付参数
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "微信支付参数", description = "微信JSAPI支付所需参数")
|
||||
public static class WechatPayParams {
|
||||
@Schema(description = "应用ID")
|
||||
private String appId;
|
||||
|
||||
@Schema(description = "时间戳")
|
||||
private String timeStamp;
|
||||
|
||||
@Schema(description = "随机字符串")
|
||||
private String nonceStr;
|
||||
|
||||
@Schema(description = "订单详情扩展字符串")
|
||||
private String packageValue;
|
||||
|
||||
@Schema(description = "签名方式")
|
||||
private String signType;
|
||||
|
||||
@Schema(description = "签名")
|
||||
private String paySign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建成功响应
|
||||
*/
|
||||
public static PaymentResponse success(String orderNo, PaymentType paymentType) {
|
||||
PaymentResponse response = new PaymentResponse();
|
||||
response.setSuccess(true);
|
||||
response.setOrderNo(orderNo);
|
||||
response.setPaymentType(paymentType);
|
||||
response.setPaymentStatus(PaymentStatus.PENDING);
|
||||
response.setCreateTime(LocalDateTime.now());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败响应
|
||||
*/
|
||||
public static PaymentResponse failure(String errorCode, String errorMessage) {
|
||||
PaymentResponse response = new PaymentResponse();
|
||||
response.setSuccess(false);
|
||||
response.setErrorCode(errorCode);
|
||||
response.setErrorMessage(errorMessage);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建微信Native支付响应
|
||||
*/
|
||||
public static PaymentResponse wechatNative(String orderNo, String codeUrl, BigDecimal amount, Integer tenantId) {
|
||||
PaymentResponse response = success(orderNo, PaymentType.WECHAT_NATIVE);
|
||||
response.setCodeUrl(codeUrl);
|
||||
response.setPaymentChannel(PaymentChannel.WECHAT_NATIVE);
|
||||
response.setAmount(amount);
|
||||
response.setTenantId(tenantId);
|
||||
response.setCurrency("CNY");
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建微信JSAPI支付响应
|
||||
*/
|
||||
public static PaymentResponse wechatJsapi(String orderNo, WechatPayParams payParams, BigDecimal amount, Integer tenantId) {
|
||||
PaymentResponse response = success(orderNo, PaymentType.WECHAT);
|
||||
response.setWechatPayParams(payParams);
|
||||
response.setPaymentChannel(PaymentChannel.WECHAT_JSAPI);
|
||||
response.setAmount(amount);
|
||||
response.setTenantId(tenantId);
|
||||
response.setCurrency("CNY");
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建微信H5支付响应
|
||||
*/
|
||||
public static PaymentResponse wechatH5(String orderNo, String h5Url, BigDecimal amount, Integer tenantId) {
|
||||
PaymentResponse response = success(orderNo, PaymentType.WECHAT);
|
||||
response.setH5Url(h5Url);
|
||||
response.setPaymentChannel(PaymentChannel.WECHAT_H5);
|
||||
response.setAmount(amount);
|
||||
response.setTenantId(tenantId);
|
||||
response.setCurrency("CNY");
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建支付宝网页支付响应
|
||||
*/
|
||||
public static PaymentResponse alipayWeb(String orderNo, String alipayForm, BigDecimal amount, Integer tenantId) {
|
||||
PaymentResponse response = success(orderNo, PaymentType.ALIPAY);
|
||||
response.setAlipayForm(alipayForm);
|
||||
response.setPaymentChannel(PaymentChannel.ALIPAY_WEB);
|
||||
response.setAmount(amount);
|
||||
response.setTenantId(tenantId);
|
||||
response.setCurrency("CNY");
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建支付宝手机网站支付响应
|
||||
*/
|
||||
public static PaymentResponse alipayWap(String orderNo, String alipayUrl, BigDecimal amount, Integer tenantId) {
|
||||
PaymentResponse response = success(orderNo, PaymentType.ALIPAY);
|
||||
response.setAlipayUrl(alipayUrl);
|
||||
response.setPaymentChannel(PaymentChannel.ALIPAY_WAP);
|
||||
response.setAmount(amount);
|
||||
response.setTenantId(tenantId);
|
||||
response.setCurrency("CNY");
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建支付宝APP支付响应
|
||||
*/
|
||||
public static PaymentResponse alipayApp(String orderNo, String alipayParams, BigDecimal amount, Integer tenantId) {
|
||||
PaymentResponse response = success(orderNo, PaymentType.ALIPAY);
|
||||
response.setAlipayParams(alipayParams);
|
||||
response.setPaymentChannel(PaymentChannel.ALIPAY_APP);
|
||||
response.setAmount(amount);
|
||||
response.setTenantId(tenantId);
|
||||
response.setCurrency("CNY");
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建余额支付响应
|
||||
*/
|
||||
public static PaymentResponse balance(String orderNo, BigDecimal amount, Integer tenantId, Integer userId) {
|
||||
PaymentResponse response = success(orderNo, PaymentType.BALANCE);
|
||||
response.setPaymentChannel(PaymentChannel.BALANCE);
|
||||
response.setPaymentStatus(PaymentStatus.SUCCESS);
|
||||
response.setAmount(amount);
|
||||
response.setPaidAmount(amount);
|
||||
response.setTenantId(tenantId);
|
||||
response.setUserId(userId);
|
||||
response.setCurrency("CNY");
|
||||
response.setPayTime(LocalDateTime.now());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为成功响应
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return Boolean.TRUE.equals(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要用户进一步操作
|
||||
*/
|
||||
public boolean needUserAction() {
|
||||
return isSuccess() && paymentStatus == PaymentStatus.PENDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付结果描述
|
||||
*/
|
||||
public String getResultDescription() {
|
||||
if (!isSuccess()) {
|
||||
return errorMessage != null ? errorMessage : "支付失败";
|
||||
}
|
||||
|
||||
if (paymentStatus == null) {
|
||||
return "支付状态未知";
|
||||
}
|
||||
|
||||
switch (paymentStatus) {
|
||||
case SUCCESS:
|
||||
return "支付成功";
|
||||
case PENDING:
|
||||
return "等待支付";
|
||||
case PROCESSING:
|
||||
return "支付处理中";
|
||||
case FAILED:
|
||||
return "支付失败";
|
||||
case CANCELLED:
|
||||
return "支付已取消";
|
||||
case TIMEOUT:
|
||||
return "支付超时";
|
||||
default:
|
||||
return paymentStatus.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PaymentResponse{success=%s, orderNo='%s', paymentType=%s, paymentStatus=%s, amount=%s}",
|
||||
success, orderNo, paymentType, paymentStatus, amount);
|
||||
}
|
||||
}
|
||||
41
java/payment/dto/PaymentStatusUpdateRequest.java
Normal file
41
java/payment/dto/PaymentStatusUpdateRequest.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package com.gxwebsoft.payment.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Positive;
|
||||
|
||||
/**
|
||||
* 支付状态更新请求DTO
|
||||
* 用于手动更新支付状态的请求参数
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2025-01-26
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "支付状态更新请求", description = "用于手动更新支付状态")
|
||||
public class PaymentStatusUpdateRequest {
|
||||
|
||||
@Schema(description = "订单号", required = true, example = "ORDER_1756544921075")
|
||||
@NotBlank(message = "订单号不能为空")
|
||||
private String orderNo;
|
||||
|
||||
@Schema(description = "租户ID", required = true, example = "10398")
|
||||
@NotNull(message = "租户ID不能为空")
|
||||
@Positive(message = "租户ID必须为正数")
|
||||
private Integer tenantId;
|
||||
|
||||
@Schema(description = "第三方交易号", example = "4200001234567890123")
|
||||
private String transactionId;
|
||||
|
||||
@Schema(description = "支付时间", example = "2025-01-26T10:30:00")
|
||||
private String payTime;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PaymentStatusUpdateRequest{orderNo='%s', tenantId=%d, transactionId='%s', payTime='%s'}",
|
||||
orderNo, tenantId, transactionId, payTime);
|
||||
}
|
||||
}
|
||||
158
java/payment/dto/PaymentWithOrderRequest.java
Normal file
158
java/payment/dto/PaymentWithOrderRequest.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package com.gxwebsoft.payment.dto;
|
||||
|
||||
import com.gxwebsoft.payment.enums.PaymentType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 支付与订单创建请求DTO
|
||||
* 用于统一支付模块中的订单创建和支付
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2025-01-26
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "PaymentWithOrderRequest", description = "支付与订单创建请求")
|
||||
public class PaymentWithOrderRequest {
|
||||
|
||||
// ========== 支付相关字段 ==========
|
||||
|
||||
@Schema(description = "支付类型", required = true)
|
||||
@NotNull(message = "支付类型不能为空")
|
||||
private PaymentType paymentType;
|
||||
|
||||
@Schema(description = "支付金额", required = true)
|
||||
@NotNull(message = "支付金额不能为空")
|
||||
@DecimalMin(value = "0.01", message = "支付金额必须大于0")
|
||||
@Digits(integer = 10, fraction = 2, message = "支付金额格式不正确")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "订单标题", required = true)
|
||||
@NotBlank(message = "订单标题不能为空")
|
||||
@Size(max = 60, message = "订单标题长度不能超过60个字符")
|
||||
private String subject;
|
||||
|
||||
@Schema(description = "订单描述")
|
||||
@Size(max = 500, message = "订单描述长度不能超过500个字符")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "租户ID", required = true)
|
||||
@NotNull(message = "租户ID不能为空")
|
||||
@Positive(message = "租户ID必须为正数")
|
||||
private Integer tenantId;
|
||||
|
||||
// ========== 订单相关字段 ==========
|
||||
|
||||
@Schema(description = "订单信息", required = true)
|
||||
@Valid
|
||||
@NotNull(message = "订单信息不能为空")
|
||||
private OrderInfo orderInfo;
|
||||
|
||||
/**
|
||||
* 订单信息
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "OrderInfo", description = "订单信息")
|
||||
public static class OrderInfo {
|
||||
|
||||
@Schema(description = "订单类型,0商城订单 1预定订单/外卖 2会员卡")
|
||||
@NotNull(message = "订单类型不能为空")
|
||||
@Min(value = 0, message = "订单类型值无效")
|
||||
@Max(value = 2, message = "订单类型值无效")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "收货人姓名")
|
||||
@Size(max = 50, message = "收货人姓名长度不能超过50个字符")
|
||||
private String realName;
|
||||
|
||||
@Schema(description = "收货地址")
|
||||
@Size(max = 200, message = "收货地址长度不能超过200个字符")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "关联收货地址ID")
|
||||
private Integer addressId;
|
||||
|
||||
@Schema(description = "快递/自提,0快递 1自提")
|
||||
private Integer deliveryType;
|
||||
|
||||
@Schema(description = "下单渠道,0小程序预定 1俱乐部训练场 3活动订场")
|
||||
private Integer channel;
|
||||
|
||||
@Schema(description = "商户ID")
|
||||
private Long merchantId;
|
||||
|
||||
@Schema(description = "商户名称")
|
||||
private String merchantName;
|
||||
|
||||
@Schema(description = "使用的优惠券ID")
|
||||
private Integer couponId;
|
||||
|
||||
@Schema(description = "备注")
|
||||
@Size(max = 500, message = "备注长度不能超过500字符")
|
||||
private String comments;
|
||||
|
||||
@Schema(description = "订单商品列表", required = true)
|
||||
@Valid
|
||||
@NotEmpty(message = "订单商品列表不能为空")
|
||||
private List<OrderGoodsItem> goodsItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单商品项
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "OrderGoodsItem", description = "订单商品项")
|
||||
public static class OrderGoodsItem {
|
||||
|
||||
@Schema(description = "商品ID", required = true)
|
||||
@NotNull(message = "商品ID不能为空")
|
||||
@Positive(message = "商品ID必须为正数")
|
||||
private Integer goodsId;
|
||||
|
||||
@Schema(description = "商品SKU ID")
|
||||
private Integer skuId;
|
||||
|
||||
@Schema(description = "商品数量", required = true)
|
||||
@NotNull(message = "商品数量不能为空")
|
||||
@Min(value = 1, message = "商品数量必须大于0")
|
||||
private Integer quantity;
|
||||
|
||||
@Schema(description = "规格信息,如:颜色:红色|尺寸:L")
|
||||
private String specInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格式化的金额字符串
|
||||
*/
|
||||
public String getFormattedAmount() {
|
||||
if (amount == null) {
|
||||
return "0.00";
|
||||
}
|
||||
return String.format("%.2f", amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证订单商品总金额是否与支付金额一致
|
||||
*/
|
||||
public boolean isAmountConsistent() {
|
||||
if (amount == null || orderInfo == null || orderInfo.getGoodsItems() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 这里可以添加商品金额计算逻辑
|
||||
// 实际实现时需要查询数据库获取商品价格
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PaymentWithOrderRequest{paymentType=%s, amount=%s, subject='%s', tenantId=%d, goodsCount=%d}",
|
||||
paymentType, getFormattedAmount(), subject, tenantId,
|
||||
orderInfo != null && orderInfo.getGoodsItems() != null ? orderInfo.getGoodsItems().size() : 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user