mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-12 21:02:08 +08:00
1、软件购买接入新的渠道code基于二进制的解析;2、账户明细、销售管理、软件购买列表查询和回调数据调整;
This commit is contained in:
+27
-1
@@ -1,15 +1,19 @@
|
|||||||
package com.cf.imes.module.system.controller.admin.funds.balancedetails.vo;
|
package com.cf.imes.module.system.controller.admin.funds.balancedetails.vo;
|
||||||
|
|
||||||
|
|
||||||
import com.cf.imes.framework.common.pojo.PageParam;
|
import com.cf.imes.framework.common.pojo.PageParam;
|
||||||
|
import com.cf.imes.module.system.validation.pay.IncomeExpenseTypeEnumValid;
|
||||||
import com.cf.imes.module.system.validation.pay.TradeTypeEnumValid;
|
import com.cf.imes.module.system.validation.pay.TradeTypeEnumValid;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.DecimalMax;
|
||||||
|
import jakarta.validation.constraints.DecimalMin;
|
||||||
|
import jakarta.validation.constraints.Digits;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
|
||||||
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||||
@@ -34,10 +38,32 @@ public class BalanceDetailsPageReqVO extends PageParam {
|
|||||||
@TradeTypeEnumValid
|
@TradeTypeEnumValid
|
||||||
private Integer tradeType;
|
private Integer tradeType;
|
||||||
|
|
||||||
|
@Schema(description = "收支类型,0收入 1支出")
|
||||||
|
@IncomeExpenseTypeEnumValid
|
||||||
|
private Integer incomeExpenseType;
|
||||||
|
|
||||||
@Schema(description = "创建时间", example = "[2022-07-01 ,2022-07-01]")
|
@Schema(description = "创建时间", example = "[2022-07-01 ,2022-07-01]")
|
||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||||
private LocalDate[] createTime;
|
private LocalDate[] createTime;
|
||||||
|
|
||||||
|
@Schema(description = "充值金额")
|
||||||
|
@DecimalMin(value = "0.01", message = "充值金额必须大于零")
|
||||||
|
@DecimalMax(value = "100000", message = "充值金额不能超过十万")
|
||||||
|
@Digits(integer = 6, fraction = 2, message = "支付金额格式不正确")
|
||||||
|
private BigDecimal rechargeAmount;
|
||||||
|
|
||||||
|
@Schema(description = "赠送金额")
|
||||||
|
@DecimalMin(value = "0", message = "赠送金额不能小于零")
|
||||||
|
@DecimalMax(value = "100000", message = "赠送金额不能超过十万")
|
||||||
|
@Digits(integer = 6, fraction = 2, message = "赠送金额格式不正确")
|
||||||
|
private BigDecimal giftAmount;
|
||||||
|
|
||||||
|
@Schema(description = "产品金额")
|
||||||
|
@DecimalMin(value = "0.01", message = "产品购买金额必须大于零")
|
||||||
|
@DecimalMax(value = "100000", message = "产品购买金额不能超过十万")
|
||||||
|
@Digits(integer = 6, fraction = 2, message = "产品购买金额格式不正确")
|
||||||
|
private BigDecimal productPrice;
|
||||||
|
|
||||||
@Schema(description = "组织ID")
|
@Schema(description = "组织ID")
|
||||||
private Long organId;
|
private Long organId;
|
||||||
|
|
||||||
|
|||||||
+9
-6
@@ -41,20 +41,20 @@ public class BalanceDetailsRespVO {
|
|||||||
@ExcelProperty("充值金额")
|
@ExcelProperty("充值金额")
|
||||||
private BigDecimal rechargeAmount;
|
private BigDecimal rechargeAmount;
|
||||||
|
|
||||||
@Schema(description = "充值赠送金金额")
|
@Schema(description = "赠送金金额")
|
||||||
@ExcelProperty("充值赠送金金额")
|
@ExcelProperty("赠送金金额")
|
||||||
private BigDecimal rechargeGiftAmount;
|
private BigDecimal rechargeGiftAmount;
|
||||||
|
|
||||||
@Schema(description = "产品金额")
|
@Schema(description = "产品金额")
|
||||||
@ExcelProperty("产品金额")
|
@ExcelProperty("产品金额")
|
||||||
private BigDecimal productPrice;
|
private BigDecimal productPrice;
|
||||||
|
|
||||||
@Schema(description = "账户充值支付")
|
@Schema(description = "账户余额支付")
|
||||||
@ExcelProperty("账户充值支付")
|
@ExcelProperty("账户余额支付")
|
||||||
private BigDecimal balancePayAmount;
|
private BigDecimal balancePayAmount;
|
||||||
|
|
||||||
@Schema(description = "支付宝和微信支付")
|
@Schema(description = "扫码支付")
|
||||||
@ExcelProperty("支付宝和微信支付")
|
@ExcelProperty("扫码支付")
|
||||||
private BigDecimal rechargePayAmount;
|
private BigDecimal rechargePayAmount;
|
||||||
|
|
||||||
@Schema(description = "账户赠送金支付")
|
@Schema(description = "账户赠送金支付")
|
||||||
@@ -68,4 +68,7 @@ public class BalanceDetailsRespVO {
|
|||||||
@Schema(description = "操作人")
|
@Schema(description = "操作人")
|
||||||
@ExcelProperty("操作人")
|
@ExcelProperty("操作人")
|
||||||
private String creator;
|
private String creator;
|
||||||
|
|
||||||
|
@Schema(description = "支付方式")
|
||||||
|
private String paymentMethod;
|
||||||
}
|
}
|
||||||
|
|||||||
-3
@@ -3,7 +3,6 @@ package com.cf.imes.module.system.controller.admin.funds.purchase.vo;
|
|||||||
import com.alibaba.excel.annotation.ExcelProperty;
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
import com.cf.imes.framework.excel.core.annotations.EnumFormat;
|
import com.cf.imes.framework.excel.core.annotations.EnumFormat;
|
||||||
import com.cf.imes.framework.excel.core.convert.EnumConvert;
|
import com.cf.imes.framework.excel.core.convert.EnumConvert;
|
||||||
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -42,8 +41,6 @@ public class ManagePurchaseRecordRespVO {
|
|||||||
private Integer giftDurationUnit;
|
private Integer giftDurationUnit;
|
||||||
|
|
||||||
@Schema(description = "支付方式")
|
@Schema(description = "支付方式")
|
||||||
@ExcelProperty(value = "支付方式", converter = EnumConvert.class)
|
|
||||||
@EnumFormat(value = PayProductChannelCodeEnum.class)
|
|
||||||
private Integer paymentMethod;
|
private Integer paymentMethod;
|
||||||
|
|
||||||
@Schema(description = "订单总额")
|
@Schema(description = "订单总额")
|
||||||
|
|||||||
+17
-2
@@ -3,14 +3,20 @@ package com.cf.imes.module.system.controller.admin.funds.purchase.vo;
|
|||||||
|
|
||||||
import com.cf.imes.framework.common.pojo.PageParam;
|
import com.cf.imes.framework.common.pojo.PageParam;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.DecimalMax;
|
||||||
|
import jakarta.validation.constraints.DecimalMin;
|
||||||
|
import jakarta.validation.constraints.Digits;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||||
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 组织购买记录明细 Request VO")
|
@Schema(description = "管理后台 - 组织购买记录明细 Request VO")
|
||||||
@@ -41,7 +47,16 @@ public class PurchaseRecordPageReqVO extends PageParam {
|
|||||||
private String creator;
|
private String creator;
|
||||||
|
|
||||||
@Schema(description = "订单号")
|
@Schema(description = "订单号")
|
||||||
@Size(max = 64, message = "订单号长度不能超过64个字符")
|
@Size(max = 20, message = "订单号长度不能超过20个字符")
|
||||||
private String orderNo;
|
private String id;
|
||||||
|
|
||||||
|
@Schema(description = "有效时间", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||||
|
private LocalDate[] endTime;
|
||||||
|
|
||||||
|
@Schema(description = "产品金额")
|
||||||
|
@DecimalMin(value = "0.01", message = "产品购买金额必须大于零")
|
||||||
|
@DecimalMax(value = "100000", message = "产品购买金额不能超过十万")
|
||||||
|
@Digits(integer = 6, fraction = 2, message = "产品购买金额格式不正确")
|
||||||
|
private BigDecimal productPrice;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-12
@@ -5,7 +5,6 @@ import com.alibaba.excel.annotation.ExcelProperty;
|
|||||||
import com.cf.imes.framework.excel.core.annotations.EnumFormat;
|
import com.cf.imes.framework.excel.core.annotations.EnumFormat;
|
||||||
import com.cf.imes.framework.excel.core.convert.EnumConvert;
|
import com.cf.imes.framework.excel.core.convert.EnumConvert;
|
||||||
import com.cf.imes.framework.jackson.core.databind.LocalDateSerializer;
|
import com.cf.imes.framework.jackson.core.databind.LocalDateSerializer;
|
||||||
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
@@ -59,9 +58,7 @@ public class PurchaseRecordRespVO {
|
|||||||
private Integer giftDurationUnit;
|
private Integer giftDurationUnit;
|
||||||
|
|
||||||
@Schema(description = "支付方式")
|
@Schema(description = "支付方式")
|
||||||
@ExcelProperty(value = "支付方式", converter = EnumConvert.class)
|
private String paymentMethod;
|
||||||
@EnumFormat(value = PayProductChannelCodeEnum.class)
|
|
||||||
private Integer paymentMethod;
|
|
||||||
|
|
||||||
@Schema(description = "订单总额")
|
@Schema(description = "订单总额")
|
||||||
@ExcelProperty("订单总额")
|
@ExcelProperty("订单总额")
|
||||||
@@ -71,18 +68,14 @@ public class PurchaseRecordRespVO {
|
|||||||
@ExcelProperty("现金余额支出")
|
@ExcelProperty("现金余额支出")
|
||||||
private BigDecimal accountBalanceSpent;
|
private BigDecimal accountBalanceSpent;
|
||||||
|
|
||||||
@Schema(description = "支付宝支出")
|
|
||||||
@ExcelProperty("支付宝支出")
|
|
||||||
private BigDecimal alipaySpent;
|
|
||||||
|
|
||||||
@Schema(description = "微信支出")
|
|
||||||
@ExcelProperty("微信支出")
|
|
||||||
private BigDecimal wechatSpent;
|
|
||||||
|
|
||||||
@Schema(description = "赠送金")
|
@Schema(description = "赠送金")
|
||||||
@ExcelProperty("赠送金")
|
@ExcelProperty("赠送金")
|
||||||
private BigDecimal giftMoneySpent;
|
private BigDecimal giftMoneySpent;
|
||||||
|
|
||||||
|
@Schema(description = "扫码支付")
|
||||||
|
@ExcelProperty("扫码支付")
|
||||||
|
private BigDecimal externalSpent;
|
||||||
|
|
||||||
@Schema(description = "购买时间")
|
@Schema(description = "购买时间")
|
||||||
@ExcelProperty("购买时间")
|
@ExcelProperty("购买时间")
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|||||||
+5
-2
@@ -31,8 +31,8 @@ public class SalesDetailRespVO {
|
|||||||
@ExcelProperty("产品金额")
|
@ExcelProperty("产品金额")
|
||||||
private BigDecimal totalAmount;
|
private BigDecimal totalAmount;
|
||||||
|
|
||||||
@Schema(description = "账户充值支付")
|
@Schema(description = "扫码支付")
|
||||||
@ExcelProperty("账户充值支付")
|
@ExcelProperty("扫码支付")
|
||||||
private BigDecimal rechargeSpent;
|
private BigDecimal rechargeSpent;
|
||||||
|
|
||||||
@Schema(description = "现金支付")
|
@Schema(description = "现金支付")
|
||||||
@@ -51,6 +51,9 @@ public class SalesDetailRespVO {
|
|||||||
@ExcelProperty("组织名称")
|
@ExcelProperty("组织名称")
|
||||||
private String organName;
|
private String organName;
|
||||||
|
|
||||||
|
@Schema(description = "支付方式")
|
||||||
|
private String paymentMethod;
|
||||||
|
|
||||||
@Schema(description = "交易时间")
|
@Schema(description = "交易时间")
|
||||||
@ExcelProperty("交易时间")
|
@ExcelProperty("交易时间")
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|||||||
+3
@@ -28,6 +28,9 @@ public class SalesDetailsPageReqVO extends PageParam {
|
|||||||
@Schema(description = "组织ID")
|
@Schema(description = "组织ID")
|
||||||
private Long organId;
|
private Long organId;
|
||||||
|
|
||||||
|
@Schema(description = "订单号")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "产品金额")
|
@Schema(description = "产品金额")
|
||||||
@DecimalMin(value = "0.01", message = "产品购买金额必须大于零")
|
@DecimalMin(value = "0.01", message = "产品购买金额必须大于零")
|
||||||
@DecimalMax(value = "100000", message = "产品购买金额不能超过十万")
|
@DecimalMax(value = "100000", message = "产品购买金额不能超过十万")
|
||||||
|
|||||||
+10
-7
@@ -28,13 +28,13 @@ public class RechargeDetailRespVO {
|
|||||||
@EnumFormat(value = TradeTypeEnum.class)
|
@EnumFormat(value = TradeTypeEnum.class)
|
||||||
private Integer tradeType;
|
private Integer tradeType;
|
||||||
|
|
||||||
@Schema(description = "订单号")
|
|
||||||
@ExcelProperty("订单号")
|
|
||||||
private String payNo;
|
|
||||||
|
|
||||||
@Schema(description = "充值金额")
|
@Schema(description = "充值金额")
|
||||||
@ExcelProperty("充值金额")
|
@ExcelProperty("充值金额")
|
||||||
private BigDecimal price;
|
private BigDecimal rechargeAmount;
|
||||||
|
|
||||||
|
@Schema(description = "赠送金金额")
|
||||||
|
@ExcelProperty("赠送金金额")
|
||||||
|
private BigDecimal rechargeGiftAmount;
|
||||||
|
|
||||||
@ExcelIgnore
|
@ExcelIgnore
|
||||||
private Long purchaseId;
|
private Long purchaseId;
|
||||||
@@ -43,8 +43,8 @@ public class RechargeDetailRespVO {
|
|||||||
@ExcelProperty("产品金额")
|
@ExcelProperty("产品金额")
|
||||||
private BigDecimal totalAmount;
|
private BigDecimal totalAmount;
|
||||||
|
|
||||||
@Schema(description = "账户充值支付")
|
@Schema(description = "扫码支付")
|
||||||
@ExcelProperty("账户充值支付")
|
@ExcelProperty("扫码支付")
|
||||||
private BigDecimal rechargeSpent;
|
private BigDecimal rechargeSpent;
|
||||||
|
|
||||||
@Schema(description = "现金支付")
|
@Schema(description = "现金支付")
|
||||||
@@ -66,4 +66,7 @@ public class RechargeDetailRespVO {
|
|||||||
@Schema(description = "交易时间")
|
@Schema(description = "交易时间")
|
||||||
@ExcelProperty("交易时间")
|
@ExcelProperty("交易时间")
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "支付方式")
|
||||||
|
private String paymentMethod;
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -1,6 +1,5 @@
|
|||||||
package com.cf.imes.module.system.controller.admin.pay.vo;
|
package com.cf.imes.module.system.controller.admin.pay.vo;
|
||||||
|
|
||||||
import com.cf.imes.module.system.validation.pay.PayProductChannelCodeValid;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
@@ -27,7 +26,6 @@ public class PayProductOrderUnifiedReqVO {
|
|||||||
private Long productId;
|
private Long productId;
|
||||||
|
|
||||||
@Schema(description = "产品支付渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
@Schema(description = "产品支付渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||||
@PayProductChannelCodeValid
|
|
||||||
@NotNull(message = "产品支付渠道编码不能为空", groups = PayProductGroup.class)
|
@NotNull(message = "产品支付渠道编码不能为空", groups = PayProductGroup.class)
|
||||||
private Integer productChannelCode;
|
private Integer productChannelCode;
|
||||||
|
|
||||||
|
|||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
package com.cf.imes.module.system.dal.dataobject.incomeandexpense;
|
||||||
|
|
||||||
|
import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Gqr
|
||||||
|
* @since 2025/10/17 11:48
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AccountDetailsDO {
|
||||||
|
/** 收支明细ID */
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 流水号 */
|
||||||
|
private String businessNo;
|
||||||
|
|
||||||
|
/** 支付订单号 */
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
/** 软件购买id */
|
||||||
|
private Long purchaseId;
|
||||||
|
|
||||||
|
/** 交易类型 */
|
||||||
|
private Integer tradeType;
|
||||||
|
|
||||||
|
/** 收支类型 */
|
||||||
|
private Integer incomeExpenseType;
|
||||||
|
|
||||||
|
/** 现金金额变化 */
|
||||||
|
private BigDecimal cashAmountChange;
|
||||||
|
|
||||||
|
/** 赠送金金额变化 */
|
||||||
|
private BigDecimal giftAmountChange;
|
||||||
|
|
||||||
|
/** 创建人 */
|
||||||
|
private String creator;
|
||||||
|
|
||||||
|
/** 创建时间 */
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/** 机构ID */
|
||||||
|
private Long organId;
|
||||||
|
|
||||||
|
/** 订单总金额 */
|
||||||
|
private BigDecimal totalAmount;
|
||||||
|
|
||||||
|
/** 外部支付金额 */
|
||||||
|
private BigDecimal externalSpent;
|
||||||
|
|
||||||
|
/** 账户余额支付金额 */
|
||||||
|
private BigDecimal accountBalanceSpent;
|
||||||
|
|
||||||
|
/** 赠送金支付金额 */
|
||||||
|
private BigDecimal giftMoneySpent;
|
||||||
|
|
||||||
|
/** 支付方式 */
|
||||||
|
private Integer paymentMethod;
|
||||||
|
|
||||||
|
/** 渠道编码 */
|
||||||
|
private PayChannelCodeEnum channelCode;
|
||||||
|
}
|
||||||
-5
@@ -63,11 +63,6 @@ public class PayOrderDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private Long purchaseId;
|
private Long purchaseId;
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付订单号:P + 当前id + 时间戳(yyyyMMddHHmmss)
|
|
||||||
*/
|
|
||||||
private String payNo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 交易类型
|
* 交易类型
|
||||||
*/
|
*/
|
||||||
|
|||||||
+8
@@ -1,5 +1,6 @@
|
|||||||
package com.cf.imes.module.system.dal.dataobject.pay;
|
package com.cf.imes.module.system.dal.dataobject.pay;
|
||||||
|
|
||||||
|
import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -62,4 +63,11 @@ public class PayOrderRechargeDetailDO {
|
|||||||
* 交易时间
|
* 交易时间
|
||||||
*/
|
*/
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渠道编号
|
||||||
|
*/
|
||||||
|
private PayChannelCodeEnum channelCode;
|
||||||
|
|
||||||
|
private Integer payMethod;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -44,13 +44,14 @@ public interface PurchaseRecordMapper extends BaseMapperX<PurchaseRecordDO> {
|
|||||||
.eq(PurchaseRecordDO::getDeleted, false)
|
.eq(PurchaseRecordDO::getDeleted, false)
|
||||||
.eqIfPresent(PurchaseRecordDO::getOrganId, pageReqVO.getOrganId())
|
.eqIfPresent(PurchaseRecordDO::getOrganId, pageReqVO.getOrganId())
|
||||||
.likeIfPresent(PurchaseRecordDO::getProductName, pageReqVO.getProductName())
|
.likeIfPresent(PurchaseRecordDO::getProductName, pageReqVO.getProductName())
|
||||||
.likeIfPresent(PurchaseRecordDO::getOrderNo, pageReqVO.getOrderNo())
|
.likeIfPresent(PurchaseRecordDO::getId, pageReqVO.getId())
|
||||||
.eqIfPresent(PurchaseRecordDO::getPurchaseDuration, pageReqVO.getPurchaseDuration())
|
.eqIfPresent(PurchaseRecordDO::getPurchaseDuration, pageReqVO.getPurchaseDuration())
|
||||||
.betweenIfPresent(PurchaseRecordDO::getCreateTime, pageReqVO.getCreateTime())
|
.betweenIfPresent(PurchaseRecordDO::getCreateTime, pageReqVO.getCreateTime())
|
||||||
.likeIfPresent(PurchaseRecordDO::getCreator, pageReqVO.getCreator())
|
.likeIfPresent(PurchaseRecordDO::getCreator, pageReqVO.getCreator())
|
||||||
|
.eqIfPresent(PurchaseRecordDO::getTotalAmount, pageReqVO.getProductPrice())
|
||||||
|
.betweenIfPresent(PurchaseRecordDO::getEndTime, pageReqVO.getEndTime())
|
||||||
.ne(PurchaseRecordDO::getInitial, true)
|
.ne(PurchaseRecordDO::getInitial, true)
|
||||||
.orderByDesc(PurchaseRecordDO::getCreateTime));
|
.orderByDesc(PurchaseRecordDO::getCreateTime));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,6 +116,7 @@ public interface PurchaseRecordMapper extends BaseMapperX<PurchaseRecordDO> {
|
|||||||
*/
|
*/
|
||||||
default PageResult<PurchaseRecordDO> getSaleDetailsPage(SalesDetailsPageReqVO pageReqVO) {
|
default PageResult<PurchaseRecordDO> getSaleDetailsPage(SalesDetailsPageReqVO pageReqVO) {
|
||||||
return selectPage(pageReqVO, new LambdaQueryWrapperX<PurchaseRecordDO>()
|
return selectPage(pageReqVO, new LambdaQueryWrapperX<PurchaseRecordDO>()
|
||||||
|
.eqIfPresent(PurchaseRecordDO::getId, pageReqVO.getId())
|
||||||
.betweenIfPresent(PurchaseRecordDO::getCreateTime, pageReqVO.getCreateTime())
|
.betweenIfPresent(PurchaseRecordDO::getCreateTime, pageReqVO.getCreateTime())
|
||||||
.eqIfPresent(PurchaseRecordDO::getTotalAmount, pageReqVO.getProductPrice())
|
.eqIfPresent(PurchaseRecordDO::getTotalAmount, pageReqVO.getProductPrice())
|
||||||
.eqIfPresent(PurchaseRecordDO::getOrganId, pageReqVO.getOrganId())
|
.eqIfPresent(PurchaseRecordDO::getOrganId, pageReqVO.getOrganId())
|
||||||
|
|||||||
+17
-1
@@ -2,16 +2,18 @@ package com.cf.imes.module.system.dal.mysql.incomeandexpense;
|
|||||||
|
|
||||||
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.cf.imes.framework.common.pojo.PageResult;
|
import com.cf.imes.framework.common.pojo.PageResult;
|
||||||
import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX;
|
import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import com.cf.imes.module.system.controller.admin.funds.balancedetails.vo.BalanceDetailsPageReqVO;
|
import com.cf.imes.module.system.controller.admin.funds.balancedetails.vo.BalanceDetailsPageReqVO;
|
||||||
import com.cf.imes.module.system.controller.admin.funds.manualadjust.vo.ManualAdjustAccountBalancePageReqVO;
|
import com.cf.imes.module.system.controller.admin.funds.manualadjust.vo.ManualAdjustAccountBalancePageReqVO;
|
||||||
|
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.AccountDetailsDO;
|
||||||
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
|
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
|
||||||
import com.cf.imes.module.system.enums.pay.AdjustTypeEnum;
|
import com.cf.imes.module.system.enums.pay.AdjustTypeEnum;
|
||||||
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
/**
|
/**
|
||||||
* 收支明细 Mapper
|
* 收支明细 Mapper
|
||||||
*
|
*
|
||||||
@@ -33,6 +35,11 @@ public interface IncomeExpenseDetailsMapper extends BaseMapperX<IncomeExpenseDet
|
|||||||
.likeIfPresent(IncomeExpenseDetailsDO::getBusinessNo, pageReqVO.getBusinessNo())
|
.likeIfPresent(IncomeExpenseDetailsDO::getBusinessNo, pageReqVO.getBusinessNo())
|
||||||
.likeIfPresent(IncomeExpenseDetailsDO::getOrderNo, pageReqVO.getOrderNo())
|
.likeIfPresent(IncomeExpenseDetailsDO::getOrderNo, pageReqVO.getOrderNo())
|
||||||
.betweenIfPresent(IncomeExpenseDetailsDO::getCreateTime, pageReqVO.getCreateTime())
|
.betweenIfPresent(IncomeExpenseDetailsDO::getCreateTime, pageReqVO.getCreateTime())
|
||||||
|
.eqIfPresent(IncomeExpenseDetailsDO::getIncomeExpenseType, pageReqVO.getIncomeExpenseType())
|
||||||
|
.eqIfPresent(IncomeExpenseDetailsDO::getTradeType, pageReqVO.getTradeType())
|
||||||
|
.eqIfPresent(IncomeExpenseDetailsDO::getCashAmountChange, pageReqVO.getRechargeAmount())
|
||||||
|
.eqIfPresent(IncomeExpenseDetailsDO::getGiftAmountChange, pageReqVO.getGiftAmount())
|
||||||
|
|
||||||
.orderByDesc(IncomeExpenseDetailsDO::getId);
|
.orderByDesc(IncomeExpenseDetailsDO::getId);
|
||||||
Integer tradeType = pageReqVO.getTradeType();
|
Integer tradeType = pageReqVO.getTradeType();
|
||||||
// 如果查的是交易,需要包含手工调账
|
// 如果查的是交易,需要包含手工调账
|
||||||
@@ -45,6 +52,15 @@ public interface IncomeExpenseDetailsMapper extends BaseMapperX<IncomeExpenseDet
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账户明细分页查询
|
||||||
|
*
|
||||||
|
* @param page
|
||||||
|
* @param reqVO
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
IPage<AccountDetailsDO> getAccountDetailsPage(@Param("page") IPage<AccountDetailsDO> page, @Param("reqVO") BalanceDetailsPageReqVO reqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取手工调账分页
|
* 获取手工调账分页
|
||||||
*
|
*
|
||||||
|
|||||||
+5
-3
@@ -17,20 +17,22 @@ public enum PayChannelCodeEnum {
|
|||||||
/**
|
/**
|
||||||
* 支付宝
|
* 支付宝
|
||||||
*/
|
*/
|
||||||
ALIPAY_PC(0),
|
ALIPAY_PC(0, "支付宝"),
|
||||||
/**
|
/**
|
||||||
* 微信
|
* 微信
|
||||||
*/
|
*/
|
||||||
WECHAT_NATIVE(1),
|
WECHAT_NATIVE(1, "微信"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手工调账
|
* 手工调账
|
||||||
*/
|
*/
|
||||||
MANUAL_ADJUST(2);
|
MANUAL_ADJUST(2, "手工调账");
|
||||||
|
|
||||||
@EnumValue
|
@EnumValue
|
||||||
private final Integer code;
|
private final Integer code;
|
||||||
|
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public static PayChannelCodeEnum fromType(Integer type) {
|
public static PayChannelCodeEnum fromType(Integer type) {
|
||||||
for (PayChannelCodeEnum value : values()) {
|
for (PayChannelCodeEnum value : values()) {
|
||||||
|
|||||||
-4
@@ -7,10 +7,6 @@ package com.cf.imes.module.system.enums.pay;
|
|||||||
* @since 2025/8/12 17:49
|
* @since 2025/8/12 17:49
|
||||||
*/
|
*/
|
||||||
public class PayConstants {
|
public class PayConstants {
|
||||||
public static final String PAY_ORDER_PREFIX_FORMAT = "PO%s"; // 第三方支付流水号格式
|
|
||||||
|
|
||||||
public static final String PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT = "PD%s"; // 产品购买流水号格式
|
|
||||||
|
|
||||||
public static final String PAY_PRODUCT_DETAIL_AUTO_GENERATE_MONTH_ID_FORMAT = "1759029544%d"; // 1~12月定价规则自动生成ID格式
|
public static final String PAY_PRODUCT_DETAIL_AUTO_GENERATE_MONTH_ID_FORMAT = "1759029544%d"; // 1~12月定价规则自动生成ID格式
|
||||||
|
|
||||||
public static final String PAY_PRODUCT_DETAIL_AUTO_GENERATE_YEAR_ID_FORMAT = "1759993429%d"; // 1年定价规则自动生成ID格式
|
public static final String PAY_PRODUCT_DETAIL_AUTO_GENERATE_YEAR_ID_FORMAT = "1759993429%d"; // 1年定价规则自动生成ID格式
|
||||||
|
|||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
package com.cf.imes.module.system.enums.pay;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Gqr
|
||||||
|
* @since 2025/10/16 11:58
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum PayProductChannelCodeEnumNew {
|
||||||
|
/**
|
||||||
|
* 现金
|
||||||
|
*/
|
||||||
|
BALANCE(1, "现金"), // 二进制 0001
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赠送金
|
||||||
|
*/
|
||||||
|
GIFT(1 << 1, "赠送金"), // 二进制 0010
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付宝
|
||||||
|
*/
|
||||||
|
ALIPAY(1 << 2, "支付宝"), // 二进制 0100
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信
|
||||||
|
*/
|
||||||
|
WECHAT(1 << 3, "微信"); // 二进制 1000
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
private final String label;
|
||||||
|
|
||||||
|
PayProductChannelCodeEnumNew(int code, String label) {
|
||||||
|
this.code = code;
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把多个支付方式组合成一个整数
|
||||||
|
*
|
||||||
|
* @param types
|
||||||
|
*/
|
||||||
|
public static int combine(PayProductChannelCodeEnumNew... types) {
|
||||||
|
int result = 0;
|
||||||
|
for (PayProductChannelCodeEnumNew type : types) {
|
||||||
|
result |= type.code;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否包含现金
|
||||||
|
*/
|
||||||
|
public static boolean hasBalance(int code) {
|
||||||
|
return (code & PayProductChannelCodeEnumNew.BALANCE.getCode()) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否只有现金
|
||||||
|
*/
|
||||||
|
public static boolean isBalanceOnly(int code) {
|
||||||
|
return code == BALANCE.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是现金+赠送金
|
||||||
|
*/
|
||||||
|
public static boolean isBalanceAndGift(int code) {
|
||||||
|
return code == combine(BALANCE, GIFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否包含赠送金
|
||||||
|
*/
|
||||||
|
public static boolean hasGift(int code) {
|
||||||
|
return (code & PayProductChannelCodeEnumNew.GIFT.getCode()) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否只有赠送金
|
||||||
|
*/
|
||||||
|
public static boolean isGiftOnly(int code) {
|
||||||
|
return code == GIFT.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否包含支付宝
|
||||||
|
*/
|
||||||
|
public static boolean hasAlipay(int code) {
|
||||||
|
return (code & PayProductChannelCodeEnumNew.ALIPAY.getCode()) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否包含微信
|
||||||
|
*/
|
||||||
|
public static boolean hasWechat(int code) {
|
||||||
|
return (code & PayProductChannelCodeEnumNew.WECHAT.getCode()) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有包含的支付方式列表
|
||||||
|
*/
|
||||||
|
public static List<PayProductChannelCodeEnumNew> parse(int code) {
|
||||||
|
List<PayProductChannelCodeEnumNew> list = new ArrayList<>();
|
||||||
|
for (PayProductChannelCodeEnumNew type : PayProductChannelCodeEnumNew.values()) {
|
||||||
|
if ((code & type.getCode()) != 0) {
|
||||||
|
list.add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回可读描述,比如“现金+微信”
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
*/
|
||||||
|
public static String describe(int code) {
|
||||||
|
return parse(code).stream()
|
||||||
|
.map(PayProductChannelCodeEnumNew::getLabel)
|
||||||
|
.collect(Collectors.joining("+"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-19
@@ -2,15 +2,17 @@ package com.cf.imes.module.system.service.funds.incomeandexpense;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
|
||||||
import com.cf.imes.framework.common.pojo.PageResult;
|
import com.cf.imes.framework.common.pojo.PageResult;
|
||||||
import com.cf.imes.module.system.controller.admin.funds.balancedetails.vo.BalanceDetailsPageReqVO;
|
import com.cf.imes.module.system.controller.admin.funds.balancedetails.vo.BalanceDetailsPageReqVO;
|
||||||
import com.cf.imes.module.system.controller.admin.funds.balancedetails.vo.BalanceDetailsRespVO;
|
import com.cf.imes.module.system.controller.admin.funds.balancedetails.vo.BalanceDetailsRespVO;
|
||||||
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
|
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.AccountDetailsDO;
|
||||||
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
|
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
|
||||||
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
|
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
|
||||||
import com.cf.imes.module.system.dal.mysql.incomeandexpense.IncomeExpenseDetailsMapper;
|
import com.cf.imes.module.system.dal.mysql.incomeandexpense.IncomeExpenseDetailsMapper;
|
||||||
|
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnumNew;
|
||||||
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
||||||
import com.cf.imes.module.system.service.funds.purchase.PurchaseService;
|
|
||||||
import com.cf.imes.module.system.service.organ.OrganService;
|
import com.cf.imes.module.system.service.organ.OrganService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -29,9 +31,6 @@ public class IncomeExpenseServiceImpl implements IncomeExpenseService {
|
|||||||
@Resource
|
@Resource
|
||||||
private IncomeExpenseDetailsMapper incomeExpenseDetailsMapper;
|
private IncomeExpenseDetailsMapper incomeExpenseDetailsMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private PurchaseService purchaseService;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private OrganService organService;
|
private OrganService organService;
|
||||||
|
|
||||||
@@ -43,8 +42,9 @@ public class IncomeExpenseServiceImpl implements IncomeExpenseService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<BalanceDetailsRespVO> getBalanceDetailsPage(BalanceDetailsPageReqVO pageReqVO) {
|
public PageResult<BalanceDetailsRespVO> getBalanceDetailsPage(BalanceDetailsPageReqVO pageReqVO) {
|
||||||
PageResult<IncomeExpenseDetailsDO> pageResult = incomeExpenseDetailsMapper.selectDetailsPage(pageReqVO);
|
PageDTO<AccountDetailsDO> page = new PageDTO<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
|
||||||
List<IncomeExpenseDetailsDO> pageResultList = pageResult.getList();
|
IPage<AccountDetailsDO> pageResult = incomeExpenseDetailsMapper.getAccountDetailsPage(page, pageReqVO);
|
||||||
|
List<AccountDetailsDO> pageResultList = pageResult.getRecords();
|
||||||
if(CollUtil.isEmpty(pageResultList)){
|
if(CollUtil.isEmpty(pageResultList)){
|
||||||
return new PageResult<>();
|
return new PageResult<>();
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,6 @@ public class IncomeExpenseServiceImpl implements IncomeExpenseService {
|
|||||||
pageResultList.forEach(i -> {
|
pageResultList.forEach(i -> {
|
||||||
BalanceDetailsRespVO respVO = new BalanceDetailsRespVO();
|
BalanceDetailsRespVO respVO = new BalanceDetailsRespVO();
|
||||||
respVO.setBusinessNo(i.getBusinessNo());
|
respVO.setBusinessNo(i.getBusinessNo());
|
||||||
respVO.setOrderNo(i.getOrderNo());
|
|
||||||
respVO.setCreator(i.getCreator());
|
respVO.setCreator(i.getCreator());
|
||||||
respVO.setCreateTime(i.getCreateTime());
|
respVO.setCreateTime(i.getCreateTime());
|
||||||
respVO.setIncomeExpenseType(i.getIncomeExpenseType());
|
respVO.setIncomeExpenseType(i.getIncomeExpenseType());
|
||||||
@@ -65,23 +64,23 @@ public class IncomeExpenseServiceImpl implements IncomeExpenseService {
|
|||||||
if (TradeTypeEnum.RECHARGE.getCode().equals(tradeType)) {
|
if (TradeTypeEnum.RECHARGE.getCode().equals(tradeType)) {
|
||||||
respVO.setRechargeAmount(i.getCashAmountChange());
|
respVO.setRechargeAmount(i.getCashAmountChange());
|
||||||
respVO.setRechargeGiftAmount(i.getGiftAmountChange());
|
respVO.setRechargeGiftAmount(i.getGiftAmountChange());
|
||||||
|
respVO.setPaymentMethod(i.getChannelCode().getDesc());
|
||||||
|
respVO.setOrderNo(i.getOrderNo());
|
||||||
|
|
||||||
} else if (TradeTypeEnum.PRODUCT.getCode().equals(tradeType)) {
|
} else if (TradeTypeEnum.PRODUCT.getCode().equals(tradeType)) {
|
||||||
Long purchaseId = i.getPurchaseId();
|
respVO.setProductPrice(i.getTotalAmount());
|
||||||
if (ObjectUtil.isNotNull(purchaseId)) {
|
respVO.setBalancePayAmount(i.getAccountBalanceSpent());
|
||||||
// 查询对应的购买记录
|
respVO.setRechargePayAmount(i.getExternalSpent());
|
||||||
PurchaseRecordDO record = purchaseService.getRecord(purchaseId);
|
respVO.setGiftPayAmount(i.getGiftMoneySpent());
|
||||||
if (ObjectUtil.isNotNull(record)) {
|
respVO.setPaymentMethod(PayProductChannelCodeEnumNew.describe(i.getPaymentMethod()));
|
||||||
respVO.setProductPrice(record.getTotalAmount());
|
respVO.setOrderNo(String.valueOf(i.getPurchaseId()));
|
||||||
respVO.setBalancePayAmount(record.getAccountBalanceSpent());
|
|
||||||
respVO.setRechargePayAmount(record.getExternalSpent());
|
|
||||||
respVO.setGiftPayAmount(record.getGiftMoneySpent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (TradeTypeEnum.MANUAL_ADJUST.getCode().equals(tradeType)) {
|
} else if (TradeTypeEnum.MANUAL_ADJUST.getCode().equals(tradeType)) {
|
||||||
respVO.setRechargeAmount(i.getCashAmountChange());
|
respVO.setRechargeAmount(i.getCashAmountChange());
|
||||||
respVO.setRechargeGiftAmount(i.getGiftAmountChange());
|
respVO.setRechargeGiftAmount(i.getGiftAmountChange());
|
||||||
// 手工调账算到充值
|
// 手工调账算到充值
|
||||||
respVO.setTradeType(TradeTypeEnum.RECHARGE.getCode());
|
respVO.setTradeType(TradeTypeEnum.RECHARGE.getCode());
|
||||||
|
respVO.setPaymentMethod(TradeTypeEnum.MANUAL_ADJUST.getDesc());
|
||||||
}
|
}
|
||||||
// 查询操作组织名
|
// 查询操作组织名
|
||||||
OrganizationDO organ = organService.getOrganWithDeleted(i.getOrganId());
|
OrganizationDO organ = organService.getOrganWithDeleted(i.getOrganId());
|
||||||
|
|||||||
-2
@@ -127,7 +127,6 @@ public class ManualAdjustAccountBalanceServiceImpl implements ManualAdjustAccoun
|
|||||||
// 新增收支明细
|
// 新增收支明细
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
||||||
.organId(organId)
|
.organId(organId)
|
||||||
.orderNo(payOrderDO.getPayNo())
|
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
||||||
.tradeType(TradeTypeEnum.MANUAL_ADJUST.getCode())
|
.tradeType(TradeTypeEnum.MANUAL_ADJUST.getCode())
|
||||||
.incomeExpenseType(incomeExpenseType)
|
.incomeExpenseType(incomeExpenseType)
|
||||||
@@ -152,7 +151,6 @@ public class ManualAdjustAccountBalanceServiceImpl implements ManualAdjustAccoun
|
|||||||
|
|
||||||
Long payOrderId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
Long payOrderId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
||||||
payOrderDO.setId(payOrderId);
|
payOrderDO.setId(payOrderId);
|
||||||
payOrderDO.setPayNo(String.format(PayConstants.PAY_ORDER_PREFIX_FORMAT, payOrderId));
|
|
||||||
payOrderDO.setStatus(PayOrderStatusEnum.SUCCESS);
|
payOrderDO.setStatus(PayOrderStatusEnum.SUCCESS);
|
||||||
payOrderDO.setPrice(recharge);
|
payOrderDO.setPrice(recharge);
|
||||||
payOrderDO.setSubject("手工调账");
|
payOrderDO.setSubject("手工调账");
|
||||||
|
|||||||
+11
-3
@@ -1,5 +1,6 @@
|
|||||||
package com.cf.imes.module.system.service.funds.purchase;
|
package com.cf.imes.module.system.service.funds.purchase;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
@@ -28,7 +29,7 @@ import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
|
|||||||
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
|
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
|
||||||
import com.cf.imes.module.system.dal.mysql.funds.purchase.PurchaseRecordMapper;
|
import com.cf.imes.module.system.dal.mysql.funds.purchase.PurchaseRecordMapper;
|
||||||
import com.cf.imes.module.system.enums.pay.InvoiceStatusEnum;
|
import com.cf.imes.module.system.enums.pay.InvoiceStatusEnum;
|
||||||
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnum;
|
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnumNew;
|
||||||
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
||||||
import com.cf.imes.module.system.enums.pay.PurchaseRecordStatusEnum;
|
import com.cf.imes.module.system.enums.pay.PurchaseRecordStatusEnum;
|
||||||
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
||||||
@@ -107,7 +108,12 @@ public class PurchaseServiceImpl implements PurchaseService {
|
|||||||
if (CollUtil.isEmpty(resultList)) {
|
if (CollUtil.isEmpty(resultList)) {
|
||||||
return new PageResult<>();
|
return new PageResult<>();
|
||||||
}
|
}
|
||||||
List<PurchaseRecordRespVO> respVOS = BeanUtils.toBean(resultList, PurchaseRecordRespVO.class);
|
List<PurchaseRecordRespVO> respVOS = new ArrayList<>();
|
||||||
|
for (PurchaseRecordDO purchaseRecordDO : resultList) {
|
||||||
|
PurchaseRecordRespVO respVO = BeanUtil.copyProperties(purchaseRecordDO, PurchaseRecordRespVO.class, "paymentMethod");
|
||||||
|
respVO.setPaymentMethod(PayProductChannelCodeEnumNew.describe(purchaseRecordDO.getPaymentMethod()));
|
||||||
|
respVOS.add(respVO);
|
||||||
|
}
|
||||||
respVOS.forEach(respVO -> {
|
respVOS.forEach(respVO -> {
|
||||||
OrganizationDO organ = organService.getOrganWithDeleted(respVO.getOrganId());
|
OrganizationDO organ = organService.getOrganWithDeleted(respVO.getOrganId());
|
||||||
if (ObjectUtil.isNotNull(organ)) {
|
if (ObjectUtil.isNotNull(organ)) {
|
||||||
@@ -184,7 +190,7 @@ public class PurchaseServiceImpl implements PurchaseService {
|
|||||||
.startTime(now)
|
.startTime(now)
|
||||||
// 组织创建时的套餐购买默认过期
|
// 组织创建时的套餐购买默认过期
|
||||||
.endTime(now.minusDays(1))
|
.endTime(now.minusDays(1))
|
||||||
.paymentMethod(PayProductChannelCodeEnum.ORGAN_CREATE.getCode())
|
.paymentMethod(PayProductChannelCodeEnumNew.BALANCE.getCode())
|
||||||
.totalAmount(price)
|
.totalAmount(price)
|
||||||
.monthAverageAmount(monthAverageAmount)
|
.monthAverageAmount(monthAverageAmount)
|
||||||
.originalAmount(price)
|
.originalAmount(price)
|
||||||
@@ -297,6 +303,8 @@ public class PurchaseServiceImpl implements PurchaseService {
|
|||||||
respVO.setTradeType(TradeTypeEnum.PRODUCT.getCode());
|
respVO.setTradeType(TradeTypeEnum.PRODUCT.getCode());
|
||||||
// 充值支付=支付宝/微信
|
// 充值支付=支付宝/微信
|
||||||
respVO.setRechargeSpent(detailDO.getExternalSpent());
|
respVO.setRechargeSpent(detailDO.getExternalSpent());
|
||||||
|
// 支付方式
|
||||||
|
respVO.setPaymentMethod(PayProductChannelCodeEnumNew.describe(detailDO.getPaymentMethod()));
|
||||||
// 查询组织名称
|
// 查询组织名称
|
||||||
OrganizationDO organ = organService.getOrganWithDeleted(detailDO.getOrganId());
|
OrganizationDO organ = organService.getOrganWithDeleted(detailDO.getOrganId());
|
||||||
if (ObjectUtil.isNotNull(organ)) {
|
if (ObjectUtil.isNotNull(organ)) {
|
||||||
|
|||||||
+9
@@ -6,6 +6,7 @@ import com.cf.imes.module.system.controller.admin.funds.recharge.vo.RechargeDeta
|
|||||||
import com.cf.imes.module.system.controller.admin.funds.recharge.vo.RechargeDetailsPageReqVO;
|
import com.cf.imes.module.system.controller.admin.funds.recharge.vo.RechargeDetailsPageReqVO;
|
||||||
import com.cf.imes.module.system.controller.admin.pay.vo.PayProductOrderUnifiedReqVO;
|
import com.cf.imes.module.system.controller.admin.pay.vo.PayProductOrderUnifiedReqVO;
|
||||||
import com.cf.imes.module.system.controller.admin.pay.vo.PayRechargeOrderReqVO;
|
import com.cf.imes.module.system.controller.admin.pay.vo.PayRechargeOrderReqVO;
|
||||||
|
import com.cf.imes.module.system.dal.dataobject.pay.PayOrderDO;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -17,6 +18,14 @@ import java.util.Map;
|
|||||||
* @since 2025/4/17 17:29
|
* @since 2025/4/17 17:29
|
||||||
*/
|
*/
|
||||||
public interface PayOrderService {
|
public interface PayOrderService {
|
||||||
|
/**
|
||||||
|
* 根据id查询支付订单
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
PayOrderDO get(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 产品支付
|
* 产品支付
|
||||||
*
|
*
|
||||||
|
|||||||
+28
-19
@@ -39,6 +39,7 @@ import com.cf.imes.module.system.controller.admin.funds.products.vo.product.Prod
|
|||||||
import com.cf.imes.module.system.dal.dataobject.funds.delay.ProductDelayRecordDO;
|
import com.cf.imes.module.system.dal.dataobject.funds.delay.ProductDelayRecordDO;
|
||||||
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
|
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
|
||||||
import com.cf.imes.module.system.dal.dataobject.pay.PayOrderRechargeDetailDO;
|
import com.cf.imes.module.system.dal.dataobject.pay.PayOrderRechargeDetailDO;
|
||||||
|
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnumNew;
|
||||||
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
||||||
import com.cf.imes.module.system.service.funds.delay.ProductDelayService;
|
import com.cf.imes.module.system.service.funds.delay.ProductDelayService;
|
||||||
import com.cf.imes.module.system.service.funds.purchase.PurchaseService;
|
import com.cf.imes.module.system.service.funds.purchase.PurchaseService;
|
||||||
@@ -52,8 +53,6 @@ import com.cf.imes.module.system.dal.dataobject.funds.recharge.GiftMoneyDetailsD
|
|||||||
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
|
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
|
||||||
import com.cf.imes.module.system.enums.pay.IncomeExpenseTypeEnum;
|
import com.cf.imes.module.system.enums.pay.IncomeExpenseTypeEnum;
|
||||||
import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum;
|
import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum;
|
||||||
import com.cf.imes.module.system.enums.pay.PayConstants;
|
|
||||||
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
||||||
import com.cf.imes.module.system.enums.recharge.RechargeGiftTypeEnum;
|
import com.cf.imes.module.system.enums.recharge.RechargeGiftTypeEnum;
|
||||||
import com.cf.imes.module.system.enums.recharge.RechargeTypeEnum;
|
import com.cf.imes.module.system.enums.recharge.RechargeTypeEnum;
|
||||||
@@ -90,6 +89,7 @@ import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.e
|
|||||||
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_PRODUCT_PURCHASE_LOCK_ERROR;
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_PRODUCT_PURCHASE_LOCK_ERROR;
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR;
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR;
|
||||||
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_CHANNEL_NOT_SUPPORT;
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR;
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR;
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_CHANNEL_QUERY_NOT_EXIST;
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_CHANNEL_QUERY_NOT_EXIST;
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_NOTIFY_DATA_PARSE_ERROR;
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_NOTIFY_DATA_PARSE_ERROR;
|
||||||
@@ -169,6 +169,11 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
@Resource
|
@Resource
|
||||||
private SnowflakeSerialNoWorker3rd snowflakeSerialNoWorker3rd;
|
private SnowflakeSerialNoWorker3rd snowflakeSerialNoWorker3rd;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayOrderDO get(Long id) {
|
||||||
|
return payOrderMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public PayOrderRespDTO productPay(PayProductOrderUnifiedReqVO payProductOrderUnifiedReqVO) {
|
public PayOrderRespDTO productPay(PayProductOrderUnifiedReqVO payProductOrderUnifiedReqVO) {
|
||||||
@@ -178,6 +183,10 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
// 主动校验一次入参
|
// 主动校验一次入参
|
||||||
ValidationUtils.validate(validator, payProductOrderUnifiedReqVO);
|
ValidationUtils.validate(validator, payProductOrderUnifiedReqVO);
|
||||||
|
|
||||||
|
// 校验组合渠道
|
||||||
|
List<PayProductChannelCodeEnumNew> parse = PayProductChannelCodeEnumNew.parse(payProductOrderUnifiedReqVO.getProductChannelCode());
|
||||||
|
AssertUtils.notEmpty(parse, PAY_CHANNEL_NOT_SUPPORT);
|
||||||
|
|
||||||
// 校验产品和产品明细
|
// 校验产品和产品明细
|
||||||
Long productId = payProductOrderUnifiedReqVO.getProductId();
|
Long productId = payProductOrderUnifiedReqVO.getProductId();
|
||||||
ProductsDO productsDO = productsDetailService.validateProductExist(productId);
|
ProductsDO productsDO = productsDetailService.validateProductExist(productId);
|
||||||
@@ -194,7 +203,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
|
|
||||||
PayOrderUnifiedReqDTO payOrderUnifiedReqDTO = null;
|
PayOrderUnifiedReqDTO payOrderUnifiedReqDTO = null;
|
||||||
// 获取渠道方处理器
|
// 获取渠道方处理器
|
||||||
Integer channelCode = PayProductChannelCodeEnum.isAliPay(payProductOrderUnifiedReqVO.getProductChannelCode()) ? PayChannelCodeEnum.ALIPAY_PC.getCode() : PayChannelCodeEnum.WECHAT_NATIVE.getCode();
|
Integer channelCode = PayProductChannelCodeEnumNew.hasAlipay(payProductOrderUnifiedReqVO.getProductChannelCode()) ? PayChannelCodeEnum.ALIPAY_PC.getCode() : PayChannelCodeEnum.WECHAT_NATIVE.getCode();
|
||||||
try {
|
try {
|
||||||
// 检查机构产品是否有待支付的购买记录
|
// 检查机构产品是否有待支付的购买记录
|
||||||
List<PurchaseRecordDO> pendingPurchaseList = purchaseService.getOrgProductPendingPurchaseList(organId, productId);
|
List<PurchaseRecordDO> pendingPurchaseList = purchaseService.getOrgProductPendingPurchaseList(organId, productId);
|
||||||
@@ -212,8 +221,9 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
PreviouProductDelayRespVO previouProductDelayRespVO = productDelayService.getActiveRecordsByProductId(productId, organId);
|
PreviouProductDelayRespVO previouProductDelayRespVO = productDelayService.getActiveRecordsByProductId(productId, organId);
|
||||||
|
|
||||||
// 根据购买类型做价格处理
|
// 根据购买类型做价格处理
|
||||||
PayProductProcessor payProductProcessor = new PayProductProcessor(productsDO, productsDetailDO, orgInitialPurchaseRecord, previouProductDelayRespVO, organAmount, organId, payConfig, mybatisIdProperties, snowflakeSerialNoWorker3rd);
|
PayProductProcessorNew payProductProcessorNew = new PayProductProcessorNew(productsDO, productsDetailDO, orgInitialPurchaseRecord, previouProductDelayRespVO, organAmount, organId, payConfig, mybatisIdProperties, snowflakeSerialNoWorker3rd);
|
||||||
ProductProcessorRespVO productProcessorRespVO = payProductProcessor.processPayment(PayProductChannelCodeEnum.fromType(payProductOrderUnifiedReqVO.getProductChannelCode()));
|
ProductProcessorRespVO productProcessorRespVO = payProductProcessorNew.processPayment(payProductOrderUnifiedReqVO.getProductChannelCode());
|
||||||
|
|
||||||
PayOrderDO payOrder = productProcessorRespVO.getPayOrderDO();
|
PayOrderDO payOrder = productProcessorRespVO.getPayOrderDO();
|
||||||
|
|
||||||
// 处理前置的购买记录
|
// 处理前置的购买记录
|
||||||
@@ -288,7 +298,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
PreviouProductDelayRespVO previouProductDelayRespVO = productDelayService.getActiveRecordsByProductId(productId, organId);
|
PreviouProductDelayRespVO previouProductDelayRespVO = productDelayService.getActiveRecordsByProductId(productId, organId);
|
||||||
|
|
||||||
// 根据购买类型做价格处理
|
// 根据购买类型做价格处理
|
||||||
PayProductProcessor payProductProcessor = new PayProductProcessor(productsDO, productsDetailDO, orgInitialPurchaseRecord, previouProductDelayRespVO, null, organId, null, null, null);
|
PayProductProcessorNew payProductProcessor = new PayProductProcessorNew(productsDO, productsDetailDO, orgInitialPurchaseRecord, previouProductDelayRespVO, null, organId, null, null, null);
|
||||||
return LocalDateTimeUtil.format(payProductProcessor.getProductPayExpireTime(), FORMAT_YEAR_MONTH_DAY);
|
return LocalDateTimeUtil.format(payProductProcessor.getProductPayExpireTime(), FORMAT_YEAR_MONTH_DAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,7 +487,6 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
Long organId = SecurityFrameworkUtils.getUserOrganId();
|
Long organId = SecurityFrameworkUtils.getUserOrganId();
|
||||||
|
|
||||||
payOrderDO.setId(payOrderId);
|
payOrderDO.setId(payOrderId);
|
||||||
payOrderDO.setPayNo(String.format(PayConstants.PAY_ORDER_PREFIX_FORMAT, payOrderId));
|
|
||||||
payOrderDO.setStatus(PayOrderStatusEnum.WAITING);
|
payOrderDO.setStatus(PayOrderStatusEnum.WAITING);
|
||||||
payOrderDO.setExpireTime(LocalDateTime.now().plus(payConfig.getPayExpireTime()));
|
payOrderDO.setExpireTime(LocalDateTime.now().plus(payConfig.getPayExpireTime()));
|
||||||
payOrderDO.setPrice(price);
|
payOrderDO.setPrice(price);
|
||||||
@@ -500,7 +509,6 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
String subject = payOrderDO.getSubject();
|
String subject = payOrderDO.getSubject();
|
||||||
payOrderUnifiedReqDTO.setSubject(subject);
|
payOrderUnifiedReqDTO.setSubject(subject);
|
||||||
payOrderUnifiedReqDTO.setBody(subject);
|
payOrderUnifiedReqDTO.setBody(subject);
|
||||||
payOrderUnifiedReqDTO.setOutTradeNo(payOrderDO.getPayNo());
|
|
||||||
payOrderUnifiedReqDTO.setPrice(payOrderDO.getPrice());
|
payOrderUnifiedReqDTO.setPrice(payOrderDO.getPrice());
|
||||||
payOrderUnifiedReqDTO.setExpireTime(payOrderDO.getExpireTime());
|
payOrderUnifiedReqDTO.setExpireTime(payOrderDO.getExpireTime());
|
||||||
payOrderUnifiedReqDTO.setNotifyUrl(String.format(alipayConfig.getNotifyUrl(), payOrderDO.getChannelCode().getCode()));
|
payOrderUnifiedReqDTO.setNotifyUrl(String.format(alipayConfig.getNotifyUrl(), payOrderDO.getChannelCode().getCode()));
|
||||||
@@ -512,7 +520,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
public void queryPayOrderStatus(String outTradeNo, int channelCode) {
|
public void queryPayOrderStatus(String outTradeNo, int channelCode) {
|
||||||
// 校验支付订单是否存在
|
// 校验支付订单是否存在
|
||||||
PayOrderDO payOrderDO = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
|
PayOrderDO payOrderDO = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
|
||||||
.eq(PayOrderDO::getPayNo, outTradeNo)
|
.eq(PayOrderDO::getId, outTradeNo)
|
||||||
.eq(PayOrderDO::getOrganId, validOrganId())
|
.eq(PayOrderDO::getOrganId, validOrganId())
|
||||||
.eq(PayOrderDO::getDeleted, false)
|
.eq(PayOrderDO::getDeleted, false)
|
||||||
);
|
);
|
||||||
@@ -601,7 +609,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
private void notifyOrderSuccess(PayOrderRespDTO notify, String outTradeNo) {
|
private void notifyOrderSuccess(PayOrderRespDTO notify, String outTradeNo) {
|
||||||
// 1. 判断 PayOrderDO 是否处于待支付
|
// 1. 判断 PayOrderDO 是否处于待支付
|
||||||
PayOrderDO order = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
|
PayOrderDO order = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
|
||||||
.eq(PayOrderDO::getPayNo, outTradeNo));
|
.eq(PayOrderDO::getId, outTradeNo));
|
||||||
Long organId = order.getOrganId();
|
Long organId = order.getOrganId();
|
||||||
if (ObjectUtil.isNull(order)) {
|
if (ObjectUtil.isNull(order)) {
|
||||||
throw exception(PAY_ORDER_NOT_EXIST, outTradeNo);
|
throw exception(PAY_ORDER_NOT_EXIST, outTradeNo);
|
||||||
@@ -653,7 +661,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
// 支付订单的金额
|
// 支付订单的金额
|
||||||
BigDecimal price = payOrderDO.getPrice();
|
BigDecimal price = payOrderDO.getPrice();
|
||||||
// 订单流水号
|
// 订单流水号
|
||||||
String payNo = payOrderDO.getPayNo();
|
Long payOrderId = payOrderDO.getId();
|
||||||
|
|
||||||
// 1.更新组织资金
|
// 1.更新组织资金
|
||||||
// 检查赠送金是否存在已经是否在有效期,有效的话计入组织充值赠送金
|
// 检查赠送金是否存在已经是否在有效期,有效的话计入组织充值赠送金
|
||||||
@@ -665,7 +673,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
// 2.新增收支明细
|
// 2.新增收支明细
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
||||||
.organId(organId)
|
.organId(organId)
|
||||||
.orderNo(payNo)
|
.orderNo(String.valueOf(payOrderId))
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
||||||
.tradeType(TradeTypeEnum.RECHARGE.getCode())
|
.tradeType(TradeTypeEnum.RECHARGE.getCode())
|
||||||
.incomeExpenseType(IncomeExpenseTypeEnum.INCOME.getCode())
|
.incomeExpenseType(IncomeExpenseTypeEnum.INCOME.getCode())
|
||||||
@@ -710,10 +718,11 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
|
|
||||||
// 5.如果是纯第三方支付需要新增收支明细
|
// 5.如果是纯第三方支付需要新增收支明细
|
||||||
Integer paymentMethod = record.getPaymentMethod();
|
Integer paymentMethod = record.getPaymentMethod();
|
||||||
if (PayProductChannelCodeEnum.ALIPAY_PC.getCode().equals(paymentMethod) || PayProductChannelCodeEnum.WECHAT_NATIVE.getCode().equals(paymentMethod)) {
|
if (PayProductChannelCodeEnumNew.hasAlipay(paymentMethod) || PayProductChannelCodeEnumNew.hasWechat(paymentMethod)) {
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
||||||
.organId(organId)
|
.organId(organId)
|
||||||
.orderNo(String.format(PayConstants.PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT, purchaseId))
|
.orderNo(String.valueOf(payOrderDO.getId()))
|
||||||
|
.purchaseId(purchaseId)
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
||||||
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
||||||
.incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
|
.incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
|
||||||
@@ -735,7 +744,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
private void notifyOrderClosed(PayOrderRespDTO notify, String outTradeNo) {
|
private void notifyOrderClosed(PayOrderRespDTO notify, String outTradeNo) {
|
||||||
// 1. 查询 PayOrderDO
|
// 1. 查询 PayOrderDO
|
||||||
PayOrderDO order = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
|
PayOrderDO order = payOrderMapper.selectOne(new LambdaQueryWrapper<PayOrderDO>()
|
||||||
.eq(PayOrderDO::getPayNo, outTradeNo));
|
.eq(PayOrderDO::getId, outTradeNo));
|
||||||
Long organId = order.getOrganId();
|
Long organId = order.getOrganId();
|
||||||
if (ObjectUtil.isNull(order)) {
|
if (ObjectUtil.isNull(order)) {
|
||||||
throw exception(PAY_ORDER_NOT_EXIST, outTradeNo);
|
throw exception(PAY_ORDER_NOT_EXIST, outTradeNo);
|
||||||
@@ -818,7 +827,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
// 3、新增退回的收支记录
|
// 3、新增退回的收支记录
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
||||||
.organId(organId)
|
.organId(organId)
|
||||||
.orderNo(String.format(PayConstants.PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT, purchaseId))
|
.purchaseId(purchaseId)
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
||||||
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
||||||
.incomeExpenseType(IncomeExpenseTypeEnum.INCOME.getCode())
|
.incomeExpenseType(IncomeExpenseTypeEnum.INCOME.getCode())
|
||||||
@@ -883,7 +892,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode.getCode());
|
AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode.getCode());
|
||||||
|
|
||||||
// 2、调用三方接口,查询支付单状态,是不是已支付
|
// 2、调用三方接口,查询支付单状态,是不是已支付
|
||||||
PayOrderRespDTO payOrderRespDTO = payHandler.queryPayOrder(order.getPayNo());
|
PayOrderRespDTO payOrderRespDTO = payHandler.queryPayOrder(String.valueOf(order.getId()));
|
||||||
if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus()) || PayOrderStatusEnum.isClosed(payOrderRespDTO.getStatus())) {
|
if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus()) || PayOrderStatusEnum.isClosed(payOrderRespDTO.getStatus())) {
|
||||||
notifyOrderWithTx(payOrderRespDTO);
|
notifyOrderWithTx(payOrderRespDTO);
|
||||||
log.info("[expireOrder][order({}) 更新为支付关闭成功]", order.getId());
|
log.info("[expireOrder][order({}) 更新为支付关闭成功]", order.getId());
|
||||||
@@ -892,9 +901,9 @@ public class PayOrderServiceImpl implements PayOrderService {
|
|||||||
|
|
||||||
// 3、系统内超期未支付状态是NOTPAY,主动关单后再次查询状态
|
// 3、系统内超期未支付状态是NOTPAY,主动关单后再次查询状态
|
||||||
if (PayOrderStatusEnum.WAITING.getStatus().equals(payOrderRespDTO.getStatus())) {
|
if (PayOrderStatusEnum.WAITING.getStatus().equals(payOrderRespDTO.getStatus())) {
|
||||||
boolean closeOrderResult = payHandler.closeOrder(order.getPayNo());
|
boolean closeOrderResult = payHandler.closeOrder(String.valueOf(order.getId()));
|
||||||
if (closeOrderResult) {
|
if (closeOrderResult) {
|
||||||
payOrderRespDTO = payHandler.queryPayOrder(order.getPayNo());
|
payOrderRespDTO = payHandler.queryPayOrder(String.valueOf(order.getId()));
|
||||||
if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus()) || PayOrderStatusEnum.isClosed(payOrderRespDTO.getStatus())) {
|
if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus()) || PayOrderStatusEnum.isClosed(payOrderRespDTO.getStatus())) {
|
||||||
notifyOrderWithTx(payOrderRespDTO);
|
notifyOrderWithTx(payOrderRespDTO);
|
||||||
log.info("[expireOrder][order({}) 更新为支付关闭成功]", order.getId());
|
log.info("[expireOrder][order({}) 更新为支付关闭成功]", order.getId());
|
||||||
|
|||||||
-989
@@ -1,989 +0,0 @@
|
|||||||
package com.cf.imes.module.system.service.pay;
|
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import cn.hutool.core.date.DatePattern;
|
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
|
||||||
import cn.hutool.core.util.IdUtil;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import com.cf.imes.framework.common.exception.ServiceException;
|
|
||||||
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
|
|
||||||
import com.cf.imes.framework.id.core.util.SnowflakeSerialNoWorker3rd;
|
|
||||||
import com.cf.imes.framework.pay.config.ChenfengPayConfig;
|
|
||||||
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
|
|
||||||
import com.cf.imes.module.system.controller.admin.funds.delay.vo.PreviouProductDelayRespVO;
|
|
||||||
import com.cf.imes.module.system.controller.admin.funds.delay.vo.ProductDelayRespVO;
|
|
||||||
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductProcessorRespVO;
|
|
||||||
import com.cf.imes.module.system.dal.dataobject.funds.organamount.OrganAmountDO;
|
|
||||||
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
|
|
||||||
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDetailDO;
|
|
||||||
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
|
|
||||||
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
|
|
||||||
import com.cf.imes.module.system.dal.dataobject.pay.PayOrderDO;
|
|
||||||
import com.cf.imes.module.system.enums.pay.IncomeExpenseTypeEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.InvoiceStatusEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.PayConstants;
|
|
||||||
import com.cf.imes.module.system.enums.pay.PayOrderStatusEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.PurchaseRecordStatusEnum;
|
|
||||||
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
|
||||||
import com.cf.imes.module.system.framework.snowflake.config.MybatisIdProperties;
|
|
||||||
import com.cf.imes.module.system.service.pay.dto.DateIntervalDTO;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_PRODUCT_AVAILABLE_BALANCE_INSUFFICIENT;
|
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_PRODUCT_BALANCE_INSUFFICIENT;
|
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_PRODUCT_GIFT_BALANCE_INSUFFICIENT;
|
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT;
|
|
||||||
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCT_PURCHASE_ENDTIME_BEFORE_DELAYTIME_ERROR;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 产品购买类型处理器
|
|
||||||
*
|
|
||||||
* @author Gqr
|
|
||||||
* @since 2025/8/14 10:54
|
|
||||||
*/
|
|
||||||
public class PayProductProcessor {
|
|
||||||
|
|
||||||
// 当次订购的产品
|
|
||||||
private ProductsDO productsDO;
|
|
||||||
|
|
||||||
// 当次订购的定价规则
|
|
||||||
private ProductsDetailDO productsDetailDO;
|
|
||||||
|
|
||||||
// 组织产品的首次订购记录
|
|
||||||
private PurchaseRecordDO orgInitialPurchaseRecord;
|
|
||||||
|
|
||||||
// 当前组织资金信息
|
|
||||||
private OrganAmountDO organAmountDO;
|
|
||||||
|
|
||||||
// 组织id
|
|
||||||
private Long organId;
|
|
||||||
|
|
||||||
// 支付配置
|
|
||||||
private ChenfengPayConfig payConfig;
|
|
||||||
|
|
||||||
// id生成规则配置
|
|
||||||
private MybatisIdProperties mybatisIdProperties;
|
|
||||||
|
|
||||||
// id生成器
|
|
||||||
private SnowflakeSerialNoWorker3rd snowflakeSerialNoWorker3rd;
|
|
||||||
|
|
||||||
// 标记本次订购是否首次
|
|
||||||
private boolean initialized = false;
|
|
||||||
|
|
||||||
// 前序购买记录的延期记录
|
|
||||||
private List<ProductDelayRespVO> delayRespVOList;
|
|
||||||
|
|
||||||
// 前序购买记录
|
|
||||||
private PurchaseRecordDO previousPurchaseRecord;
|
|
||||||
|
|
||||||
// 本地订购产品的开始时间
|
|
||||||
private LocalDate startTime = LocalDate.now();
|
|
||||||
|
|
||||||
// 产品结束时间
|
|
||||||
private LocalDate endTime;
|
|
||||||
|
|
||||||
// 本地订购产品的月均单价
|
|
||||||
private BigDecimal monthAverageAmount = BigDecimal.ZERO;
|
|
||||||
|
|
||||||
// 原始价格
|
|
||||||
private BigDecimal originalPrice;
|
|
||||||
|
|
||||||
public PayProductProcessor(ProductsDO productsDO, ProductsDetailDO productsDetailDO, PurchaseRecordDO orgInitialPurchaseRecord, PreviouProductDelayRespVO previouProductDelayRespVO,
|
|
||||||
OrganAmountDO organAmountDO, Long organId, ChenfengPayConfig payConfig, MybatisIdProperties mybatisIdProperties, SnowflakeSerialNoWorker3rd snowflakeSerialNoWorker3rd) {
|
|
||||||
this.productsDO = productsDO;
|
|
||||||
this.productsDetailDO = productsDetailDO;
|
|
||||||
this.orgInitialPurchaseRecord = orgInitialPurchaseRecord;
|
|
||||||
if (ObjectUtil.isNotNull(previouProductDelayRespVO.getPreviouPurchaseRecord())) {
|
|
||||||
delayRespVOList = previouProductDelayRespVO.getDelayRespVOList();
|
|
||||||
previousPurchaseRecord = previouProductDelayRespVO.getPreviouPurchaseRecord();
|
|
||||||
// 上一次订购产品的结束时间进行计算
|
|
||||||
endTime = previousPurchaseRecord.getEndTime();
|
|
||||||
endTime = calculateEndTime(productsDetailDO.getDuration(), productsDetailDO.getDurationUnit());
|
|
||||||
} else {
|
|
||||||
// 首次订购正常从今天开始累加算世界时间
|
|
||||||
endTime = addTime(startTime, productsDetailDO.getDuration(), productsDetailDO.getDurationUnit());
|
|
||||||
}
|
|
||||||
this.organAmountDO = organAmountDO;
|
|
||||||
this.organId = organId;
|
|
||||||
this.payConfig = payConfig;
|
|
||||||
this.mybatisIdProperties = mybatisIdProperties;
|
|
||||||
this.snowflakeSerialNoWorker3rd = snowflakeSerialNoWorker3rd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据产品支付类型解析出对应的信息,
|
|
||||||
*
|
|
||||||
* @param payType
|
|
||||||
*/
|
|
||||||
public ProductProcessorRespVO processPayment(PayProductChannelCodeEnum payType) {
|
|
||||||
// 首次购买记录存在则需要做计算,否则直接用定价规则的现价
|
|
||||||
boolean initialPurchaseExist = ObjectUtil.isNotNull(orgInitialPurchaseRecord);
|
|
||||||
BigDecimal price;
|
|
||||||
if (initialPurchaseExist) {
|
|
||||||
price = computePrice();
|
|
||||||
} else {
|
|
||||||
price = computeInitialProductPayAmount();
|
|
||||||
// 首次购买记录是空,那么当前就是首次订购
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
// 当前组织的余额
|
|
||||||
BigDecimal balance = organAmountDO.getAmount();
|
|
||||||
// 当前组织的赠送金
|
|
||||||
BigDecimal gift = organAmountDO.getGiftAmount();
|
|
||||||
|
|
||||||
switch (payType) {
|
|
||||||
case BALANCE_ONLY:
|
|
||||||
return payByBalance(price, balance);
|
|
||||||
case GIFT_ONLY:
|
|
||||||
return payByGift(price, gift);
|
|
||||||
case ALIPAY_PC:
|
|
||||||
return payByAlipay(price);
|
|
||||||
case WECHAT_NATIVE:
|
|
||||||
return payByWechat(price);
|
|
||||||
case BALANCE_AND_GIFT:
|
|
||||||
return payByBalanceAndGift(price, gift, balance);
|
|
||||||
case BALANCE_AND_ALIPAY:
|
|
||||||
return payByBalanceAndExternal(price, balance, PayChannelCodeEnum.ALIPAY_PC);
|
|
||||||
case BALANCE_AND_WECHAT:
|
|
||||||
return payByBalanceAndExternal(price, balance, PayChannelCodeEnum.WECHAT_NATIVE);
|
|
||||||
case GIFT_AND_ALIPAY:
|
|
||||||
return payByGiftAndExternal(price, gift, PayChannelCodeEnum.ALIPAY_PC);
|
|
||||||
case GIFT_AND_WECHAT:
|
|
||||||
return payByGiftAndExternal(price, gift, PayChannelCodeEnum.WECHAT_NATIVE);
|
|
||||||
case BALANCE_AND_GIFT_AND_ALIPAY:
|
|
||||||
return payByBalanceAndGiftAndExternal(price, gift, balance, PayChannelCodeEnum.ALIPAY_PC);
|
|
||||||
case BALANCE_AND_GIFT_AND_WECHAT:
|
|
||||||
return payByBalanceAndGiftAndExternal(price, gift, balance, PayChannelCodeEnum.WECHAT_NATIVE);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订购产品的到期时间
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public LocalDate getProductPayExpireTime() {
|
|
||||||
return endTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用现金余额支付
|
|
||||||
*
|
|
||||||
* @param price
|
|
||||||
* @param balance
|
|
||||||
*/
|
|
||||||
private ProductProcessorRespVO payByBalance(BigDecimal price, BigDecimal balance) {
|
|
||||||
if (balance.compareTo(price) < 0) {
|
|
||||||
throw new ServiceException(PAY_PRODUCT_BALANCE_INSUFFICIENT);
|
|
||||||
}
|
|
||||||
// 新增购买记录
|
|
||||||
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
|
||||||
String orderNo = String.format(PayConstants.PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT, purchaseId);
|
|
||||||
PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
|
|
||||||
.id(purchaseId)
|
|
||||||
.organId(organId)
|
|
||||||
.productId(productsDO.getId())
|
|
||||||
.productName(productsDO.getProductName())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.productDetailsId(productsDetailDO.getId())
|
|
||||||
.purchaseDuration(productsDetailDO.getDuration())
|
|
||||||
.purchaseDurationUnit(productsDetailDO.getDurationUnit())
|
|
||||||
.startTime(startTime)
|
|
||||||
.endTime(endTime)
|
|
||||||
.paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode())
|
|
||||||
.totalAmount(price)
|
|
||||||
.originalAmount(originalPrice)
|
|
||||||
.monthAverageAmount(monthAverageAmount)
|
|
||||||
.accountBalanceSpent(price)
|
|
||||||
.isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
|
|
||||||
.initial(initialized)
|
|
||||||
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
|
|
||||||
.build();
|
|
||||||
// 记录前序购买记录id
|
|
||||||
if (ObjectUtil.isNotNull(previousPurchaseRecord)) {
|
|
||||||
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
|
|
||||||
}
|
|
||||||
// 新增收支明细
|
|
||||||
// 现金余额减去购买金额
|
|
||||||
BigDecimal newBalance = balance.subtract(price);
|
|
||||||
BigDecimal giftAmount = organAmountDO.getGiftAmount();
|
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
|
||||||
.organId(organId)
|
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.purchaseId(purchaseRecordDO.getId())
|
|
||||||
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
|
||||||
.incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
|
|
||||||
.cashAmountChange(price)
|
|
||||||
.availableAmount(newBalance.add(giftAmount))
|
|
||||||
.amount(newBalance)
|
|
||||||
.giftAmount(giftAmount)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 更新余额
|
|
||||||
organAmountDO.setAmount(newBalance);
|
|
||||||
organAmountDO.setGiftAmount(giftAmount);
|
|
||||||
return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, null, organAmountDO, delayRespVOList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用赠送金余额支付
|
|
||||||
*
|
|
||||||
* @param price
|
|
||||||
* @param gift
|
|
||||||
*/
|
|
||||||
private ProductProcessorRespVO payByGift(BigDecimal price, BigDecimal gift) {
|
|
||||||
if (gift.compareTo(price) < 0) {
|
|
||||||
throw new ServiceException(PAY_PRODUCT_GIFT_BALANCE_INSUFFICIENT);
|
|
||||||
}
|
|
||||||
// 新增购买记录
|
|
||||||
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
|
||||||
String orderNo = String.format(PayConstants.PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT, purchaseId);
|
|
||||||
PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
|
|
||||||
.id(purchaseId)
|
|
||||||
.organId(organId)
|
|
||||||
.productId(productsDO.getId())
|
|
||||||
.productName(productsDO.getProductName())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.productDetailsId(productsDetailDO.getId())
|
|
||||||
.purchaseDuration(productsDetailDO.getDuration())
|
|
||||||
.purchaseDurationUnit(productsDetailDO.getDurationUnit())
|
|
||||||
.startTime(startTime)
|
|
||||||
.endTime(endTime)
|
|
||||||
.paymentMethod(PayProductChannelCodeEnum.GIFT_ONLY.getCode())
|
|
||||||
.totalAmount(price)
|
|
||||||
.originalAmount(originalPrice)
|
|
||||||
.monthAverageAmount(monthAverageAmount)
|
|
||||||
.accountBalanceSpent(price)
|
|
||||||
.isInvocing(null)
|
|
||||||
.initial(initialized)
|
|
||||||
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
|
|
||||||
.build();
|
|
||||||
// 记录前序购买记录id
|
|
||||||
if (ObjectUtil.isNotNull(previousPurchaseRecord)) {
|
|
||||||
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
|
|
||||||
}
|
|
||||||
// 新增收支明细
|
|
||||||
// 赠送金余额减去购买金额
|
|
||||||
BigDecimal newGift = gift.subtract(price);
|
|
||||||
BigDecimal amount = organAmountDO.getAmount();
|
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
|
||||||
.organId(organId)
|
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.purchaseId(purchaseRecordDO.getId())
|
|
||||||
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
|
||||||
.incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
|
|
||||||
.giftAmountChange(price)
|
|
||||||
.availableAmount(amount.add(newGift))
|
|
||||||
.amount(amount)
|
|
||||||
.giftAmount(newGift)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 更新余额
|
|
||||||
organAmountDO.setAmount(amount);
|
|
||||||
organAmountDO.setGiftAmount(newGift);
|
|
||||||
return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, null, organAmountDO, delayRespVOList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用支付宝支付
|
|
||||||
*
|
|
||||||
* @param price
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private ProductProcessorRespVO payByAlipay(BigDecimal price) {
|
|
||||||
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
|
||||||
// 新增购买记录
|
|
||||||
PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
|
|
||||||
.id(purchaseId)
|
|
||||||
.organId(organId)
|
|
||||||
.productId(productsDO.getId())
|
|
||||||
.productName(productsDO.getProductName())
|
|
||||||
.productDetailsId(productsDetailDO.getId())
|
|
||||||
.purchaseDuration(productsDetailDO.getDuration())
|
|
||||||
.purchaseDurationUnit(productsDetailDO.getDurationUnit())
|
|
||||||
.startTime(startTime)
|
|
||||||
.endTime(endTime)
|
|
||||||
.paymentMethod(PayProductChannelCodeEnum.ALIPAY_PC.getCode())
|
|
||||||
.totalAmount(price)
|
|
||||||
.originalAmount(originalPrice)
|
|
||||||
.monthAverageAmount(monthAverageAmount)
|
|
||||||
.externalSpent(price)
|
|
||||||
.initial(initialized)
|
|
||||||
.status(PurchaseRecordStatusEnum.PENDING.getStatus())
|
|
||||||
.build();
|
|
||||||
// 记录前序购买记录id
|
|
||||||
if (ObjectUtil.isNotNull(previousPurchaseRecord)) {
|
|
||||||
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增第三方支付订单
|
|
||||||
PayOrderDO payOrderDO = goExternalPay(purchaseId, price, PayChannelCodeEnum.ALIPAY_PC);
|
|
||||||
return new ProductProcessorRespVO(purchaseRecordDO, null, payOrderDO, null, delayRespVOList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用微信支付
|
|
||||||
*
|
|
||||||
* @param price
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private ProductProcessorRespVO payByWechat(BigDecimal price) {
|
|
||||||
// 新增购买记录
|
|
||||||
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
|
||||||
PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
|
|
||||||
.id(purchaseId)
|
|
||||||
.organId(organId)
|
|
||||||
.productId(productsDO.getId())
|
|
||||||
.productName(productsDO.getProductName())
|
|
||||||
.productDetailsId(productsDetailDO.getId())
|
|
||||||
.purchaseDuration(productsDetailDO.getDuration())
|
|
||||||
.purchaseDurationUnit(productsDetailDO.getDurationUnit())
|
|
||||||
.startTime(startTime)
|
|
||||||
.endTime(endTime)
|
|
||||||
.paymentMethod(PayProductChannelCodeEnum.WECHAT_NATIVE.getCode())
|
|
||||||
.totalAmount(price)
|
|
||||||
.originalAmount(originalPrice)
|
|
||||||
.monthAverageAmount(monthAverageAmount)
|
|
||||||
.externalSpent(price)
|
|
||||||
.initial(initialized)
|
|
||||||
.status(PurchaseRecordStatusEnum.PENDING.getStatus())
|
|
||||||
.build();
|
|
||||||
// 记录前序购买记录id
|
|
||||||
if (ObjectUtil.isNotNull(previousPurchaseRecord)) {
|
|
||||||
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增第三方支付订单
|
|
||||||
PayOrderDO payOrderDO = goExternalPay(purchaseId, price, PayChannelCodeEnum.WECHAT_NATIVE);
|
|
||||||
return new ProductProcessorRespVO(purchaseRecordDO, null, payOrderDO, null, delayRespVOList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用现在余额+赠送金余额支付
|
|
||||||
*
|
|
||||||
* @param price
|
|
||||||
* @param gift
|
|
||||||
* @param balance
|
|
||||||
*/
|
|
||||||
private ProductProcessorRespVO payByBalanceAndGift(BigDecimal price, BigDecimal gift, BigDecimal balance) {
|
|
||||||
BigDecimal remaining = price;
|
|
||||||
BigDecimal usedGift = BigDecimal.ZERO;
|
|
||||||
BigDecimal usedBalance = BigDecimal.ZERO;
|
|
||||||
|
|
||||||
// 新增购买记录
|
|
||||||
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
|
||||||
String orderNo = String.format(PayConstants.PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT, purchaseId);
|
|
||||||
PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
|
|
||||||
.id(purchaseId)
|
|
||||||
.organId(organId)
|
|
||||||
.productId(productsDO.getId())
|
|
||||||
.productName(productsDO.getProductName())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.productDetailsId(productsDetailDO.getId())
|
|
||||||
.purchaseDuration(productsDetailDO.getDuration())
|
|
||||||
.purchaseDurationUnit(productsDetailDO.getDurationUnit())
|
|
||||||
.startTime(startTime)
|
|
||||||
.endTime(endTime)
|
|
||||||
.paymentMethod(PayProductChannelCodeEnum.BALANCE_AND_GIFT.getCode())
|
|
||||||
.totalAmount(price)
|
|
||||||
.originalAmount(originalPrice)
|
|
||||||
.monthAverageAmount(monthAverageAmount)
|
|
||||||
.isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
|
|
||||||
.initial(initialized)
|
|
||||||
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
|
|
||||||
.build();
|
|
||||||
// 记录前序购买记录id
|
|
||||||
if (ObjectUtil.isNotNull(previousPurchaseRecord)) {
|
|
||||||
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否只用了赠送金
|
|
||||||
boolean justPayGift = true;
|
|
||||||
// 先用赠送金
|
|
||||||
if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
usedGift = remaining.min(gift);
|
|
||||||
remaining = remaining.subtract(usedGift);
|
|
||||||
purchaseRecordDO.setGiftMoneySpent(usedGift);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 再用余额
|
|
||||||
if (remaining.compareTo(BigDecimal.ZERO) > 0 && balance != null && balance.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
usedBalance = remaining.min(balance);
|
|
||||||
remaining = remaining.subtract(usedBalance);
|
|
||||||
purchaseRecordDO.setAccountBalanceSpent(usedBalance);
|
|
||||||
justPayGift = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 剩下走第三方
|
|
||||||
if (remaining.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
throw new ServiceException(PAY_PRODUCT_AVAILABLE_BALANCE_INSUFFICIENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增收支明细
|
|
||||||
// 减去用掉的得到剩下的
|
|
||||||
BigDecimal newAmounta = balance.subtract(usedBalance);
|
|
||||||
BigDecimal newGift = gift.subtract(usedGift);
|
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
|
||||||
.organId(organId)
|
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.purchaseId(purchaseRecordDO.getId())
|
|
||||||
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
|
||||||
.incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
|
|
||||||
.cashAmountChange(usedBalance)
|
|
||||||
.giftAmountChange(usedGift)
|
|
||||||
.availableAmount(newAmounta.add(newGift))
|
|
||||||
.amount(newAmounta)
|
|
||||||
.giftAmount(newGift)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 如果只用赠送金那么不可开票
|
|
||||||
if (justPayGift) {
|
|
||||||
purchaseRecordDO.setIsInvocing(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新余额
|
|
||||||
organAmountDO.setAmount(newAmounta);
|
|
||||||
organAmountDO.setGiftAmount(newGift);
|
|
||||||
return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, null, organAmountDO, delayRespVOList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用现金余额+第三方
|
|
||||||
*
|
|
||||||
* @param price
|
|
||||||
* @param balance
|
|
||||||
*/
|
|
||||||
private ProductProcessorRespVO payByBalanceAndExternal(BigDecimal price, BigDecimal balance, PayChannelCodeEnum payChannelCodeEnum) {
|
|
||||||
BigDecimal remaining = price;
|
|
||||||
BigDecimal usedBalance = BigDecimal.ZERO;
|
|
||||||
|
|
||||||
// 新增购买记录
|
|
||||||
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
|
||||||
String orderNo = String.format(PayConstants.PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT, purchaseId);
|
|
||||||
PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
|
|
||||||
.id(purchaseId)
|
|
||||||
.organId(organId)
|
|
||||||
.productId(productsDO.getId())
|
|
||||||
.productName(productsDO.getProductName())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.productDetailsId(productsDetailDO.getId())
|
|
||||||
.purchaseDuration(productsDetailDO.getDuration())
|
|
||||||
.purchaseDurationUnit(productsDetailDO.getDurationUnit())
|
|
||||||
.startTime(startTime)
|
|
||||||
.endTime(endTime)
|
|
||||||
.paymentMethod(PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum) ? PayProductChannelCodeEnum.BALANCE_AND_ALIPAY.getCode() : PayProductChannelCodeEnum.BALANCE_AND_WECHAT.getCode())
|
|
||||||
.totalAmount(price)
|
|
||||||
.originalAmount(originalPrice)
|
|
||||||
.monthAverageAmount(monthAverageAmount)
|
|
||||||
.initial(initialized)
|
|
||||||
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
|
|
||||||
.build();
|
|
||||||
// 记录前序购买记录id
|
|
||||||
if (ObjectUtil.isNotNull(previousPurchaseRecord)) {
|
|
||||||
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 先用余额
|
|
||||||
if (balance != null && balance.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
usedBalance = remaining.min(balance);
|
|
||||||
remaining = remaining.subtract(usedBalance);
|
|
||||||
purchaseRecordDO.setAccountBalanceSpent(usedBalance);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增收支明细
|
|
||||||
// 减去用掉的得到剩下的
|
|
||||||
BigDecimal newAmounta = balance.subtract(usedBalance);
|
|
||||||
BigDecimal giftAmount = organAmountDO.getGiftAmount();
|
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
|
||||||
.organId(organId)
|
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.purchaseId(purchaseRecordDO.getId())
|
|
||||||
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
|
||||||
.incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
|
|
||||||
.cashAmountChange(usedBalance)
|
|
||||||
.availableAmount(newAmounta.add(giftAmount))
|
|
||||||
.amount(newAmounta)
|
|
||||||
.giftAmount(giftAmount)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 剩下走第三方
|
|
||||||
PayOrderDO payOrderDO = null;
|
|
||||||
if (remaining.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
payOrderDO = goExternalPay(purchaseId, remaining, payChannelCodeEnum);
|
|
||||||
purchaseRecordDO.setExternalSpent(remaining);
|
|
||||||
purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新余额
|
|
||||||
organAmountDO.setAmount(newAmounta);
|
|
||||||
organAmountDO.setGiftAmount(giftAmount);
|
|
||||||
return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, payOrderDO, organAmountDO, delayRespVOList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用赠送金余额+第三方
|
|
||||||
*
|
|
||||||
* @param price
|
|
||||||
* @param gift
|
|
||||||
*/
|
|
||||||
private ProductProcessorRespVO payByGiftAndExternal(BigDecimal price, BigDecimal gift, PayChannelCodeEnum payChannelCodeEnum) {
|
|
||||||
BigDecimal remaining = price;
|
|
||||||
BigDecimal usedGift = BigDecimal.ZERO;
|
|
||||||
|
|
||||||
// 新增购买记录
|
|
||||||
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
|
||||||
String orderNo = String.format(PayConstants.PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT, purchaseId);
|
|
||||||
PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
|
|
||||||
.id(purchaseId)
|
|
||||||
.organId(organId)
|
|
||||||
.productId(productsDO.getId())
|
|
||||||
.productName(productsDO.getProductName())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.productDetailsId(productsDetailDO.getId())
|
|
||||||
.purchaseDuration(productsDetailDO.getDuration())
|
|
||||||
.purchaseDurationUnit(productsDetailDO.getDurationUnit())
|
|
||||||
.startTime(startTime)
|
|
||||||
.endTime(endTime)
|
|
||||||
.paymentMethod(PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum) ? PayProductChannelCodeEnum.GIFT_AND_ALIPAY.getCode() : PayProductChannelCodeEnum.GIFT_AND_WECHAT.getCode())
|
|
||||||
.totalAmount(price)
|
|
||||||
.originalAmount(originalPrice)
|
|
||||||
.monthAverageAmount(monthAverageAmount)
|
|
||||||
.initial(initialized)
|
|
||||||
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
|
|
||||||
.build();
|
|
||||||
// 记录前序购买记录id
|
|
||||||
if (ObjectUtil.isNotNull(previousPurchaseRecord)) {
|
|
||||||
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否只用赠送金
|
|
||||||
boolean justPayGift = true;
|
|
||||||
// 先用赠送金
|
|
||||||
if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
usedGift = remaining.min(gift);
|
|
||||||
remaining = remaining.subtract(usedGift);
|
|
||||||
purchaseRecordDO.setGiftMoneySpent(usedGift);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增收支明细
|
|
||||||
// 减去用掉的得到剩下的
|
|
||||||
BigDecimal newGift = gift.subtract(usedGift);
|
|
||||||
BigDecimal amount = organAmountDO.getAmount();
|
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
|
||||||
.organId(organId)
|
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.purchaseId(purchaseId)
|
|
||||||
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
|
||||||
.incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
|
|
||||||
.giftAmountChange(usedGift)
|
|
||||||
.availableAmount(amount.add(newGift))
|
|
||||||
.amount(amount)
|
|
||||||
.giftAmount(newGift)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 剩下走第三方
|
|
||||||
PayOrderDO payOrderDO = null;
|
|
||||||
if (remaining.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
payOrderDO = goExternalPay(purchaseId, remaining, payChannelCodeEnum);
|
|
||||||
purchaseRecordDO.setExternalSpent(remaining);
|
|
||||||
purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus());
|
|
||||||
justPayGift = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果只用赠送金那么不可开票
|
|
||||||
if (justPayGift) {
|
|
||||||
purchaseRecordDO.setIsInvocing(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新余额
|
|
||||||
organAmountDO.setAmount(amount);
|
|
||||||
organAmountDO.setGiftAmount(newGift);
|
|
||||||
return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, payOrderDO, organAmountDO, delayRespVOList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用现在余额+赠送金余额支付
|
|
||||||
*
|
|
||||||
* @param price
|
|
||||||
* @param gift
|
|
||||||
* @param balance
|
|
||||||
*/
|
|
||||||
private ProductProcessorRespVO payByBalanceAndGiftAndExternal(BigDecimal price, BigDecimal gift, BigDecimal balance, PayChannelCodeEnum payChannelCodeEnum) {
|
|
||||||
BigDecimal remaining = price;
|
|
||||||
BigDecimal usedGift = BigDecimal.ZERO;
|
|
||||||
BigDecimal usedBalance = BigDecimal.ZERO;
|
|
||||||
|
|
||||||
// 新增购买记录
|
|
||||||
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
|
||||||
String orderNo = String.format(PayConstants.PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT, purchaseId);
|
|
||||||
PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
|
|
||||||
.id(purchaseId)
|
|
||||||
.organId(organId)
|
|
||||||
.productId(productsDO.getId())
|
|
||||||
.productName(productsDO.getProductName())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.productDetailsId(productsDetailDO.getId())
|
|
||||||
.purchaseDuration(productsDetailDO.getDuration())
|
|
||||||
.purchaseDurationUnit(productsDetailDO.getDurationUnit())
|
|
||||||
.startTime(startTime)
|
|
||||||
.endTime(endTime)
|
|
||||||
.paymentMethod(PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum) ? PayProductChannelCodeEnum.BALANCE_AND_GIFT_AND_ALIPAY.getCode() : PayProductChannelCodeEnum.BALANCE_AND_GIFT_AND_WECHAT.getCode())
|
|
||||||
.totalAmount(price)
|
|
||||||
.originalAmount(originalPrice)
|
|
||||||
.monthAverageAmount(monthAverageAmount)
|
|
||||||
.initial(initialized)
|
|
||||||
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
|
|
||||||
.build();
|
|
||||||
// 记录前序购买记录id
|
|
||||||
if (ObjectUtil.isNotNull(previousPurchaseRecord)) {
|
|
||||||
purchaseRecordDO.setPreviousId(previousPurchaseRecord.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否只用了赠送金
|
|
||||||
boolean justPayGift = true;
|
|
||||||
// 先用赠送金
|
|
||||||
if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
usedGift = remaining.min(gift);
|
|
||||||
remaining = remaining.subtract(usedGift);
|
|
||||||
purchaseRecordDO.setGiftMoneySpent(usedGift);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 再用余额
|
|
||||||
if (remaining.compareTo(BigDecimal.ZERO) > 0 && balance != null && balance.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
usedBalance = remaining.min(balance);
|
|
||||||
remaining = remaining.subtract(usedBalance);
|
|
||||||
purchaseRecordDO.setAccountBalanceSpent(usedBalance);
|
|
||||||
justPayGift = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增收支明细
|
|
||||||
// 减去用掉的得到剩下的
|
|
||||||
BigDecimal newAmounta = balance.subtract(usedBalance);
|
|
||||||
BigDecimal newGift = gift.subtract(usedGift);
|
|
||||||
IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
|
||||||
.organId(organId)
|
|
||||||
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
|
||||||
.orderNo(orderNo)
|
|
||||||
.purchaseId(purchaseId)
|
|
||||||
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
|
||||||
.incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
|
|
||||||
.cashAmountChange(usedBalance)
|
|
||||||
.giftAmountChange(usedGift)
|
|
||||||
.availableAmount(newAmounta.add(newGift))
|
|
||||||
.amount(newAmounta)
|
|
||||||
.giftAmount(newGift)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 剩下走第三方
|
|
||||||
PayOrderDO payOrderDO = null;
|
|
||||||
if (remaining.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
payOrderDO = goExternalPay(purchaseId, remaining, payChannelCodeEnum);
|
|
||||||
purchaseRecordDO.setExternalSpent(remaining);
|
|
||||||
purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus());
|
|
||||||
justPayGift = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果只用赠送金那么不可开票
|
|
||||||
if (justPayGift) {
|
|
||||||
purchaseRecordDO.setIsInvocing(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新余额
|
|
||||||
organAmountDO.setAmount(newAmounta);
|
|
||||||
organAmountDO.setGiftAmount(newGift);
|
|
||||||
return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, payOrderDO, organAmountDO, delayRespVOList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 外部支付
|
|
||||||
*
|
|
||||||
* @param purchaseId
|
|
||||||
* @param price
|
|
||||||
* @param payChannelCodeEnum
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private PayOrderDO goExternalPay(Long purchaseId, BigDecimal price, PayChannelCodeEnum payChannelCodeEnum) {
|
|
||||||
PayOrderDO payOrderDO = new PayOrderDO();
|
|
||||||
|
|
||||||
Long payOrderId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
|
||||||
Long organId = SecurityFrameworkUtils.getUserOrganId();
|
|
||||||
|
|
||||||
payOrderDO.setId(payOrderId);
|
|
||||||
payOrderDO.setPayNo(String.format(PayConstants.PAY_ORDER_PREFIX_FORMAT, payOrderId));
|
|
||||||
payOrderDO.setStatus(PayOrderStatusEnum.WAITING);
|
|
||||||
payOrderDO.setExpireTime(LocalDateTime.now().plus(payConfig.getPayExpireTime()));
|
|
||||||
payOrderDO.setPrice(price);
|
|
||||||
payOrderDO.setSubject(productsDO.getProductName());
|
|
||||||
payOrderDO.setChannelCode(payChannelCodeEnum);
|
|
||||||
payOrderDO.setOrganId(organId);
|
|
||||||
payOrderDO.setPurchaseId(purchaseId);
|
|
||||||
payOrderDO.setTradeType(TradeTypeEnum.PRODUCT.getCode());
|
|
||||||
return payOrderDO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算结束时间
|
|
||||||
*
|
|
||||||
* @param duration
|
|
||||||
* @param durationUnit
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private LocalDate calculateEndTime(Integer duration, Integer durationUnit) {
|
|
||||||
LocalDate today = LocalDate.now();
|
|
||||||
// 1、 计算延期后的实际有效期
|
|
||||||
LocalDate maxDelayEnd = endTime;
|
|
||||||
if (CollUtil.isNotEmpty(delayRespVOList)) {
|
|
||||||
// 找到延期记录中最大的 delayTime 作为最终有效期
|
|
||||||
maxDelayEnd = delayRespVOList.stream()
|
|
||||||
.map(ProductDelayRespVO::getDelayTime)
|
|
||||||
.max(LocalDate::compareTo)
|
|
||||||
.orElse(endTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2、 计算有效期叠加的起点
|
|
||||||
LocalDate purchaseStart;
|
|
||||||
if (today.isAfter(maxDelayEnd)) {
|
|
||||||
// 已过期 → 从今天开始累加
|
|
||||||
purchaseStart = today;
|
|
||||||
} else {
|
|
||||||
// 未过期 → 从 maxDelayEnd 的下一天开始累加
|
|
||||||
purchaseStart = maxDelayEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3、加正常时间
|
|
||||||
if (ObjectUtil.isNotNull(duration) && durationUnit != null) {
|
|
||||||
endTime = addTime(purchaseStart, duration, durationUnit);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4、扣掉延期天数
|
|
||||||
long delayDays = 0L;
|
|
||||||
if (CollUtil.isNotEmpty(delayRespVOList)) {
|
|
||||||
delayDays = calculateDelayDays(startTime, delayRespVOList);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delayDays > 0) {
|
|
||||||
endTime = endTime.minusDays(delayDays);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endTime.isBefore(today)) {
|
|
||||||
throw ServiceExceptionUtil.exception(PRODUCT_PURCHASE_ENDTIME_BEFORE_DELAYTIME_ERROR, LocalDateTimeUtil.format(endTime, DatePattern.NORM_DATE_PATTERN));
|
|
||||||
}
|
|
||||||
|
|
||||||
return endTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算总延期天数(合并区间,避免重叠/不连续)
|
|
||||||
*/
|
|
||||||
private static long calculateDelayDays(LocalDate startTime, List<ProductDelayRespVO> delayRecords) {
|
|
||||||
// 1. 生成延期区间并打上备注
|
|
||||||
List<DateIntervalDTO> intervals = buildIntervals(startTime, delayRecords);
|
|
||||||
if (CollUtil.isEmpty(intervals)) {
|
|
||||||
return 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 合并区间
|
|
||||||
List<DateIntervalDTO> merged = mergeIntervals(intervals);
|
|
||||||
|
|
||||||
// 3. 计算天数
|
|
||||||
return calculateTotalDays(merged);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据延期记录生成扣减区间,同时写入备注
|
|
||||||
*/
|
|
||||||
private static List<DateIntervalDTO> buildIntervals(LocalDate startTime, List<ProductDelayRespVO> delayRecords) {
|
|
||||||
List<DateIntervalDTO> intervals = new ArrayList<>();
|
|
||||||
|
|
||||||
for (ProductDelayRespVO record : delayRecords) {
|
|
||||||
LocalDate delayStart = record.getEndTime().plusDays(1);
|
|
||||||
LocalDate delayEnd = record.getDelayTime();
|
|
||||||
|
|
||||||
if (delayEnd.isBefore(delayStart)) {
|
|
||||||
record.setRemark("无扣减");
|
|
||||||
continue; // 异常数据,忽略
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalDate intervalEnd;
|
|
||||||
if (!delayEnd.isAfter(startTime)) {
|
|
||||||
// 情况1:延期结束在计算结束之前 → 整段都扣
|
|
||||||
intervalEnd = delayEnd;
|
|
||||||
} else {
|
|
||||||
// 情况2:延期结束晚于计算结束 → 扣到 startTime 为止
|
|
||||||
intervalEnd = startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
long deductDays = ChronoUnit.DAYS.between(delayStart, intervalEnd) + 1;
|
|
||||||
if (deductDays > 0) {
|
|
||||||
record.setRemark("扣减" + deductDays + "天");
|
|
||||||
intervals.add(new DateIntervalDTO(delayStart, intervalEnd));
|
|
||||||
} else {
|
|
||||||
record.setRemark("无扣减");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return intervals;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 合并区间(避免重叠/相邻)
|
|
||||||
*/
|
|
||||||
private static List<DateIntervalDTO> mergeIntervals(List<DateIntervalDTO> intervals) {
|
|
||||||
intervals.sort(Comparator.comparing(DateIntervalDTO::getStart));
|
|
||||||
|
|
||||||
List<DateIntervalDTO> merged = new ArrayList<>();
|
|
||||||
DateIntervalDTO current = intervals.get(0);
|
|
||||||
|
|
||||||
for (int i = 1; i < intervals.size(); i++) {
|
|
||||||
DateIntervalDTO next = intervals.get(i);
|
|
||||||
if (!next.getStart().isAfter(current.getEnd().plusDays(1))) {
|
|
||||||
// 重叠或相邻 → 合并
|
|
||||||
current.setEnd(current.getEnd().isAfter(next.getEnd()) ? current.getEnd() : next.getEnd());
|
|
||||||
} else {
|
|
||||||
merged.add(current);
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
merged.add(current);
|
|
||||||
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算合并区间的总天数
|
|
||||||
*/
|
|
||||||
private static long calculateTotalDays(List<DateIntervalDTO> merged) {
|
|
||||||
long totalDays = 0L;
|
|
||||||
for (DateIntervalDTO interval : merged) {
|
|
||||||
totalDays += ChronoUnit.DAYS.between(interval.getStart(), interval.getEnd()) + 1;
|
|
||||||
}
|
|
||||||
return totalDays;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 基于时长和单位累加time
|
|
||||||
*
|
|
||||||
* @param time
|
|
||||||
* @param duration
|
|
||||||
* @param unit
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static LocalDate addTime(LocalDate time, int duration, int unit) {
|
|
||||||
switch (ProductDurationUnitEnum.fromStatus(unit)) {
|
|
||||||
case YEAR:
|
|
||||||
return time.plusYears(duration);
|
|
||||||
case MONTH:
|
|
||||||
return time.plusMonths(duration);
|
|
||||||
default:
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算有首购记录的产品价格
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private BigDecimal computePrice() {
|
|
||||||
// 月均单价
|
|
||||||
monthAverageAmount = orgInitialPurchaseRecord.getMonthAverageAmount();
|
|
||||||
|
|
||||||
// 首购的定价
|
|
||||||
BigDecimal initialPurchaseRecordTotalAmount = orgInitialPurchaseRecord.getTotalAmount();
|
|
||||||
|
|
||||||
// 定价规则的折扣
|
|
||||||
BigDecimal discount = productsDetailDO.getDiscount();
|
|
||||||
if (ObjectUtil.isNull(discount)) {
|
|
||||||
discount = BigDecimal.valueOf(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigDecimal totalAmount;
|
|
||||||
/**
|
|
||||||
* ①:续费订购满1年:
|
|
||||||
* 原始价格 = 订购总价 * duration
|
|
||||||
* 现价 = 原始价格 * 折扣 /100,保留两位小数
|
|
||||||
* ②:不满1年按月份计算
|
|
||||||
* 原始价格 = 月均单价 * duration
|
|
||||||
* 现价 = 原始价格 * 折扣 /100,保留两位小数
|
|
||||||
*/
|
|
||||||
switch (ProductDurationUnitEnum.fromStatus(productsDetailDO.getDurationUnit())) {
|
|
||||||
case YEAR -> {
|
|
||||||
originalPrice = initialPurchaseRecordTotalAmount
|
|
||||||
.multiply(BigDecimal.valueOf(productsDetailDO.getDuration()));
|
|
||||||
totalAmount = originalPrice
|
|
||||||
.multiply(discount)
|
|
||||||
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
|
||||||
}
|
|
||||||
case MONTH -> {
|
|
||||||
originalPrice = monthAverageAmount
|
|
||||||
.multiply(BigDecimal.valueOf(productsDetailDO.getDuration()));
|
|
||||||
totalAmount = originalPrice
|
|
||||||
.multiply(discount)
|
|
||||||
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
|
||||||
}
|
|
||||||
default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算首次订购的价格
|
|
||||||
*/
|
|
||||||
private BigDecimal computeInitialProductPayAmount() {
|
|
||||||
// 订购数量
|
|
||||||
Integer duration = productsDetailDO.getDuration();
|
|
||||||
// 产品订购价格
|
|
||||||
BigDecimal orgPurchasePrice = productsDO.getPrice();
|
|
||||||
// 折扣
|
|
||||||
BigDecimal discount = productsDetailDO.getDiscount();
|
|
||||||
if (ObjectUtil.isNull(discount)) {
|
|
||||||
discount = BigDecimal.valueOf(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigDecimal totalAmount;
|
|
||||||
switch (ProductDurationUnitEnum.fromStatus(productsDetailDO.getDurationUnit())) {
|
|
||||||
case YEAR -> {
|
|
||||||
// 原价
|
|
||||||
originalPrice = orgPurchasePrice.multiply(BigDecimal.valueOf(duration));
|
|
||||||
// 折扣价格 = 原价 * 折扣
|
|
||||||
totalAmount = originalPrice
|
|
||||||
.multiply(discount)
|
|
||||||
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
|
||||||
// 月均单价 = 折扣价格 / 10
|
|
||||||
monthAverageAmount = originalPrice.divide(new BigDecimal(10), 2, RoundingMode.DOWN);
|
|
||||||
}
|
|
||||||
case MONTH -> {
|
|
||||||
// 月均单价 = 订购价格 / 10
|
|
||||||
monthAverageAmount = orgPurchasePrice.divide(new BigDecimal(10), 2, RoundingMode.DOWN);
|
|
||||||
// 原价
|
|
||||||
originalPrice = monthAverageAmount.multiply(BigDecimal.valueOf(duration));
|
|
||||||
// 折扣价格 = 原价 * 折扣
|
|
||||||
totalAmount = originalPrice.multiply(discount)
|
|
||||||
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
|
||||||
}
|
|
||||||
default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT);
|
|
||||||
}
|
|
||||||
return totalAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+593
@@ -0,0 +1,593 @@
|
|||||||
|
package com.cf.imes.module.system.service.pay;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.cf.imes.framework.common.exception.ServiceException;
|
||||||
|
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
|
||||||
|
import com.cf.imes.framework.id.core.util.SnowflakeSerialNoWorker3rd;
|
||||||
|
import com.cf.imes.framework.pay.config.ChenfengPayConfig;
|
||||||
|
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
|
import com.cf.imes.module.system.controller.admin.funds.delay.vo.PreviouProductDelayRespVO;
|
||||||
|
import com.cf.imes.module.system.controller.admin.funds.delay.vo.ProductDelayRespVO;
|
||||||
|
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductProcessorRespVO;
|
||||||
|
import com.cf.imes.module.system.dal.dataobject.funds.organamount.OrganAmountDO;
|
||||||
|
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
|
||||||
|
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDetailDO;
|
||||||
|
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
|
||||||
|
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
|
||||||
|
import com.cf.imes.module.system.dal.dataobject.pay.PayOrderDO;
|
||||||
|
import com.cf.imes.module.system.enums.pay.IncomeExpenseTypeEnum;
|
||||||
|
import com.cf.imes.module.system.enums.pay.InvoiceStatusEnum;
|
||||||
|
import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum;
|
||||||
|
import com.cf.imes.module.system.enums.pay.PayOrderStatusEnum;
|
||||||
|
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnumNew;
|
||||||
|
import com.cf.imes.module.system.enums.pay.ProductDurationUnitEnum;
|
||||||
|
import com.cf.imes.module.system.enums.pay.PurchaseRecordStatusEnum;
|
||||||
|
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
|
||||||
|
import com.cf.imes.module.system.framework.snowflake.config.MybatisIdProperties;
|
||||||
|
import com.cf.imes.module.system.service.pay.dto.DateIntervalDTO;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_PRODUCT_BALANCE_INSUFFICIENT;
|
||||||
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_PRODUCT_GIFT_BALANCE_INSUFFICIENT;
|
||||||
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT;
|
||||||
|
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCT_PURCHASE_ENDTIME_BEFORE_DELAYTIME_ERROR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产品购买类型处理器
|
||||||
|
*
|
||||||
|
* @author Gqr
|
||||||
|
* @since 2025/8/14 10:54
|
||||||
|
*/
|
||||||
|
public class PayProductProcessorNew {
|
||||||
|
|
||||||
|
// 当次订购的产品
|
||||||
|
private ProductsDO productsDO;
|
||||||
|
|
||||||
|
// 当次订购的定价规则
|
||||||
|
private ProductsDetailDO productsDetailDO;
|
||||||
|
|
||||||
|
// 组织产品的首次订购记录
|
||||||
|
private PurchaseRecordDO orgInitialPurchaseRecord;
|
||||||
|
|
||||||
|
// 当前组织资金信息
|
||||||
|
private OrganAmountDO organAmountDO;
|
||||||
|
|
||||||
|
// 组织id
|
||||||
|
private Long organId;
|
||||||
|
|
||||||
|
// 支付配置
|
||||||
|
private ChenfengPayConfig payConfig;
|
||||||
|
|
||||||
|
// id生成规则配置
|
||||||
|
private MybatisIdProperties mybatisIdProperties;
|
||||||
|
|
||||||
|
// id生成器
|
||||||
|
private SnowflakeSerialNoWorker3rd snowflakeSerialNoWorker3rd;
|
||||||
|
|
||||||
|
// 标记本次订购是否首次
|
||||||
|
private boolean initialized = false;
|
||||||
|
|
||||||
|
// 前序购买记录的延期记录
|
||||||
|
private List<ProductDelayRespVO> delayRespVOList;
|
||||||
|
|
||||||
|
// 前序购买记录
|
||||||
|
private PurchaseRecordDO previousPurchaseRecord;
|
||||||
|
|
||||||
|
// 本地订购产品的开始时间
|
||||||
|
private LocalDate startTime = LocalDate.now();
|
||||||
|
|
||||||
|
// 产品结束时间
|
||||||
|
private LocalDate endTime;
|
||||||
|
|
||||||
|
// 本地订购产品的月均单价
|
||||||
|
private BigDecimal monthAverageAmount = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
// 原始价格
|
||||||
|
private BigDecimal originalPrice;
|
||||||
|
|
||||||
|
// 前序购买记录的id
|
||||||
|
private Long previousPurchaseRecordId;
|
||||||
|
|
||||||
|
// 产品支付价格
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
// 组织赠送金
|
||||||
|
private BigDecimal gift;
|
||||||
|
|
||||||
|
// 组织现金
|
||||||
|
private BigDecimal balance;
|
||||||
|
|
||||||
|
// 使用赠送金
|
||||||
|
private BigDecimal usedGift;
|
||||||
|
|
||||||
|
// 使用余额
|
||||||
|
private BigDecimal usedBalance;
|
||||||
|
|
||||||
|
// 支付类型
|
||||||
|
private Integer payType;
|
||||||
|
|
||||||
|
// 当次购买记录DO
|
||||||
|
private PurchaseRecordDO purchaseRecordDO;
|
||||||
|
|
||||||
|
// 当次收支记录DO
|
||||||
|
private IncomeExpenseDetailsDO incomeExpenseDetailsDO;
|
||||||
|
|
||||||
|
// 当次支付订单DO
|
||||||
|
private PayOrderDO payOrderDO;
|
||||||
|
|
||||||
|
// 是否只用了赠送金
|
||||||
|
private boolean justPayGift = false;
|
||||||
|
|
||||||
|
public PayProductProcessorNew(ProductsDO productsDO, ProductsDetailDO productsDetailDO, PurchaseRecordDO orgInitialPurchaseRecord, PreviouProductDelayRespVO previouProductDelayRespVO,
|
||||||
|
OrganAmountDO organAmountDO, Long organId, ChenfengPayConfig payConfig, MybatisIdProperties mybatisIdProperties, SnowflakeSerialNoWorker3rd snowflakeSerialNoWorker3rd) {
|
||||||
|
this.productsDO = productsDO;
|
||||||
|
this.productsDetailDO = productsDetailDO;
|
||||||
|
this.orgInitialPurchaseRecord = orgInitialPurchaseRecord;
|
||||||
|
if (ObjectUtil.isNotNull(previouProductDelayRespVO.getPreviouPurchaseRecord())) {
|
||||||
|
delayRespVOList = previouProductDelayRespVO.getDelayRespVOList();
|
||||||
|
previousPurchaseRecord = previouProductDelayRespVO.getPreviouPurchaseRecord();
|
||||||
|
previousPurchaseRecordId = previousPurchaseRecord.getId();
|
||||||
|
// 上一次订购产品的结束时间进行计算
|
||||||
|
endTime = previousPurchaseRecord.getEndTime();
|
||||||
|
endTime = calculateEndTime(productsDetailDO.getDuration(), productsDetailDO.getDurationUnit());
|
||||||
|
} else {
|
||||||
|
// 首次订购正常从今天开始累加算世界时间
|
||||||
|
endTime = addTime(startTime, productsDetailDO.getDuration(), productsDetailDO.getDurationUnit());
|
||||||
|
}
|
||||||
|
this.organAmountDO = organAmountDO;
|
||||||
|
this.organId = organId;
|
||||||
|
this.payConfig = payConfig;
|
||||||
|
this.mybatisIdProperties = mybatisIdProperties;
|
||||||
|
this.snowflakeSerialNoWorker3rd = snowflakeSerialNoWorker3rd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据产品支付类型解析出对应的信息,
|
||||||
|
*
|
||||||
|
* @param payType
|
||||||
|
*/
|
||||||
|
public ProductProcessorRespVO processPayment(Integer payType) {
|
||||||
|
// 首次购买记录存在则需要做计算,否则直接用定价规则的现价
|
||||||
|
boolean initialPurchaseExist = ObjectUtil.isNotNull(orgInitialPurchaseRecord);
|
||||||
|
if (initialPurchaseExist) {
|
||||||
|
price = computePrice();
|
||||||
|
} else {
|
||||||
|
price = computeInitialProductPayAmount();
|
||||||
|
// 首次购买记录是空,那么当前就是首次订购
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
// 当前组织的余额
|
||||||
|
balance = organAmountDO.getAmount();
|
||||||
|
// 当前组织的赠送金
|
||||||
|
gift = organAmountDO.getGiftAmount();
|
||||||
|
this.payType = payType;
|
||||||
|
|
||||||
|
// 按序扣款
|
||||||
|
deduction();
|
||||||
|
|
||||||
|
// 根据扣款情况生成购买记录和收支记录
|
||||||
|
generatePurchaseRecordAndIncomeRecord();
|
||||||
|
|
||||||
|
return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, payOrderDO, organAmountDO, delayRespVOList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按顺序扣款
|
||||||
|
*/
|
||||||
|
private void deduction(){
|
||||||
|
// 有赠送金先扣赠送金
|
||||||
|
if (PayProductChannelCodeEnumNew.hasGift(payType)) {
|
||||||
|
payByGift();
|
||||||
|
if (price.compareTo(BigDecimal.ZERO) == 0) {
|
||||||
|
// 赠送金直接扣完了,开出的购买记录为不可开票
|
||||||
|
justPayGift = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有现金扣现金
|
||||||
|
if(PayProductChannelCodeEnumNew.hasBalance(payType)) {
|
||||||
|
payByBalance();
|
||||||
|
if (price.compareTo(BigDecimal.ZERO) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有第三方支付走第三方
|
||||||
|
if (PayProductChannelCodeEnumNew.hasAlipay(payType) || PayProductChannelCodeEnumNew.hasWechat(payType)) {
|
||||||
|
payByExternal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赠送金支付
|
||||||
|
*/
|
||||||
|
private void payByGift() {
|
||||||
|
// 赠送金余额减去购买金额
|
||||||
|
usedGift = price.min(gift);
|
||||||
|
|
||||||
|
price = price.subtract(usedGift);
|
||||||
|
|
||||||
|
// 更新余额
|
||||||
|
organAmountDO.setAmount(organAmountDO.getAmount());
|
||||||
|
organAmountDO.setGiftAmount(gift.subtract(usedGift));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 余额支付
|
||||||
|
*/
|
||||||
|
private void payByBalance() {
|
||||||
|
//现金余额减去购买金额
|
||||||
|
usedBalance = price.min(balance);
|
||||||
|
|
||||||
|
price = price.subtract(usedBalance);
|
||||||
|
|
||||||
|
// 更新余额
|
||||||
|
organAmountDO.setAmount(balance.subtract(usedBalance));
|
||||||
|
organAmountDO.setGiftAmount(organAmountDO.getGiftAmount());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据扣款情况生成购买记录和收支记录
|
||||||
|
*/
|
||||||
|
private void generatePurchaseRecordAndIncomeRecord() {
|
||||||
|
// 扣款价格大于0
|
||||||
|
if (price.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
// 只有赠送金
|
||||||
|
if (PayProductChannelCodeEnumNew.isGiftOnly(payType)) {
|
||||||
|
throw new ServiceException(PAY_PRODUCT_GIFT_BALANCE_INSUFFICIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有现金/现金+赠送金
|
||||||
|
if (PayProductChannelCodeEnumNew.isBalanceOnly(payType) || PayProductChannelCodeEnumNew.isBalanceAndGift(payType)) {
|
||||||
|
throw new ServiceException(PAY_PRODUCT_BALANCE_INSUFFICIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// 扣款完成,生成记录
|
||||||
|
// 新增购买记录
|
||||||
|
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
||||||
|
purchaseRecordDO = PurchaseRecordDO.builder()
|
||||||
|
.id(purchaseId)
|
||||||
|
.organId(organId)
|
||||||
|
.productId(productsDO.getId())
|
||||||
|
.productName(productsDO.getProductName())
|
||||||
|
.productDetailsId(productsDetailDO.getId())
|
||||||
|
.purchaseDuration(productsDetailDO.getDuration())
|
||||||
|
.purchaseDurationUnit(productsDetailDO.getDurationUnit())
|
||||||
|
.startTime(startTime)
|
||||||
|
.endTime(endTime)
|
||||||
|
.paymentMethod(payType)
|
||||||
|
.totalAmount(price)
|
||||||
|
.originalAmount(originalPrice)
|
||||||
|
.monthAverageAmount(monthAverageAmount)
|
||||||
|
.isInvocing(justPayGift ? null : InvoiceStatusEnum.INVOICABLE.getCode())
|
||||||
|
.initial(initialized)
|
||||||
|
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
|
||||||
|
.previousId(previousPurchaseRecordId)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 以上检查都通过,走的是第三方支付
|
||||||
|
if(ObjectUtil.isNotNull(payOrderDO)) {
|
||||||
|
// 第三方支付在异步回调后创建收支记录
|
||||||
|
|
||||||
|
payOrderDO.setPurchaseId(purchaseId);
|
||||||
|
if (PayProductChannelCodeEnumNew.hasAlipay(payType)) {
|
||||||
|
payOrderDO.setChannelCode(PayChannelCodeEnum.ALIPAY_PC);
|
||||||
|
} else if (PayProductChannelCodeEnumNew.hasWechat(payType)) {
|
||||||
|
payOrderDO.setChannelCode(PayChannelCodeEnum.WECHAT_NATIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录第三方支付金额
|
||||||
|
purchaseRecordDO.setExternalSpent(price);
|
||||||
|
// 购买记录为等待支付
|
||||||
|
purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus());
|
||||||
|
// 更新为不可开票,等异步回调后修改为可开票
|
||||||
|
purchaseRecordDO.setIsInvocing(null);
|
||||||
|
} else {
|
||||||
|
BigDecimal amount = organAmountDO.getAmount();
|
||||||
|
BigDecimal giftAmount = organAmountDO.getGiftAmount();
|
||||||
|
|
||||||
|
incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
|
||||||
|
.organId(organId)
|
||||||
|
.businessNo(snowflakeSerialNoWorker3rd.nextSerialNo())
|
||||||
|
.purchaseId(purchaseRecordDO.getId())
|
||||||
|
.tradeType(TradeTypeEnum.PRODUCT.getCode())
|
||||||
|
.incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
|
||||||
|
.cashAmountChange(usedBalance)
|
||||||
|
.giftAmountChange(usedGift)
|
||||||
|
.availableAmount(amount.add(giftAmount))
|
||||||
|
.amount(amount)
|
||||||
|
.giftAmount(giftAmount)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建第三方支付订单
|
||||||
|
*/
|
||||||
|
private void payByExternal() {
|
||||||
|
payOrderDO = new PayOrderDO();
|
||||||
|
|
||||||
|
Long payOrderId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
|
||||||
|
Long organId = SecurityFrameworkUtils.getUserOrganId();
|
||||||
|
|
||||||
|
payOrderDO.setId(payOrderId);
|
||||||
|
payOrderDO.setStatus(PayOrderStatusEnum.WAITING);
|
||||||
|
payOrderDO.setExpireTime(LocalDateTime.now().plus(payConfig.getPayExpireTime()));
|
||||||
|
payOrderDO.setPrice(price);
|
||||||
|
payOrderDO.setSubject(productsDO.getProductName());
|
||||||
|
payOrderDO.setOrganId(organId);
|
||||||
|
payOrderDO.setTradeType(TradeTypeEnum.PRODUCT.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取订购产品的到期时间
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public LocalDate getProductPayExpireTime() {
|
||||||
|
return endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算结束时间
|
||||||
|
*
|
||||||
|
* @param duration
|
||||||
|
* @param durationUnit
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private LocalDate calculateEndTime(Integer duration, Integer durationUnit) {
|
||||||
|
LocalDate today = LocalDate.now();
|
||||||
|
// 1、 计算延期后的实际有效期
|
||||||
|
LocalDate maxDelayEnd = endTime;
|
||||||
|
if (CollUtil.isNotEmpty(delayRespVOList)) {
|
||||||
|
// 找到延期记录中最大的 delayTime 作为最终有效期
|
||||||
|
maxDelayEnd = delayRespVOList.stream()
|
||||||
|
.map(ProductDelayRespVO::getDelayTime)
|
||||||
|
.max(LocalDate::compareTo)
|
||||||
|
.orElse(endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2、 计算有效期叠加的起点
|
||||||
|
LocalDate purchaseStart;
|
||||||
|
if (today.isAfter(maxDelayEnd)) {
|
||||||
|
// 已过期 → 从今天开始累加
|
||||||
|
purchaseStart = today;
|
||||||
|
} else {
|
||||||
|
// 未过期 → 从 maxDelayEnd 的下一天开始累加
|
||||||
|
purchaseStart = maxDelayEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3、加正常时间
|
||||||
|
if (ObjectUtil.isNotNull(duration) && durationUnit != null) {
|
||||||
|
endTime = addTime(purchaseStart, duration, durationUnit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4、扣掉延期天数
|
||||||
|
long delayDays = 0L;
|
||||||
|
if (CollUtil.isNotEmpty(delayRespVOList)) {
|
||||||
|
delayDays = calculateDelayDays(startTime, delayRespVOList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delayDays > 0) {
|
||||||
|
endTime = endTime.minusDays(delayDays);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endTime.isBefore(today)) {
|
||||||
|
throw ServiceExceptionUtil.exception(PRODUCT_PURCHASE_ENDTIME_BEFORE_DELAYTIME_ERROR, LocalDateTimeUtil.format(endTime, DatePattern.NORM_DATE_PATTERN));
|
||||||
|
}
|
||||||
|
|
||||||
|
return endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算总延期天数(合并区间,避免重叠/不连续)
|
||||||
|
*/
|
||||||
|
private static long calculateDelayDays(LocalDate startTime, List<ProductDelayRespVO> delayRecords) {
|
||||||
|
// 1. 生成延期区间并打上备注
|
||||||
|
List<DateIntervalDTO> intervals = buildIntervals(startTime, delayRecords);
|
||||||
|
if (CollUtil.isEmpty(intervals)) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 合并区间
|
||||||
|
List<DateIntervalDTO> merged = mergeIntervals(intervals);
|
||||||
|
|
||||||
|
// 3. 计算天数
|
||||||
|
return calculateTotalDays(merged);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据延期记录生成扣减区间,同时写入备注
|
||||||
|
*/
|
||||||
|
private static List<DateIntervalDTO> buildIntervals(LocalDate startTime, List<ProductDelayRespVO> delayRecords) {
|
||||||
|
List<DateIntervalDTO> intervals = new ArrayList<>();
|
||||||
|
|
||||||
|
for (ProductDelayRespVO record : delayRecords) {
|
||||||
|
LocalDate delayStart = record.getEndTime().plusDays(1);
|
||||||
|
LocalDate delayEnd = record.getDelayTime();
|
||||||
|
|
||||||
|
if (delayEnd.isBefore(delayStart)) {
|
||||||
|
record.setRemark("无扣减");
|
||||||
|
continue; // 异常数据,忽略
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDate intervalEnd;
|
||||||
|
if (!delayEnd.isAfter(startTime)) {
|
||||||
|
// 情况1:延期结束在计算结束之前 → 整段都扣
|
||||||
|
intervalEnd = delayEnd;
|
||||||
|
} else {
|
||||||
|
// 情况2:延期结束晚于计算结束 → 扣到 startTime 为止
|
||||||
|
intervalEnd = startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
long deductDays = ChronoUnit.DAYS.between(delayStart, intervalEnd) + 1;
|
||||||
|
if (deductDays > 0) {
|
||||||
|
record.setRemark("扣减" + deductDays + "天");
|
||||||
|
intervals.add(new DateIntervalDTO(delayStart, intervalEnd));
|
||||||
|
} else {
|
||||||
|
record.setRemark("无扣减");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return intervals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并区间(避免重叠/相邻)
|
||||||
|
*/
|
||||||
|
private static List<DateIntervalDTO> mergeIntervals(List<DateIntervalDTO> intervals) {
|
||||||
|
intervals.sort(Comparator.comparing(DateIntervalDTO::getStart));
|
||||||
|
|
||||||
|
List<DateIntervalDTO> merged = new ArrayList<>();
|
||||||
|
DateIntervalDTO current = intervals.get(0);
|
||||||
|
|
||||||
|
for (int i = 1; i < intervals.size(); i++) {
|
||||||
|
DateIntervalDTO next = intervals.get(i);
|
||||||
|
if (!next.getStart().isAfter(current.getEnd().plusDays(1))) {
|
||||||
|
// 重叠或相邻 → 合并
|
||||||
|
current.setEnd(current.getEnd().isAfter(next.getEnd()) ? current.getEnd() : next.getEnd());
|
||||||
|
} else {
|
||||||
|
merged.add(current);
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
merged.add(current);
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算合并区间的总天数
|
||||||
|
*/
|
||||||
|
private static long calculateTotalDays(List<DateIntervalDTO> merged) {
|
||||||
|
long totalDays = 0L;
|
||||||
|
for (DateIntervalDTO interval : merged) {
|
||||||
|
totalDays += ChronoUnit.DAYS.between(interval.getStart(), interval.getEnd()) + 1;
|
||||||
|
}
|
||||||
|
return totalDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于时长和单位累加time
|
||||||
|
*
|
||||||
|
* @param time
|
||||||
|
* @param duration
|
||||||
|
* @param unit
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static LocalDate addTime(LocalDate time, int duration, int unit) {
|
||||||
|
switch (ProductDurationUnitEnum.fromStatus(unit)) {
|
||||||
|
case YEAR:
|
||||||
|
return time.plusYears(duration);
|
||||||
|
case MONTH:
|
||||||
|
return time.plusMonths(duration);
|
||||||
|
default:
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算有首购记录的产品价格
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private BigDecimal computePrice() {
|
||||||
|
// 月均单价
|
||||||
|
monthAverageAmount = orgInitialPurchaseRecord.getMonthAverageAmount();
|
||||||
|
|
||||||
|
// 首购的定价
|
||||||
|
BigDecimal initialPurchaseRecordTotalAmount = orgInitialPurchaseRecord.getTotalAmount();
|
||||||
|
|
||||||
|
// 定价规则的折扣
|
||||||
|
BigDecimal discount = productsDetailDO.getDiscount();
|
||||||
|
if (ObjectUtil.isNull(discount)) {
|
||||||
|
discount = BigDecimal.valueOf(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal totalAmount;
|
||||||
|
/**
|
||||||
|
* ①:续费订购满1年:
|
||||||
|
* 原始价格 = 订购总价 * duration
|
||||||
|
* 现价 = 原始价格 * 折扣 /100,保留两位小数
|
||||||
|
* ②:不满1年按月份计算
|
||||||
|
* 原始价格 = 月均单价 * duration
|
||||||
|
* 现价 = 原始价格 * 折扣 /100,保留两位小数
|
||||||
|
*/
|
||||||
|
switch (ProductDurationUnitEnum.fromStatus(productsDetailDO.getDurationUnit())) {
|
||||||
|
case YEAR -> {
|
||||||
|
originalPrice = initialPurchaseRecordTotalAmount
|
||||||
|
.multiply(BigDecimal.valueOf(productsDetailDO.getDuration()));
|
||||||
|
totalAmount = originalPrice
|
||||||
|
.multiply(discount)
|
||||||
|
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
||||||
|
}
|
||||||
|
case MONTH -> {
|
||||||
|
originalPrice = monthAverageAmount
|
||||||
|
.multiply(BigDecimal.valueOf(productsDetailDO.getDuration()));
|
||||||
|
totalAmount = originalPrice
|
||||||
|
.multiply(discount)
|
||||||
|
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
||||||
|
}
|
||||||
|
default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算首次订购的价格
|
||||||
|
*/
|
||||||
|
private BigDecimal computeInitialProductPayAmount() {
|
||||||
|
// 订购数量
|
||||||
|
Integer duration = productsDetailDO.getDuration();
|
||||||
|
// 产品订购价格
|
||||||
|
BigDecimal orgPurchasePrice = productsDO.getPrice();
|
||||||
|
// 折扣
|
||||||
|
BigDecimal discount = productsDetailDO.getDiscount();
|
||||||
|
if (ObjectUtil.isNull(discount)) {
|
||||||
|
discount = BigDecimal.valueOf(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal totalAmount;
|
||||||
|
switch (ProductDurationUnitEnum.fromStatus(productsDetailDO.getDurationUnit())) {
|
||||||
|
case YEAR -> {
|
||||||
|
// 原价
|
||||||
|
originalPrice = orgPurchasePrice.multiply(BigDecimal.valueOf(duration));
|
||||||
|
// 折扣价格 = 原价 * 折扣
|
||||||
|
totalAmount = originalPrice
|
||||||
|
.multiply(discount)
|
||||||
|
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
||||||
|
// 月均单价 = 折扣价格 / 10
|
||||||
|
monthAverageAmount = originalPrice.divide(new BigDecimal(10), 2, RoundingMode.DOWN);
|
||||||
|
}
|
||||||
|
case MONTH -> {
|
||||||
|
// 月均单价 = 订购价格 / 10
|
||||||
|
monthAverageAmount = orgPurchasePrice.divide(new BigDecimal(10), 2, RoundingMode.DOWN);
|
||||||
|
// 原价
|
||||||
|
originalPrice = monthAverageAmount.multiply(BigDecimal.valueOf(duration));
|
||||||
|
// 折扣价格 = 原价 * 折扣
|
||||||
|
totalAmount = originalPrice.multiply(discount)
|
||||||
|
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
||||||
|
}
|
||||||
|
default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT);
|
||||||
|
}
|
||||||
|
return totalAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||||
|
<mapper namespace="com.cf.imes.module.system.dal.mysql.incomeandexpense.IncomeExpenseDetailsMapper">
|
||||||
|
<select id="getAccountDetailsPage" resultType="com.cf.imes.module.system.dal.dataobject.incomeandexpense.AccountDetailsDO">
|
||||||
|
select i.id, i.business_no, i.order_no ,i.trade_type , i.income_expense_type, i.cash_amount_change, i.gift_amount_change, i.creator, i.create_time, i.organ_id ,
|
||||||
|
p.total_amount, p.external_spent, p.account_balance_spent, p.gift_money_spent, p.payment_method, o.channel_code
|
||||||
|
from income_expense_details i
|
||||||
|
left join purchase_record p on i.purchase_id = p.id
|
||||||
|
left join pay_order o on i.order_no = o.id
|
||||||
|
where i.deleted = false
|
||||||
|
<if test="reqVO.organId != null">
|
||||||
|
AND i.organ_id = #{reqVO.organId}
|
||||||
|
</if>
|
||||||
|
<if test="reqVO.businessNo != null and reqVO.businessNo != ''">
|
||||||
|
AND i.business_no = #{reqVO.businessNo}
|
||||||
|
</if>
|
||||||
|
<if test="reqVO.orderNo != null and reqVO.orderNo != ''">
|
||||||
|
AND i.order_no = #{reqVO.orderNo}
|
||||||
|
</if>
|
||||||
|
<if test="reqVO.tradeType != null">
|
||||||
|
<choose>
|
||||||
|
<when test="reqVO.tradeType == @com.cf.imes.module.system.enums.pay.TradeTypeEnum@RECHARGE.code">
|
||||||
|
AND i.trade_type IN (
|
||||||
|
#{reqVO.tradeType},
|
||||||
|
'${@com.cf.imes.module.system.enums.pay.TradeTypeEnum@MANUAL_ADJUST.code}'
|
||||||
|
)
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
AND i.trade_type = #{reqVO.tradeType}
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
</if>
|
||||||
|
<if test="reqVO.incomeExpenseType != null">
|
||||||
|
AND i.income_expense_type = #{reqVO.incomeExpenseType}
|
||||||
|
</if>
|
||||||
|
<if test="reqVO.createTime != null and reqVO.createTime.size() == 2">
|
||||||
|
AND i.create_time between #{reqVo.createTime[0]} and #{reqVo.createTime[1]}
|
||||||
|
</if>
|
||||||
|
<if test="reqVO.productPrice != null">
|
||||||
|
AND p.total_amount = #{reqVo.productPrice}
|
||||||
|
</if>
|
||||||
|
<if test="reqVO.rechargeAmount != null">
|
||||||
|
AND i.cash_amount_change = #{reqVo.rechargeAmount}
|
||||||
|
</if>
|
||||||
|
<if test="reqVO.giftAmount != null">
|
||||||
|
AND i.gift_amount_change = #{reqVo.giftAmount}
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
+2
-1
@@ -2,10 +2,11 @@
|
|||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||||
<mapper namespace="com.cf.imes.module.system.dal.mysql.pay.PayOrderMapper">
|
<mapper namespace="com.cf.imes.module.system.dal.mysql.pay.PayOrderMapper">
|
||||||
<select id="getRechargeDetailsPage" resultType="com.cf.imes.module.system.dal.dataobject.pay.PayOrderRechargeDetailDO">
|
<select id="getRechargeDetailsPage" resultType="com.cf.imes.module.system.dal.dataobject.pay.PayOrderRechargeDetailDO">
|
||||||
select o.id, o.organ_id, o.pay_no, o.price, o.purchase_id, o.creator, o.create_time, p.total_amount, p.account_balance_spent, p.gift_money_spent
|
select o.id, o.organ_id, o.pay_no, o.price, o.purchase_id, o.channel_code, o.creator, o.create_time, p.total_amount, p.account_balance_spent, p.gift_money_spent, p.payment_method
|
||||||
from pay_order o
|
from pay_order o
|
||||||
left join purchase_record p on o.purchase_id = p.id
|
left join purchase_record p on o.purchase_id = p.id
|
||||||
where o.status = '${@com.cf.imes.module.system.enums.pay.PayOrderStatusEnum@SUCCESS.status}'
|
where o.status = '${@com.cf.imes.module.system.enums.pay.PayOrderStatusEnum@SUCCESS.status}'
|
||||||
|
|
||||||
<if test="reqVO.organId != null">
|
<if test="reqVO.organId != null">
|
||||||
AND o.organ_id = #{reqVO.organId}
|
AND o.organ_id = #{reqVO.organId}
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
Reference in New Issue
Block a user