1、新增组织支持自动过期的购买记录;2、软件购买定价规则自动展示1~10月的价格和参考定价中年的展示;3、产品管理和定价管理适配新的需求入参;

This commit is contained in:
gaoqr
2025-09-28 17:45:06 +08:00
parent 836b61ac6b
commit 7250861d0e
28 changed files with 322 additions and 308 deletions
@@ -474,6 +474,7 @@ public class ErrorCodeConstants {
public static final ErrorCode RECHARGE_ACTIVE_NO_EXIST = new ErrorCode(1_002_045_010, "当前赠送金活动不存在"); public static final ErrorCode RECHARGE_ACTIVE_NO_EXIST = new ErrorCode(1_002_045_010, "当前赠送金活动不存在");
public static final ErrorCode PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT = new ErrorCode(1_002_045_011, "不支持的时长单位,请检查该定价规则的时长单位"); public static final ErrorCode PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT = new ErrorCode(1_002_045_011, "不支持的时长单位,请检查该定价规则的时长单位");
public static final ErrorCode ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR = new ErrorCode(1_002_045_012, "组织下存在当前产品的待支付记录,请确认或取消支付后再次发起软件购买"); public static final ErrorCode ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR = new ErrorCode(1_002_045_012, "组织下存在当前产品的待支付记录,请确认或取消支付后再次发起软件购买");
public static final ErrorCode PRODUCT_PRICE_NULL_ERROR = new ErrorCode(1_002_045_013, "产品建议价格为空,请联系客服");
//=========== 广告相关 1-002-046-000 ============ //=========== 广告相关 1-002-046-000 ============
public static final ErrorCode ADVERTISEMENT_POSITION_NO_EXIST = new ErrorCode(1_002_046_001, "广告所选放置位置不存在"); public static final ErrorCode ADVERTISEMENT_POSITION_NO_EXIST = new ErrorCode(1_002_046_001, "广告所选放置位置不存在");
@@ -33,31 +33,30 @@ public class ProductsController {
@PutMapping("") @PutMapping("")
@Operation(summary = "新建产品") @Operation(summary = "新建产品")
public CommonResult<Long> createProducts(@RequestParam("productName") String productName) { public CommonResult<Long> createProducts(@Valid @RequestBody ProductSaveReqVO updateReqVO) {
return success(productsService.create(productName)); return success(productsService.create(updateReqVO));
} }
@PostMapping("") @PostMapping("")
@Operation(summary = "更新产品") @Operation(summary = "更新产品")
public CommonResult<Boolean> updateProducts(@Valid @RequestBody ProductSaveReqVO updateReqVO) { public CommonResult<Boolean> updateProducts(@Valid @RequestBody ProductSaveReqVO updateReqVO) {
ProductsDO productsDO = BeanUtils.toBean(updateReqVO, ProductsDO.class); productsService.update(updateReqVO);
productsService.update(productsDO);
return success(true); return success(true);
} }
@PostMapping("/freeze") @PostMapping("/freeze")
@Operation(summary = "下架产品") @Operation(summary = "下架产品")
public CommonResult<Boolean> freezeProducts(@Valid Long id) { public CommonResult<Boolean> freezeProducts(@Valid Long id) {
ProductsDO productsDO = new ProductsDO().setId(id).setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus()); ProductSaveReqVO updateReqVO = new ProductSaveReqVO().setId(id).setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus());
productsService.update(productsDO); productsService.update(updateReqVO);
return success(true); return success(true);
} }
@PostMapping("/melt") @PostMapping("/melt")
@Operation(summary = "上架产品") @Operation(summary = "上架产品")
public CommonResult<Boolean> meltProducts(@Valid Long id) { public CommonResult<Boolean> meltProducts(@Valid Long id) {
ProductsDO productsDO = new ProductsDO().setId(id).setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus()); ProductSaveReqVO updateReqVO = new ProductSaveReqVO().setId(id).setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
productsService.update(productsDO); productsService.update(updateReqVO);
return success(true); return success(true);
} }
@@ -74,8 +74,8 @@ public class ProductsDetailController {
@Parameter(name = "organId", description = "组织编号", required = true, example = "1") @Parameter(name = "organId", description = "组织编号", required = true, example = "1")
@Parameter(name = "productId", description = "产品编号", required = true, example = "1") @Parameter(name = "productId", description = "产品编号", required = true, example = "1")
public CommonResult<List<ProductDetailsRespVO>> getOrgProductDetailList(@RequestParam("organId") Long organId, @RequestParam("productId") Long productId) { public CommonResult<List<ProductDetailsRespVO>> getOrgProductDetailList(@RequestParam("organId") Long organId, @RequestParam("productId") Long productId) {
List<ProductsDetailDO> pageResult = purchaseService.getOrgProductDetailList(organId, productId); List<ProductDetailsRespVO> pageResult = purchaseService.getOrgProductDetailList(organId, productId);
return success(BeanUtils.toBean(pageResult, ProductDetailsRespVO.class)); return success(pageResult);
} }
} }
@@ -6,6 +6,7 @@ import com.cf.imes.module.system.validation.products.ProductStatus;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Schema(description = "管理后台 - 产品表 products Response VO") @Schema(description = "管理后台 - 产品表 products Response VO")
@@ -34,4 +35,7 @@ public class ProductRespVO {
@ExcelProperty("编辑时间") @ExcelProperty("编辑时间")
private LocalDateTime updateTime; private LocalDateTime updateTime;
@Schema(description = "建议价格")
@ExcelProperty("建议价格")
private BigDecimal price;
} }
@@ -2,11 +2,17 @@ package com.cf.imes.module.system.controller.admin.funds.products.vo.product;
import com.cf.imes.module.system.validation.products.ProductStatus; import com.cf.imes.module.system.validation.products.ProductStatus;
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.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import lombok.Data; import lombok.Data;
import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 产品表 product新增/修改 Request VO") @Schema(description = "管理后台 - 产品表 product新增/修改 Request VO")
@Data @Data
public class ProductSaveReqVO { public class ProductSaveReqVO {
@@ -19,6 +25,13 @@ public class ProductSaveReqVO {
@Size(max = 64, message = "产品名称长度不能超过64") @Size(max = 64, message = "产品名称长度不能超过64")
private String productName; private String productName;
@Schema(description = "产品建议价格")
@NotNull(message = "产品建议价格不能为空")
@DecimalMin(value = "0.01", message = "产品建议价格必须大于零")
@DecimalMax(value = "100000", message = "产品建议价格不能超过十万")
@Digits(integer = 6, fraction = 2, message = "产品建议价格格式不正确")
private BigDecimal price;
@Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED)
@ProductStatus @ProductStatus
private Integer status; private Integer status;
@@ -25,7 +25,7 @@ public class ProductDetailsRespVO {
private Integer purchaseType; private Integer purchaseType;
@Schema(description = "购买时长", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "购买时长", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String duration; private Integer duration;
@Schema(description = "产品时长单位,0:年、1:月", example = "1") @Schema(description = "产品时长单位,0:年、1:月", example = "1")
private Integer durationUnit; private Integer durationUnit;
@@ -1,8 +1,7 @@
package com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails; package com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails;
import com.cf.imes.framework.common.validation.NumberValid; import com.cf.imes.module.system.enums.products.ProductsPurchaseTypeEnum;
import com.cf.imes.module.system.validation.products.ProductDurationUnitValid; import com.cf.imes.module.system.validation.products.ProductDurationUnitValid;
import com.cf.imes.module.system.validation.products.ProductType;
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.DecimalMax;
import jakarta.validation.constraints.DecimalMin; import jakarta.validation.constraints.DecimalMin;
@@ -24,46 +23,28 @@ public class ProductDetailsSaveReqVO {
private Long productId; private Long productId;
@Schema(description = "购买方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @Schema(description = "购买方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "购买方式不能为空") private Integer purchaseType = ProductsPurchaseTypeEnum.DURATION.getStatus();
@ProductType
private Integer purchaseType;
@Schema(description = "产品时间,格式:时长值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "产品时间,格式:时长值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String duration; private String duration;
@Schema(description = "产品时长单位,0:年、1:月", example = "1") @Schema(description = "产品时长单位,0:年", example = "1")
@ProductDurationUnitValid @ProductDurationUnitValid
private Integer durationUnit; private Integer durationUnit;
@Schema(description = "产品产量", requiredMode = Schema.RequiredMode.REQUIRED, example = "") @Schema(description = "产品产量", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
private BigDecimal output; private BigDecimal output;
@Schema(description = "赠送时间,格式:时长值 - 时间单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String giftDuration;
@Schema(description = "赠送时长单位,0:年、1:月", example = "1")
@ProductDurationUnitValid
private Integer giftDurationUnit;
@Schema(description = "赠送产量", requiredMode = Schema.RequiredMode.REQUIRED, example = "") @Schema(description = "赠送产量", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
private BigDecimal giftOutput; private BigDecimal giftOutput;
@Schema(description = "产品原价", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
@NotNull(message = "产品原价不能为空")
@NumberValid(name = "产品原价")
private BigDecimal originalPrice;
@Schema(description = "折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8") @Schema(description = "折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8")
@DecimalMax(value = "100", message = "折扣必须小于等于100") @DecimalMax(value = "100", message = "折扣必须小于等于100")
@DecimalMin(value = "0", message = "折扣必须大于等于0") @DecimalMin(value = "0", message = "折扣必须大于等于0")
@Digits(integer = 3, fraction = 0, message = "折扣必须是0 ~ 100间的整数") @Digits(integer = 3, fraction = 0, message = "折扣必须是0 ~ 100间的整数")
@NotNull(message = "折扣不能为空")
private BigDecimal discount; private BigDecimal discount;
@Schema(description = "产品现价", requiredMode = Schema.RequiredMode.REQUIRED, example = "80")
@NotNull(message = "产品现价不能为空")
@NumberValid(name = "产品现价")
private BigDecimal currentPrice;
@Schema(description = "是否特惠", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") @Schema(description = "是否特惠", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean isDiscounted; private Boolean isDiscounted;
@@ -1,6 +1,5 @@
package com.cf.imes.module.system.controller.admin.funds.purchase.vo; package com.cf.imes.module.system.controller.admin.funds.purchase.vo;
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.funds.purchase.PurchaseRecordDO;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@@ -18,9 +17,4 @@ public class OrgEarliestPurchaseProductRespVO {
* 购买记录 * 购买记录
*/ */
private PurchaseRecordDO purchaseRecordDO; private PurchaseRecordDO purchaseRecordDO;
/**
* 产品定价规则
*/
private ProductsDetailDO productsDetailDO;
} }
@@ -116,7 +116,7 @@ public class OrganController {
Long organId = bean.getId(); Long organId = bean.getId();
// 查询当次列表组织下的所有购买记录 // 查询当次列表组织下的所有购买记录
List<OrganPagePurchaseRespVO> orgPurchaseList = BeanUtil.copyToList(purchaseService.getOrgPurchaseList(Set.of(organId)), OrganPagePurchaseRespVO.class); List<OrganPagePurchaseRespVO> orgPurchaseList = BeanUtil.copyToList(purchaseService.getOrgPurchaseNotInitialList(Set.of(organId)), OrganPagePurchaseRespVO.class);
// 查询购买记录下的延期记录 // 查询购买记录下的延期记录
orgPurchaseList.forEach(p -> { orgPurchaseList.forEach(p -> {
List<ProductDelayRespVO> delayRecordsByPurchaseId = productDelayService.getRecordsByPurchaseId(p.getId(), organId); List<ProductDelayRespVO> delayRecordsByPurchaseId = productDelayService.getRecordsByPurchaseId(p.getId(), organId);
@@ -143,7 +143,7 @@ public class OrganController {
Set<Long> organIds = bean.getList().stream().map(OrganRespVO::getId).collect(Collectors.toSet()); Set<Long> organIds = bean.getList().stream().map(OrganRespVO::getId).collect(Collectors.toSet());
// 查询当次列表组织下的所有购买记录 // 查询当次列表组织下的所有购买记录
List<PurchaseRecordDO> orgPurchaseList = purchaseService.getOrgPurchaseList(organIds); List<PurchaseRecordDO> orgPurchaseList = purchaseService.getOrgPurchaseNotInitialList(organIds);
// 匹配到对应的组织下 // 匹配到对应的组织下
bean.getList().forEach(e -> { bean.getList().forEach(e -> {
List<OrganPagePurchaseRespVO> purchaseRespVOList = orgPurchaseList.stream() List<OrganPagePurchaseRespVO> purchaseRespVOList = orgPurchaseList.stream()
@@ -8,7 +8,6 @@ import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate;
@Schema(description = "管理后台 - 组织创建购买产品 Request VO") @Schema(description = "管理后台 - 组织创建购买产品 Request VO")
@Data @Data
@@ -17,18 +16,10 @@ public class OrganCreateProductPurchaseReqVO {
@NotNull(message = "产品编号不能为空", groups = {OrganSaveCreateGroup.class}) @NotNull(message = "产品编号不能为空", groups = {OrganSaveCreateGroup.class})
private Long productId; private Long productId;
@Schema(description = "定价规则编号", example = "1")
@NotNull(message = "定价规则编号不能为空", groups = {OrganSaveCreateGroup.class})
private Long productDetailsId;
@Schema(description = "产品购买金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") @Schema(description = "产品购买金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
@NotNull(message = "产品购买金额不能为空", groups = {OrganSaveCreateGroup.class}) @NotNull(message = "产品购买金额不能为空", groups = {OrganSaveCreateGroup.class})
@DecimalMin(value = "0.01", message = "产品购买金额必须大于零") @DecimalMin(value = "0.01", message = "产品购买金额必须大于零")
@DecimalMax(value = "100000", message = "产品购买金额不能超过十万") @DecimalMax(value = "100000", message = "产品购买金额不能超过十万")
@Digits(integer = 6, fraction = 2, message = "产品购买金额格式不正确") @Digits(integer = 6, fraction = 2, message = "产品购买金额格式不正确")
private BigDecimal price; private BigDecimal price;
@Schema(description = "产品有效时间")
@NotNull(message = "产品有效时间不能为空", groups = {OrganSaveCreateGroup.class})
private LocalDate productExpireDate;
} }
@@ -3,7 +3,6 @@ package com.cf.imes.module.system.controller.admin.pay.vo;
import com.cf.imes.module.system.validation.pay.PayProductChannelCodeValid; 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.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
@@ -23,6 +22,10 @@ public class PayProductOrderUnifiedReqVO {
@NotNull(message = "产品定价规则编号不能为空") @NotNull(message = "产品定价规则编号不能为空")
private Long productDetailsId; private Long productDetailsId;
@Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "产品ID不能为空")
private Long productId;
@Schema(description = "产品支付渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @Schema(description = "产品支付渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@PayProductChannelCodeValid @PayProductChannelCodeValid
@NotNull(message = "产品支付渠道编码不能为空") @NotNull(message = "产品支付渠道编码不能为空")
@@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.cf.imes.framework.mybatis.core.dataobject.BaseDO; import com.cf.imes.framework.mybatis.core.dataobject.BaseDO;
import lombok.*; import lombok.*;
import java.math.BigDecimal;
/** /**
* 产品表 products DO * 产品表 products DO
@@ -47,6 +49,8 @@ public class ProductsDO extends BaseDO {
*/ */
private Integer softwareId; private Integer softwareId;
/**
* 建议价格
*/
private BigDecimal price;
} }
@@ -56,36 +56,16 @@ public class ProductsDetailDO extends BaseDO {
@TableField("`output`") @TableField("`output`")
private BigDecimal output; private BigDecimal output;
/**
* 赠送时间
*/
private Integer giftDuration;
/**
* 赠送时间单位
*/
private Integer giftDurationUnit;
/** /**
* 赠送产量 * 赠送产量
*/ */
private BigDecimal giftOutput; private BigDecimal giftOutput;
/**
* 产品原价
*/
private BigDecimal originalPrice;
/** /**
* 折扣 * 折扣
*/ */
private BigDecimal discount; private BigDecimal discount;
/**
* 产品现价
*/
private BigDecimal currentPrice;
/** /**
* 是否特惠: 0否1是 * 是否特惠: 0否1是
*/ */
@@ -22,12 +22,9 @@ public interface ProductsDetailMapper extends BaseMapperX<ProductsDetailDO> {
.eqIfPresent(ProductsDetailDO::getPurchaseType, reqVO.getPurchaseType()) .eqIfPresent(ProductsDetailDO::getPurchaseType, reqVO.getPurchaseType())
.eqIfPresent(ProductsDetailDO::getProductId, reqVO.getProductId()) .eqIfPresent(ProductsDetailDO::getProductId, reqVO.getProductId())
.eqIfPresent(ProductsDetailDO::getDuration, reqVO.getDuration()) .eqIfPresent(ProductsDetailDO::getDuration, reqVO.getDuration())
.eqIfPresent(ProductsDetailDO::getGiftDuration, reqVO.getGiftDuration())
.eqIfPresent(ProductsDetailDO::getOutput, reqVO.getOutput()) .eqIfPresent(ProductsDetailDO::getOutput, reqVO.getOutput())
.eqIfPresent(ProductsDetailDO::getGiftOutput, reqVO.getGiftOutput()) .eqIfPresent(ProductsDetailDO::getGiftOutput, reqVO.getGiftOutput())
.eqIfPresent(ProductsDetailDO::getOriginalPrice, reqVO.getOriginalPrice())
.eqIfPresent(ProductsDetailDO::getDiscount, reqVO.getDiscount()) .eqIfPresent(ProductsDetailDO::getDiscount, reqVO.getDiscount())
.eqIfPresent(ProductsDetailDO::getCurrentPrice, reqVO.getCurrentPrice())
.eqIfPresent(ProductsDetailDO::getIsDiscounted, reqVO.getIsDiscounted()) .eqIfPresent(ProductsDetailDO::getIsDiscounted, reqVO.getIsDiscounted())
.eqIfPresent(ProductsDetailDO::getDeleted, false) .eqIfPresent(ProductsDetailDO::getDeleted, false)
); );
@@ -45,6 +45,7 @@ public interface PurchaseRecordMapper extends BaseMapperX<PurchaseRecordDO> {
.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())
.ne(PurchaseRecordDO::getInitial, true)
.orderByDesc(PurchaseRecordDO::getCreateTime)); .orderByDesc(PurchaseRecordDO::getCreateTime));
} }
@@ -63,6 +64,7 @@ public interface PurchaseRecordMapper extends BaseMapperX<PurchaseRecordDO> {
.eq(PurchaseRecordDO::getOrganId, SecurityFrameworkUtils.getUserOrganId()) .eq(PurchaseRecordDO::getOrganId, SecurityFrameworkUtils.getUserOrganId())
.eq(PurchaseRecordDO::getDeleted, false) .eq(PurchaseRecordDO::getDeleted, false)
.in(PurchaseRecordDO::getIsInvocing, InvoiceStatusEnum.INVOICABLE.getCode(), InvoiceStatusEnum.INVOICINGFAILED.getCode()) .in(PurchaseRecordDO::getIsInvocing, InvoiceStatusEnum.INVOICABLE.getCode(), InvoiceStatusEnum.INVOICINGFAILED.getCode())
.ne(PurchaseRecordDO::getInitial, true)
.orderByDesc(PurchaseRecordDO::getCreateTime) .orderByDesc(PurchaseRecordDO::getCreateTime)
); );
@@ -109,6 +111,7 @@ public interface PurchaseRecordMapper extends BaseMapperX<PurchaseRecordDO> {
.eqIfPresent(PurchaseRecordDO::getOrganId, pageReqVO.getOrganId()) .eqIfPresent(PurchaseRecordDO::getOrganId, pageReqVO.getOrganId())
.eqIfPresent(PurchaseRecordDO::getCreator, pageReqVO.getOperator()) .eqIfPresent(PurchaseRecordDO::getCreator, pageReqVO.getOperator())
.eq(PurchaseRecordDO::getDeleted, false) .eq(PurchaseRecordDO::getDeleted, false)
.ne(PurchaseRecordDO::getInitial, true)
.orderByDesc(PurchaseRecordDO::getCreateTime) .orderByDesc(PurchaseRecordDO::getCreateTime)
.select(PurchaseRecordDO::getId, PurchaseRecordDO::getOrganId, PurchaseRecordDO::getTotalAmount, PurchaseRecordDO::getAccountBalanceSpent, PurchaseRecordDO::getAlipaySpent, .select(PurchaseRecordDO::getId, PurchaseRecordDO::getOrganId, PurchaseRecordDO::getTotalAmount, PurchaseRecordDO::getAccountBalanceSpent, PurchaseRecordDO::getAlipaySpent,
PurchaseRecordDO::getWechatSpent, PurchaseRecordDO::getGiftMoneySpent, PurchaseRecordDO::getCreator, PurchaseRecordDO::getCreateTime) PurchaseRecordDO::getWechatSpent, PurchaseRecordDO::getGiftMoneySpent, PurchaseRecordDO::getCreator, PurchaseRecordDO::getCreateTime)
@@ -10,4 +10,6 @@ public class PayConstants {
public static final String PAY_ORDER_PREFIX_FORMAT = "P%dO%s"; // 第三方支付流水号格式 public static final String PAY_ORDER_PREFIX_FORMAT = "P%dO%s"; // 第三方支付流水号格式
public static final String PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT = "P%dD%s"; // 产品购买流水号格式 public static final String PAY_PRODUCT_DETAIL_BUSINESSNO_PREFIX_FORMAT = "P%dD%s"; // 产品购买流水号格式
public static final String PAY_PRODUCT_DETAIL_AUTO_GENERATE_MONTH_ID_FORMAT = "1759029544%d"; // 1~12月定价规则自动生成ID格式
} }
@@ -40,10 +40,6 @@ public enum ProductDurationUnitEnum implements IntArrayValuable, BaseEnum {
return Objects.equals(YEAR.code, name); return Objects.equals(YEAR.code, name);
} }
public static boolean isMonth(String name) {
return Objects.equals(MONTH.code, name);
}
public static ProductDurationUnitEnum fromStatus(Integer status) { public static ProductDurationUnitEnum fromStatus(Integer status) {
return Arrays.stream(values()) return Arrays.stream(values())
.filter(bean -> Objects.equals(bean.code, status)) .filter(bean -> Objects.equals(bean.code, status))
@@ -61,4 +57,13 @@ public enum ProductDurationUnitEnum implements IntArrayValuable, BaseEnum {
ProductDurationUnitEnum unit = fromStatus(status); ProductDurationUnitEnum unit = fromStatus(status);
return unit != null ? unit.getDesc() : null; return unit != null ? unit.getDesc() : null;
} }
/**
* 获取需要筛选的类型:年
*
* @return
*/
public static ProductDurationUnitEnum[] validArr() {
return new ProductDurationUnitEnum[]{YEAR};
}
} }
@@ -187,16 +187,11 @@ public class OrganAmountServiceImpl implements OrganAmountService {
// 查询组织最晚还有效的产品购买记录 // 查询组织最晚还有效的产品购买记录
OrgEarliestPurchaseProductRespVO orgEarliestProductDetail = purchaseService.getOrgEarliestProductDetail(organId); OrgEarliestPurchaseProductRespVO orgEarliestProductDetail = purchaseService.getOrgEarliestProductDetail(organId);
if (ObjectUtil.isNotNull(orgEarliestProductDetail)) { if (ObjectUtil.isNotNull(orgEarliestProductDetail)) {
// 购买记录 // 查询对应购买记录
PurchaseRecordDO purchaseRecordDO = orgEarliestProductDetail.getPurchaseRecordDO(); PurchaseRecordDO purchaseRecordDO = orgEarliestProductDetail.getPurchaseRecordDO();
// 购买记录关联的定价规则
ProductsDetailDO productsDetailDO = orgEarliestProductDetail.getProductsDetailDO();
organAmountRespVO.setProductEfficientDate(LocalDateTimeUtil.format(purchaseRecordDO.getEndTime(), NORM_DATETIME_FORMATTER)); organAmountRespVO.setProductEfficientDate(LocalDateTimeUtil.format(purchaseRecordDO.getEndTime(), NORM_DATETIME_FORMATTER));
organAmountRespVO.setDuration(productsDetailDO.getDuration()); organAmountRespVO.setDuration(purchaseRecordDO.getPurchaseDuration());
organAmountRespVO.setDurationUnit(productsDetailDO.getDurationUnit()); organAmountRespVO.setDurationUnit(purchaseRecordDO.getPurchaseDurationUnit());
organAmountRespVO.setGiftDuration(productsDetailDO.getGiftDuration());
organAmountRespVO.setGiftDurationUnit(productsDetailDO.getGiftDurationUnit());
} }
// 查询可开票金额 // 查询可开票金额
@@ -8,7 +8,7 @@ import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDetailDO;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import java.util.List; import java.math.BigDecimal;
/** /**
* 产品明细表 product_details Service 接口 * 产品明细表 product_details Service 接口
@@ -67,4 +67,12 @@ public interface ProductsDetailService {
* @param productId * @param productId
*/ */
ProductsDO validateProductExist(Long productId); ProductsDO validateProductExist(Long productId);
/**
* 获取产品1年定价的折扣
*
* @param productId
* @return
*/
BigDecimal getProductDetailOneYearDiscount(Long productId);
} }
@@ -1,11 +1,14 @@
package com.cf.imes.module.system.service.funds.products; package com.cf.imes.module.system.service.funds.products;
import com.alibaba.nacos.common.utils.StringUtils; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil; import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.Assert.AssertUtils; import com.cf.imes.framework.common.util.Assert.AssertUtils;
import com.cf.imes.framework.common.util.object.BeanUtils; import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.mybatis.core.query.LambdaUpdateWrapperX;
import com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails.ProductDetailsPageReqVO; import com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails.ProductDetailsPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails.ProductDetailsSaveReqVO; import com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails.ProductDetailsSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO; import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
@@ -19,8 +22,8 @@ import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*; import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
@@ -49,14 +52,7 @@ public class ProductsDetailServiceImpl implements ProductsDetailService {
if (ProductsPurchaseTypeEnum.DURATION.getStatus().equals(createReqVO.getPurchaseType())) { if (ProductsPurchaseTypeEnum.DURATION.getStatus().equals(createReqVO.getPurchaseType())) {
// 校验时长 // 校验时长
checkDuration(createReqVO.getDuration(), createReqVO.getDurationUnit()); checkDuration(createReqVO.getDuration(), createReqVO.getDurationUnit());
// 校验赠送时长
String giftDuration = createReqVO.getGiftDuration();
if (StringUtils.isNotEmpty(giftDuration)) {
checkDuration(giftDuration, createReqVO.getGiftDurationUnit());
}
// 校验现价计算是否正确
calculateDiscount(createReqVO.getOriginalPrice(), createReqVO.getCurrentPrice(), createReqVO.getDiscount());
// 查询是否有重复的定价 // 查询是否有重复的定价
checkProductDetails(createReqVO); checkProductDetails(createReqVO);
@@ -81,14 +77,6 @@ public class ProductsDetailServiceImpl implements ProductsDetailService {
if (updateReqVO.getPurchaseType().equals(ProductsPurchaseTypeEnum.DURATION.getStatus())) { if (updateReqVO.getPurchaseType().equals(ProductsPurchaseTypeEnum.DURATION.getStatus())) {
// 校验时长 // 校验时长
checkDuration(updateReqVO.getDuration(), updateReqVO.getDurationUnit()); checkDuration(updateReqVO.getDuration(), updateReqVO.getDurationUnit());
// 校验赠送时长
String giftDuration = updateReqVO.getGiftDuration();
if (StringUtils.isNotEmpty(giftDuration)) {
checkDuration(giftDuration, updateReqVO.getGiftDurationUnit());
}
// 校验现价计算是否正确
calculateDiscount(updateReqVO.getOriginalPrice(), updateReqVO.getCurrentPrice(), updateReqVO.getDiscount());
// 校验同时长是否还有其他的定价规则 // 校验同时长是否还有其他的定价规则
checkProductDetails(updateReqVO); checkProductDetails(updateReqVO);
@@ -97,7 +85,12 @@ public class ProductsDetailServiceImpl implements ProductsDetailService {
throw exception(PRODUCTS_OUTPUT_NOT_SUPPORT); throw exception(PRODUCTS_OUTPUT_NOT_SUPPORT);
} }
productDetailsMapper.updateById(BeanUtils.toBean(updateReqVO, ProductsDetailDO.class)); productDetailsMapper.update(new LambdaUpdateWrapper<ProductsDetailDO>()
.eq(ProductsDetailDO::getId, updateReqVO.getId())
.set(ProductsDetailDO::getDiscount, updateReqVO.getDiscount())
.set(ProductsDetailDO::getDuration, updateReqVO.getDuration())
.set(ProductsDetailDO::getDurationUnit, updateReqVO.getDurationUnit())
);
} }
@Override @Override
@@ -228,22 +221,16 @@ public class ProductsDetailServiceImpl implements ProductsDetailService {
} }
} }
@Override
/** public BigDecimal getProductDetailOneYearDiscount(Long productId) {
* 计算折扣是否正确 ProductsDetailDO productsDetailDO = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductsDetailDO>()
* .eq(ProductsDetailDO::getProductId, productId)
* @param originalPrice 原价 .eq(ProductsDetailDO::getDuration, 1)
* @param currentPrice 当前价格 .eq(ProductsDetailDO::getDurationUnit, ProductDurationUnitEnum.YEAR.getCode()));
* @param discount 折扣(百分比) if (ObjectUtil.isNotNull(productsDetailDO)) {
*/ return productsDetailDO.getDiscount();
private void calculateDiscount(BigDecimal originalPrice, BigDecimal currentPrice, BigDecimal discount) { } else {
// 校验当前价格是否正确 return BigDecimal.valueOf(100);
// 计算期望现价 = 原价 * 折扣 /100(保留2位小数)
BigDecimal expectedPrice = originalPrice.multiply(discount).divide(new BigDecimal(100)).setScale(2, RoundingMode.DOWN);
if (currentPrice.compareTo(expectedPrice) != 0) {
throw ServiceExceptionUtil.exception(PRODUCTS_DETAIL_CURRENTPRICE_CALCULATE_ERROR, expectedPrice);
} }
} }
} }
@@ -2,6 +2,7 @@ package com.cf.imes.module.system.service.funds.products;
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.products.vo.product.ProductPageReqVO; import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO; import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@@ -16,17 +17,17 @@ public interface ProductsService {
/** /**
* 创建产品 * 创建产品
* *
* @param productName 产品名称 * @param saveReqVO
* @return 编号 * @return 编号
*/ */
Long create(@Valid String productName); Long create(ProductSaveReqVO saveReqVO);
/** /**
* 更新产品 * 更新产品
* *
* @param updateReqVO 更新信息 * @param updateReqVO 更新信息
*/ */
void update(@Valid ProductsDO updateReqVO); void update(ProductSaveReqVO updateReqVO);
/** /**
* 删除产品 * 删除产品
@@ -3,8 +3,11 @@ package com.cf.imes.module.system.service.funds.products;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.Assert.AssertUtils; import com.cf.imes.framework.common.util.Assert.AssertUtils;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.mybatis.core.query.LambdaUpdateWrapperX;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductPageReqVO; import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO; import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import com.cf.imes.module.system.dal.mysql.funds.products.ProductsMapper; import com.cf.imes.module.system.dal.mysql.funds.products.ProductsMapper;
import com.cf.imes.module.system.enums.products.ProductStatusEnum; import com.cf.imes.module.system.enums.products.ProductStatusEnum;
@@ -32,12 +35,14 @@ public class ProductsServiceImpl implements ProductsService {
private ProductsMapper productsMapper; private ProductsMapper productsMapper;
@Override @Override
public Long create(String productName) { public Long create(ProductSaveReqVO createReqVO) {
String productName = createReqVO.getProductName();
// 校验产品名称唯一 // 校验产品名称唯一
validateProductNameUnique(null, productName); validateProductNameUnique(null, productName);
ProductsDO productsDO = new ProductsDO() ProductsDO productsDO = new ProductsDO()
.setProductName(productName) .setProductName(productName)
.setPrice(createReqVO.getPrice())
.setStatus(ProductStatusEnum.PRODUCT_WAITING_UPDATES.getStatus()) .setStatus(ProductStatusEnum.PRODUCT_WAITING_UPDATES.getStatus())
.setSoftwareId(0); .setSoftwareId(0);
productsMapper.insert(productsDO); productsMapper.insert(productsDO);
@@ -47,7 +52,7 @@ public class ProductsServiceImpl implements ProductsService {
@Override @Override
public void update(ProductsDO updateReqVO) { public void update(ProductSaveReqVO updateReqVO) {
Long updateReqVOId = updateReqVO.getId(); Long updateReqVOId = updateReqVO.getId();
String productName = updateReqVO.getProductName(); String productName = updateReqVO.getProductName();
// 目标状态 // 目标状态
@@ -63,9 +68,14 @@ public class ProductsServiceImpl implements ProductsService {
if (ProductStatusEnum.PRODUCT_UPDATES.getStatus().equals(status) && !ProductStatusEnum.isProductDelis(updateReqVOStatus)) { if (ProductStatusEnum.PRODUCT_UPDATES.getStatus().equals(status) && !ProductStatusEnum.isProductDelis(updateReqVOStatus)) {
throw exception(PRODUCTS_NO_UPDATE); throw exception(PRODUCTS_NO_UPDATE);
} }
ProductsDO productsDO = BeanUtils.toBean(updateReqVO, ProductsDO.class);
productsMapper.updateById(updateReqVO); productsMapper.updateById(productsDO);
productsMapper.update(new LambdaUpdateWrapperX<ProductsDO>()
.eq(ProductsDO::getId, updateReqVOId)
.setIfPresent(ProductsDO::getProductName, productName)
.setIfPresent(ProductsDO::getPrice, updateReqVO.getPrice())
.set(ProductsDO::getStatus, updateReqVOStatus)
);
} }
@@ -2,13 +2,13 @@ package com.cf.imes.module.system.service.funds.purchase;
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.organamount.vo.TotalSalesAmountRespVO; import com.cf.imes.module.system.controller.admin.funds.organamount.vo.TotalSalesAmountRespVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails.ProductDetailsRespVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.OrgEarliestPurchaseProductRespVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.OrgEarliestPurchaseProductRespVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordPageReqVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordRespVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordRespVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.SalesDetailRespVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.SalesDetailRespVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.SalesDetailsPageReqVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.SalesDetailsPageReqVO;
import com.cf.imes.module.system.controller.admin.organ.vo.organ.OrganCreateProductPurchaseReqVO; import com.cf.imes.module.system.controller.admin.organ.vo.organ.OrganCreateProductPurchaseReqVO;
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.funds.purchase.PurchaseRecordDO;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -82,12 +82,12 @@ public interface PurchaseService {
void createOrgInitProductPurchaseRecord(Long organId, List<OrganCreateProductPurchaseReqVO> productPurchaseReqVOS); void createOrgInitProductPurchaseRecord(Long organId, List<OrganCreateProductPurchaseReqVO> productPurchaseReqVOS);
/** /**
* 查询组织下的产品购买记录 * 查询组织下的非首购的产品购买记录
* *
* @param organIds * @param organIds
* @return * @return
*/ */
List<PurchaseRecordDO> getOrgPurchaseList(Set<Long> organIds); List<PurchaseRecordDO> getOrgPurchaseNotInitialList(Set<Long> organIds);
/** /**
* 查询销售总额 * 查询销售总额
@@ -131,7 +131,7 @@ public interface PurchaseService {
* @param organId * @param organId
* @return * @return
*/ */
List<ProductsDetailDO> getOrgProductDetailList(Long organId, Long productId); List<ProductDetailsRespVO> getOrgProductDetailList(Long organId, Long productId);
/** /**
* 作废历史组织产品购买记录 * 作废历史组织产品购买记录
@@ -13,6 +13,7 @@ import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.module.system.controller.admin.funds.delay.vo.ProductDelayRespVO; import com.cf.imes.module.system.controller.admin.funds.delay.vo.ProductDelayRespVO;
import com.cf.imes.module.system.controller.admin.funds.organamount.vo.TotalSalesAmountRespVO; import com.cf.imes.module.system.controller.admin.funds.organamount.vo.TotalSalesAmountRespVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails.ProductDetailsPageReqVO; import com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails.ProductDetailsPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.productDetails.ProductDetailsRespVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.OrgEarliestPurchaseProductRespVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.OrgEarliestPurchaseProductRespVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordPageReqVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordRespVO; import com.cf.imes.module.system.controller.admin.funds.purchase.vo.PurchaseRecordRespVO;
@@ -23,7 +24,6 @@ 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.products.ProductsDetailDO;
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO; 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.products.ProductsDetailMapper;
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.PayProductChannelCodeEnum;
@@ -46,7 +46,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
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_PRICE_NULL_ERROR;
import static com.cf.imes.module.system.enums.pay.PayConstants.PAY_PRODUCT_DETAIL_AUTO_GENERATE_MONTH_ID_FORMAT;
/** /**
* @author 软件购买接口 实现类 * @author 软件购买接口 实现类
@@ -58,9 +59,6 @@ public class PurchaseServiceImpl implements PurchaseService {
@Resource @Resource
private PurchaseRecordMapper purchaseRecordMapper; private PurchaseRecordMapper purchaseRecordMapper;
@Resource
private ProductsDetailMapper productsDetailMapper;
@Resource @Resource
private ProductsDetailService productsDetailService; private ProductsDetailService productsDetailService;
@@ -139,7 +137,7 @@ public class PurchaseServiceImpl implements PurchaseService {
// 延期记录是按自增id逆序的,第一条的延期后时间作为产品到期时间 // 延期记录是按自增id逆序的,第一条的延期后时间作为产品到期时间
purchaseRecordDO.setEndTime(recordsByPurchaseId.get(0).getDelayTime()); purchaseRecordDO.setEndTime(recordsByPurchaseId.get(0).getDelayTime());
} }
return new OrgEarliestPurchaseProductRespVO(purchaseRecordDO, productsDetailMapper.selectById(purchaseRecordDO.getProductDetailsId())); return new OrgEarliestPurchaseProductRespVO(purchaseRecordDO);
} }
return null; return null;
} }
@@ -152,53 +150,35 @@ public class PurchaseServiceImpl implements PurchaseService {
@Override @Override
public void createOrgInitProductPurchaseRecord(Long organId, List<OrganCreateProductPurchaseReqVO> productPurchaseReqVOS) { public void createOrgInitProductPurchaseRecord(Long organId, List<OrganCreateProductPurchaseReqVO> productPurchaseReqVOS) {
for (OrganCreateProductPurchaseReqVO purchaseReqVO : productPurchaseReqVOS) { for (OrganCreateProductPurchaseReqVO purchaseReqVO : productPurchaseReqVOS) {
// 1、校验定价 // 1、校验产品
ProductsDetailDO productsDetailDO = productsDetailService.validateProductDetailsExists(purchaseReqVO.getProductDetailsId()); Long productId = purchaseReqVO.getProductId();
// 2、校验产品
Long productId = productsDetailDO.getProductId();
ProductsDO productsDO = productsDetailService.validateProductExist(productId); ProductsDO productsDO = productsDetailService.validateProductExist(productId);
// 3、创建购买记录 // 2、创建购买记录
// 输入的价格 // 输入的价格
BigDecimal price = purchaseReqVO.getPrice(); BigDecimal price = purchaseReqVO.getPrice();
// 月均单价 // 首次订购默认是1年的价格,月均单价/10(赠送两个月)
BigDecimal monthAverageAmount; BigDecimal monthAverageAmount = price.divide(new BigDecimal(10), 2, RoundingMode.DOWN);
// 计算后总价
BigDecimal totalAmount = price;
/**
* ①:首次订购满1年:
* 总价 = 输入金额
* 月均单价 = 输入金额 / (12 * duration)
* ②:不满1年按月份计算
* 总价 = 输入金额
* 月均单价 = 输入金额 / duration
*/
Integer duration = productsDetailDO.getDuration();
switch (ProductDurationUnitEnum.fromStatus(productsDetailDO.getDurationUnit())) {
case YEAR -> monthAverageAmount = price.divide(new BigDecimal(duration * 12), 2, RoundingMode.DOWN);
case MONTH -> monthAverageAmount = price.divide(new BigDecimal(duration), 2, RoundingMode.DOWN);
default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT);
}
long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId(); long purchaseId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
LocalDate now = LocalDate.now();
PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder() PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
.id(purchaseId) .id(purchaseId)
.organId(organId) .organId(organId)
.productId(productId) .productId(productId)
.productName(productsDO.getProductName()) .productName(productsDO.getProductName())
.productDetailsId(productsDetailDO.getId()) .productDetailsId(0L)
.purchaseDuration(duration) .purchaseDuration(1)
.purchaseDurationUnit(productsDetailDO.getDurationUnit()) .purchaseDurationUnit(ProductDurationUnitEnum.YEAR.getCode())
.giftDuration(productsDetailDO.getGiftDuration()) .startTime(now)
.giftDurationUnit(productsDetailDO.getGiftDurationUnit()) // 组织创建时的套餐购买默认过期
.startTime(LocalDate.now()) .endTime(now.minusDays(1))
.endTime(purchaseReqVO.getProductExpireDate())
.paymentMethod(PayProductChannelCodeEnum.ORGAN_CREATE.getCode()) .paymentMethod(PayProductChannelCodeEnum.ORGAN_CREATE.getCode())
.totalAmount(totalAmount) .totalAmount(price)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.originalAmount(price) .originalAmount(price)
.isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
.initial(true) .initial(true)
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus()) .status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
.build(); .build();
@@ -207,10 +187,11 @@ public class PurchaseServiceImpl implements PurchaseService {
} }
@Override @Override
public List<PurchaseRecordDO> getOrgPurchaseList(Set<Long> organIds) { public List<PurchaseRecordDO> getOrgPurchaseNotInitialList(Set<Long> organIds) {
return purchaseRecordMapper.selectList(new LambdaQueryWrapper<PurchaseRecordDO>() return purchaseRecordMapper.selectList(new LambdaQueryWrapper<PurchaseRecordDO>()
.in(PurchaseRecordDO::getOrganId, organIds) .in(PurchaseRecordDO::getOrganId, organIds)
.eq(PurchaseRecordDO::getStatus, PurchaseRecordStatusEnum.ACTIVE.getStatus()) .eq(PurchaseRecordDO::getStatus, PurchaseRecordStatusEnum.ACTIVE.getStatus())
.ne(PurchaseRecordDO::getInitial, true)
.eq(PurchaseRecordDO::getDeleted, false)); .eq(PurchaseRecordDO::getDeleted, false));
} }
@@ -265,9 +246,9 @@ public class PurchaseServiceImpl implements PurchaseService {
} }
@Override @Override
public List<ProductsDetailDO> getOrgProductDetailList(Long organId, Long productId) { public List<ProductDetailsRespVO> getOrgProductDetailList(Long organId, Long productId) {
// 校验产品 // 校验产品
productsDetailService.validateProductExist(productId); ProductsDO productsDO = productsDetailService.validateProductExist(productId);
// 查询产品下的所有定价规则 // 查询产品下的所有定价规则
ProductDetailsPageReqVO pageReqVO = new ProductDetailsPageReqVO(); ProductDetailsPageReqVO pageReqVO = new ProductDetailsPageReqVO();
@@ -278,64 +259,110 @@ public class PurchaseServiceImpl implements PurchaseService {
// 查询组织下的首次订购产品记录 // 查询组织下的首次订购产品记录
PurchaseRecordDO orgInitialPurchaseRecord = getOrgInitialPurchaseRecord(organId, productId); PurchaseRecordDO orgInitialPurchaseRecord = getOrgInitialPurchaseRecord(organId, productId);
if (ObjectUtil.isNull(orgInitialPurchaseRecord)) { if (ObjectUtil.isNull(orgInitialPurchaseRecord)) {
return page.getList(); return getInitialProductDetailList(productsDO, page.getList());
} }
List<ProductsDetailDO> resultList = new ArrayList<>(); List<ProductDetailsRespVO> resultList = new ArrayList<>();
for (ProductsDetailDO detailDO : page.getList()) {
ProductsDetailDO newDetailDO = BeanUtils.toBean(detailDO, ProductsDetailDO.class);
// 月均单价 // 月均单价
BigDecimal monthAverageAmount = orgInitialPurchaseRecord.getMonthAverageAmount(); BigDecimal monthAverageAmount = orgInitialPurchaseRecord.getMonthAverageAmount();
// 1年定价
BigDecimal totalAmount = orgInitialPurchaseRecord.getTotalAmount();
// 定价中有的,以年数量*组织定价*折扣展示
List<ProductDetailsRespVO> productDetailRespVOList = new ArrayList<>();
for (ProductsDetailDO detailDO : page.getList()) {
ProductDetailsRespVO newDetailRespVO = BeanUtils.toBean(detailDO, ProductDetailsRespVO.class);
// 折扣 // 折扣
BigDecimal discount = detailDO.getDiscount(); BigDecimal discount = detailDO.getDiscount();
// 需要支付的总价
BigDecimal totalAmount;
// 重新计算购买价格 // 重新计算购买价格
switch (ProductDurationUnitEnum.fromStatus(detailDO.getDurationUnit())) { switch (ProductDurationUnitEnum.fromStatus(detailDO.getDurationUnit())) {
case YEAR -> totalAmount = monthAverageAmount case YEAR -> {
.multiply(new BigDecimal(10)) Integer duration = detailDO.getDuration();
.multiply(new BigDecimal(detailDO.getDuration())) // 原价
.multiply(discount) BigDecimal originalPrice = totalAmount.multiply(BigDecimal.valueOf(duration));
.divide(new BigDecimal(100), 2, RoundingMode.DOWN); newDetailRespVO.setOriginalPrice(originalPrice);
case MONTH -> { // 折扣价格
int months = detailDO.getDuration(); newDetailRespVO.setCurrentPrice(originalPrice.multiply(discount).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN));
if (months >= 12) { productDetailRespVOList.add(newDetailRespVO);
// 转换成年 + 剩余月 }
int years = months / 12; default -> {
int remainMonths = months % 12; }
}
}
// 默认展示1到10月的价格
for (int i = 1; i <= 10; i++) {
ProductDetailsRespVO detailRespVO = new ProductDetailsRespVO();
detailRespVO.setId(Long.parseLong(String.format(PAY_PRODUCT_DETAIL_AUTO_GENERATE_MONTH_ID_FORMAT, i)));
detailRespVO.setProductId(productId);
detailRespVO.setDuration(i);
detailRespVO.setDurationUnit(ProductDurationUnitEnum.MONTH.getCode());
// 价格:月均单价*月数量
BigDecimal price = monthAverageAmount.multiply(BigDecimal.valueOf(i));
// 有一年的折扣要把月价格做折算
detailRespVO.setCurrentPrice(price);
detailRespVO.setOriginalPrice(price);
resultList.add(detailRespVO);
}
resultList.addAll(productDetailRespVOList);
// 整年部分:用年逻辑(10 个月价) return resultList;
BigDecimal yearPart = monthAverageAmount }
.multiply(new BigDecimal(10))
.multiply(new BigDecimal(years))
.multiply(discount)
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
// 剩余月部分:用月逻辑 /**
BigDecimal monthPart = remainMonths == 0 * 拼接没有首次购买记录的定价方式列表
? BigDecimal.ZERO *
: monthAverageAmount * @param productsDO
.multiply(new BigDecimal(remainMonths)) * @param productsDetailDOS
.multiply(discount) * @return
.divide(new BigDecimal(100), 2, RoundingMode.DOWN); */
private List<ProductDetailsRespVO> getInitialProductDetailList(ProductsDO productsDO, List<ProductsDetailDO> productsDetailDOS) {
List<ProductDetailsRespVO> resultList = new ArrayList<>();
// 没有首次购买记录的产品,展示的总价是产品建议价格
// 1年定价
BigDecimal totalAmount = productsDO.getPrice();
if(ObjectUtil.isNull(totalAmount)) {
throw new ServiceException(PRODUCT_PRICE_NULL_ERROR);
}
// 月均单价
BigDecimal monthAverageAmount = totalAmount.divide(new BigDecimal(10), 2, RoundingMode.DOWN);
totalAmount = yearPart.add(monthPart); List<ProductDetailsRespVO> productDetailRespVOList = new ArrayList<>();
} else { for (ProductsDetailDO detailDO : productsDetailDOS) {
// 小于 12 个月直接按月逻辑 ProductDetailsRespVO newDetailRespVO = BeanUtils.toBean(detailDO, ProductDetailsRespVO.class);
totalAmount = monthAverageAmount // 折扣
.multiply(new BigDecimal(months)) BigDecimal discount = detailDO.getDiscount();
.multiply(discount) // 重新计算购买价格
.divide(new BigDecimal(100), 2, RoundingMode.DOWN); switch (ProductDurationUnitEnum.fromStatus(detailDO.getDurationUnit())) {
} case YEAR -> {
Integer duration = detailDO.getDuration();
// 原价
BigDecimal originalPrice = totalAmount.multiply(BigDecimal.valueOf(duration));
newDetailRespVO.setOriginalPrice(originalPrice);
// 折扣价格
newDetailRespVO.setCurrentPrice(originalPrice.multiply(discount).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN));
productDetailRespVOList.add(newDetailRespVO);
}
default -> {
} }
default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT);
} }
newDetailDO.setCurrentPrice(totalAmount);
newDetailDO.setOriginalPrice(totalAmount);
resultList.add(newDetailDO);
} }
// 默认展示1到10月的价格
for (int i = 1; i <= 10; i++) {
ProductDetailsRespVO detailRespVO = new ProductDetailsRespVO();
detailRespVO.setId(Long.parseLong(String.format(PAY_PRODUCT_DETAIL_AUTO_GENERATE_MONTH_ID_FORMAT, i)));
detailRespVO.setProductId(productsDO.getId());
detailRespVO.setDuration(i);
detailRespVO.setDurationUnit(ProductDurationUnitEnum.MONTH.getCode());
// 价格:月均单价*月数量
BigDecimal price = monthAverageAmount.multiply(BigDecimal.valueOf(i));
// 有一年的折扣要把月价格做折算
detailRespVO.setCurrentPrice(price);
detailRespVO.setOriginalPrice(price);
resultList.add(detailRespVO);
}
resultList.addAll(productDetailRespVOList);
return resultList; return resultList;
} }
@@ -38,6 +38,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.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;
import com.cf.imes.module.system.controller.admin.pay.vo.PayProductOrderUnifiedReqVO; import com.cf.imes.module.system.controller.admin.pay.vo.PayProductOrderUnifiedReqVO;
@@ -111,6 +112,9 @@ public class PayOrderServiceImpl implements PayOrderService {
// 外部payNo正则 // 外部payNo正则
private static final Pattern pattern = Pattern.compile("P(\\d+)O"); private static final Pattern pattern = Pattern.compile("P(\\d+)O");
// 自动生成定价id正则
private static final Pattern productDetailIdPattern = Pattern.compile("^1759029544(\\d+)$");
@Resource @Resource
private PayHandlerFactory payHandlerFactory; private PayHandlerFactory payHandlerFactory;
@@ -169,10 +173,12 @@ public class PayOrderServiceImpl implements PayOrderService {
ValidationUtils.validate(validator, payProductOrderUnifiedReqVO); ValidationUtils.validate(validator, payProductOrderUnifiedReqVO);
// 校验产品和产品明细 // 校验产品和产品明细
ProductsDetailDO productsDetailDO = productsDetailService.validateProductDetailsExists(payProductOrderUnifiedReqVO.getProductDetailsId()); Long productId = payProductOrderUnifiedReqVO.getProductId();
Long productId = productsDetailDO.getProductId();
ProductsDO productsDO = productsDetailService.validateProductExist(productId); ProductsDO productsDO = productsDetailService.validateProductExist(productId);
// 解析产品定价明细
ProductsDetailDO productsDetailDO = analyzeProductDetail(productId, payProductOrderUnifiedReqVO.getProductDetailsId());
// 检查机构产品购买锁 // 检查机构产品购买锁
String purchaseLockKey = String.format(RedisKeyConstants.PRODUCT_PURCHASE_LOCK_KEY, organId, productId); String purchaseLockKey = String.format(RedisKeyConstants.PRODUCT_PURCHASE_LOCK_KEY, organId, productId);
boolean lockResult = redisLockUtil.redissonLock(purchaseLockKey, chenfengCacheProperties.getLockWaitTime(), chenfengCacheProperties.getLockTimeout()); boolean lockResult = redisLockUtil.redissonLock(purchaseLockKey, chenfengCacheProperties.getLockWaitTime(), chenfengCacheProperties.getLockTimeout());
@@ -250,6 +256,30 @@ public class PayOrderServiceImpl implements PayOrderService {
} }
} }
/**
* 解析定价规则,年从数据库取,月就手动拼
*
* @param productDetailId
* @return
*/
private ProductsDetailDO analyzeProductDetail(Long productId, Long productDetailId) {
// productDetailId符合规则PPAG%d
Matcher matcher = productDetailIdPattern.matcher(String.valueOf(productDetailId));
if (matcher.find()) {
// 取PPAG后的数字,是订购月份
String monthDuration = matcher.group(1);
ProductsDetailDO monthDetail = new ProductsDetailDO();
monthDetail.setId(productDetailId);
monthDetail.setDuration(Integer.parseInt(monthDuration));
monthDetail.setDurationUnit(ProductDurationUnitEnum.MONTH.getCode());
// 获取产品下的1年的折扣
monthDetail.setDiscount(productsDetailService.getProductDetailOneYearDiscount(productId));
return monthDetail;
} else {
return productsDetailService.validateProductDetailsExists(productDetailId);
}
}
/** /**
* 处理前序购买记录: * 处理前序购买记录:
* 1、需要第三方支付,当前购买记录状态为待支付,等第三方回调/超期轮训处理到的时候标记为成功还是失败 * 1、需要第三方支付,当前购买记录状态为待支付,等第三方回调/超期轮训处理到的时候标记为成功还是失败
@@ -94,6 +94,9 @@ public class PayProductProcessor {
// 本地订购产品的月均单价 // 本地订购产品的月均单价
private BigDecimal monthAverageAmount = BigDecimal.ZERO; private BigDecimal monthAverageAmount = BigDecimal.ZERO;
// 原始价格
private BigDecimal originalPrice;
public PayProductProcessor(ProductsDO productsDO, ProductsDetailDO productsDetailDO, PurchaseRecordDO orgInitialPurchaseRecord, PreviouProductDelayRespVO previouProductDelayRespVO, public PayProductProcessor(ProductsDO productsDO, ProductsDetailDO productsDetailDO, PurchaseRecordDO orgInitialPurchaseRecord, PreviouProductDelayRespVO previouProductDelayRespVO,
OrganAmountDO organAmountDO, Long organId, ChenfengPayConfig payConfig, MybatisIdProperties mybatisIdProperties) { OrganAmountDO organAmountDO, Long organId, ChenfengPayConfig payConfig, MybatisIdProperties mybatisIdProperties) {
this.productsDO = productsDO; this.productsDO = productsDO;
@@ -104,8 +107,7 @@ public class PayProductProcessor {
previousPurchaseRecord = previouProductDelayRespVO.getPreviouPurchaseRecord(); previousPurchaseRecord = previouProductDelayRespVO.getPreviouPurchaseRecord();
// 上一次订购产品的结束时间进行计算 // 上一次订购产品的结束时间进行计算
endTime = previousPurchaseRecord.getEndTime(); endTime = previousPurchaseRecord.getEndTime();
endTime = calculateEndTime(productsDetailDO.getDuration(), productsDetailDO.getDurationUnit(), endTime = calculateEndTime(productsDetailDO.getDuration(), productsDetailDO.getDurationUnit());
productsDetailDO.getGiftDuration(), productsDetailDO.getGiftDurationUnit());
} }
this.organAmountDO = organAmountDO; this.organAmountDO = organAmountDO;
this.organId = organId; this.organId = organId;
@@ -125,9 +127,7 @@ public class PayProductProcessor {
if (initialPurchaseExist) { if (initialPurchaseExist) {
price = computePrice(orgInitialPurchaseRecord, productsDetailDO); price = computePrice(orgInitialPurchaseRecord, productsDetailDO);
} else { } else {
price = productsDetailDO.getCurrentPrice(); price = computeInitialProductPayAmount();
// 首次订购计算月均单价
monthAverageAmount = computeInitialProductPayMonthAverageAmount(price);
// 首次购买记录是空,那么当前就是首次订购 // 首次购买记录是空,那么当前就是首次订购
initialized = true; initialized = true;
} }
@@ -184,13 +184,11 @@ public class PayProductProcessor {
.productDetailsId(productsDetailDO.getId()) .productDetailsId(productsDetailDO.getId())
.purchaseDuration(productsDetailDO.getDuration()) .purchaseDuration(productsDetailDO.getDuration())
.purchaseDurationUnit(productsDetailDO.getDurationUnit()) .purchaseDurationUnit(productsDetailDO.getDurationUnit())
.giftDuration(productsDetailDO.getGiftDuration())
.giftDurationUnit(productsDetailDO.getGiftDurationUnit())
.startTime(startTime) .startTime(startTime)
.endTime(endTime) .endTime(endTime)
.paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode()) .paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode())
.totalAmount(price) .totalAmount(price)
.originalAmount(productsDetailDO.getCurrentPrice()) .originalAmount(originalPrice)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.accountBalanceSpent(price) .accountBalanceSpent(price)
.isInvocing(InvoiceStatusEnum.INVOICABLE.getCode()) .isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
@@ -244,13 +242,11 @@ public class PayProductProcessor {
.productDetailsId(productsDetailDO.getId()) .productDetailsId(productsDetailDO.getId())
.purchaseDuration(productsDetailDO.getDuration()) .purchaseDuration(productsDetailDO.getDuration())
.purchaseDurationUnit(productsDetailDO.getDurationUnit()) .purchaseDurationUnit(productsDetailDO.getDurationUnit())
.giftDuration(productsDetailDO.getGiftDuration())
.giftDurationUnit(productsDetailDO.getGiftDurationUnit())
.startTime(startTime) .startTime(startTime)
.endTime(endTime) .endTime(endTime)
.paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode()) .paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode())
.totalAmount(price) .totalAmount(price)
.originalAmount(productsDetailDO.getCurrentPrice()) .originalAmount(originalPrice)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.accountBalanceSpent(price) .accountBalanceSpent(price)
.isInvocing(InvoiceStatusEnum.INVOICABLE.getCode()) .isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
@@ -301,13 +297,11 @@ public class PayProductProcessor {
.productDetailsId(productsDetailDO.getId()) .productDetailsId(productsDetailDO.getId())
.purchaseDuration(productsDetailDO.getDuration()) .purchaseDuration(productsDetailDO.getDuration())
.purchaseDurationUnit(productsDetailDO.getDurationUnit()) .purchaseDurationUnit(productsDetailDO.getDurationUnit())
.giftDuration(productsDetailDO.getGiftDuration())
.giftDurationUnit(productsDetailDO.getGiftDurationUnit())
.startTime(startTime) .startTime(startTime)
.endTime(endTime) .endTime(endTime)
.paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode()) .paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode())
.totalAmount(price) .totalAmount(price)
.originalAmount(productsDetailDO.getCurrentPrice()) .originalAmount(originalPrice)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.alipaySpent(price) .alipaySpent(price)
.initial(initialized) .initial(initialized)
@@ -340,13 +334,11 @@ public class PayProductProcessor {
.productDetailsId(productsDetailDO.getId()) .productDetailsId(productsDetailDO.getId())
.purchaseDuration(productsDetailDO.getDuration()) .purchaseDuration(productsDetailDO.getDuration())
.purchaseDurationUnit(productsDetailDO.getDurationUnit()) .purchaseDurationUnit(productsDetailDO.getDurationUnit())
.giftDuration(productsDetailDO.getGiftDuration())
.giftDurationUnit(productsDetailDO.getGiftDurationUnit())
.startTime(startTime) .startTime(startTime)
.endTime(endTime) .endTime(endTime)
.paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode()) .paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode())
.totalAmount(price) .totalAmount(price)
.originalAmount(productsDetailDO.getCurrentPrice()) .originalAmount(originalPrice)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.wechatSpent(price) .wechatSpent(price)
.initial(initialized) .initial(initialized)
@@ -384,13 +376,11 @@ public class PayProductProcessor {
.productDetailsId(productsDetailDO.getId()) .productDetailsId(productsDetailDO.getId())
.purchaseDuration(productsDetailDO.getDuration()) .purchaseDuration(productsDetailDO.getDuration())
.purchaseDurationUnit(productsDetailDO.getDurationUnit()) .purchaseDurationUnit(productsDetailDO.getDurationUnit())
.giftDuration(productsDetailDO.getGiftDuration())
.giftDurationUnit(productsDetailDO.getGiftDurationUnit())
.startTime(startTime) .startTime(startTime)
.endTime(endTime) .endTime(endTime)
.paymentMethod(PayProductChannelCodeEnum.BALANCE_AND_GIFT.getCode()) .paymentMethod(PayProductChannelCodeEnum.BALANCE_AND_GIFT.getCode())
.totalAmount(price) .totalAmount(price)
.originalAmount(productsDetailDO.getCurrentPrice()) .originalAmount(originalPrice)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.isInvocing(InvoiceStatusEnum.INVOICABLE.getCode()) .isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
.initial(initialized) .initial(initialized)
@@ -464,13 +454,11 @@ public class PayProductProcessor {
.productDetailsId(productsDetailDO.getId()) .productDetailsId(productsDetailDO.getId())
.purchaseDuration(productsDetailDO.getDuration()) .purchaseDuration(productsDetailDO.getDuration())
.purchaseDurationUnit(productsDetailDO.getDurationUnit()) .purchaseDurationUnit(productsDetailDO.getDurationUnit())
.giftDuration(productsDetailDO.getGiftDuration())
.giftDurationUnit(productsDetailDO.getGiftDurationUnit())
.startTime(startTime) .startTime(startTime)
.endTime(endTime) .endTime(endTime)
.paymentMethod(PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum) ? PayProductChannelCodeEnum.BALANCE_AND_GIFT_AND_ALIPAY.getCode() : PayProductChannelCodeEnum.BALANCE_AND_GIFT_AND_WECHAT.getCode()) .paymentMethod(PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum) ? PayProductChannelCodeEnum.BALANCE_AND_GIFT_AND_ALIPAY.getCode() : PayProductChannelCodeEnum.BALANCE_AND_GIFT_AND_WECHAT.getCode())
.totalAmount(price) .totalAmount(price)
.originalAmount(productsDetailDO.getCurrentPrice()) .originalAmount(originalPrice)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.initial(initialized) .initial(initialized)
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus()) .status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
@@ -543,13 +531,11 @@ public class PayProductProcessor {
.productDetailsId(productsDetailDO.getId()) .productDetailsId(productsDetailDO.getId())
.purchaseDuration(productsDetailDO.getDuration()) .purchaseDuration(productsDetailDO.getDuration())
.purchaseDurationUnit(productsDetailDO.getDurationUnit()) .purchaseDurationUnit(productsDetailDO.getDurationUnit())
.giftDuration(productsDetailDO.getGiftDuration())
.giftDurationUnit(productsDetailDO.getGiftDurationUnit())
.startTime(startTime) .startTime(startTime)
.endTime(endTime) .endTime(endTime)
.paymentMethod(PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum) ? PayProductChannelCodeEnum.GIFT_AND_ALIPAY.getCode() : PayProductChannelCodeEnum.GIFT_AND_WECHAT.getCode()) .paymentMethod(PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum) ? PayProductChannelCodeEnum.GIFT_AND_ALIPAY.getCode() : PayProductChannelCodeEnum.GIFT_AND_WECHAT.getCode())
.totalAmount(price) .totalAmount(price)
.originalAmount(productsDetailDO.getCurrentPrice()) .originalAmount(originalPrice)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.initial(initialized) .initial(initialized)
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus()) .status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
@@ -624,13 +610,11 @@ public class PayProductProcessor {
.productDetailsId(productsDetailDO.getId()) .productDetailsId(productsDetailDO.getId())
.purchaseDuration(productsDetailDO.getDuration()) .purchaseDuration(productsDetailDO.getDuration())
.purchaseDurationUnit(productsDetailDO.getDurationUnit()) .purchaseDurationUnit(productsDetailDO.getDurationUnit())
.giftDuration(productsDetailDO.getGiftDuration())
.giftDurationUnit(productsDetailDO.getGiftDurationUnit())
.startTime(startTime) .startTime(startTime)
.endTime(endTime) .endTime(endTime)
.paymentMethod(PayProductChannelCodeEnum.BALANCE_AND_GIFT.getCode()) .paymentMethod(PayProductChannelCodeEnum.BALANCE_AND_GIFT.getCode())
.totalAmount(price) .totalAmount(price)
.originalAmount(productsDetailDO.getCurrentPrice()) .originalAmount(originalPrice)
.monthAverageAmount(monthAverageAmount) .monthAverageAmount(monthAverageAmount)
.initial(initialized) .initial(initialized)
.status(PurchaseRecordStatusEnum.ACTIVE.getStatus()) .status(PurchaseRecordStatusEnum.ACTIVE.getStatus())
@@ -723,12 +707,9 @@ public class PayProductProcessor {
* *
* @param duration * @param duration
* @param durationUnit * @param durationUnit
* @param giftDuration
* @param giftDurationUnit
* @return * @return
*/ */
private LocalDate calculateEndTime(Integer duration, Integer durationUnit, private LocalDate calculateEndTime(Integer duration, Integer durationUnit) {
Integer giftDuration, Integer giftDurationUnit) {
LocalDate today = LocalDate.now(); LocalDate today = LocalDate.now();
// 1、 计算延期后的实际有效期 // 1、 计算延期后的实际有效期
LocalDate maxDelayEnd = endTime; LocalDate maxDelayEnd = endTime;
@@ -758,11 +739,6 @@ public class PayProductProcessor {
endTime = addTime(purchaseStart, duration, durationUnit); endTime = addTime(purchaseStart, duration, durationUnit);
} }
// 加赠送时间
if (ObjectUtil.isNotNull(giftDuration) && giftDurationUnit != null) {
endTime = addTime(endTime, giftDuration, giftDurationUnit);
}
// 5. 扣掉延期天数 // 5. 扣掉延期天数
long delayDays = 0L; long delayDays = 0L;
if (CollUtil.isNotEmpty(delayRespVOList)) { if (CollUtil.isNotEmpty(delayRespVOList)) {
@@ -889,7 +865,7 @@ public class PayProductProcessor {
} }
/** /**
* 计算产品价格 * 计算有首购记录的产品价格
* *
* @param orgInitialPurchaseRecord 首次购买记录 * @param orgInitialPurchaseRecord 首次购买记录
* @param productsDetailDO 本次购买产品的定价规则 * @param productsDetailDO 本次购买产品的定价规则
@@ -899,52 +875,35 @@ public class PayProductProcessor {
// 月均单价 // 月均单价
monthAverageAmount = orgInitialPurchaseRecord.getMonthAverageAmount(); monthAverageAmount = orgInitialPurchaseRecord.getMonthAverageAmount();
// 首购的定价
BigDecimal initialPurchaseRecordTotalAmount = orgInitialPurchaseRecord.getTotalAmount();
// 定价规则的折扣 // 定价规则的折扣
BigDecimal discount = productsDetailDO.getDiscount(); BigDecimal discount = productsDetailDO.getDiscount();
BigDecimal totalAmount; BigDecimal totalAmount;
/** /**
* ①:续费订购满1年: * ①:续费订购满1年:
* 总价 = 月均单价 * 10 * duration * 折扣 /100,保留两位小数 * 原始价格 = 订购总价 * duration
* 现价 = 原始价格 * 折扣 /100,保留两位小数
* ②:不满1年按月份计算 * ②:不满1年按月份计算
* 总价 = 月均单价 * duration * 折扣 /100,保留两位小数 * 原始价格 = 月均单价 * duration
* 现价 = 原始价格 * 折扣 /100,保留两位小数
*/ */
switch (ProductDurationUnitEnum.fromStatus(productsDetailDO.getDurationUnit())) { switch (ProductDurationUnitEnum.fromStatus(productsDetailDO.getDurationUnit())) {
case YEAR -> totalAmount = monthAverageAmount case YEAR -> {
.multiply(new BigDecimal(10)) originalPrice = initialPurchaseRecordTotalAmount
.multiply(new BigDecimal(productsDetailDO.getDuration())) .multiply(BigDecimal.valueOf(productsDetailDO.getDuration()));
.multiply(discount) totalAmount = originalPrice
.divide(new BigDecimal(100), 2, RoundingMode.DOWN); .multiply(discount)
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
}
case MONTH -> { case MONTH -> {
int months = productsDetailDO.getDuration(); originalPrice = monthAverageAmount
if (months >= 12) { .multiply(BigDecimal.valueOf(productsDetailDO.getDuration()));
// 转换成年 + 剩余月 totalAmount = originalPrice
int years = months / 12; .multiply(discount)
int remainMonths = months % 12; .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
// 整年部分:用年逻辑(10 个月价)
BigDecimal yearPart = monthAverageAmount
.multiply(new BigDecimal(10))
.multiply(new BigDecimal(years))
.multiply(discount)
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
// 剩余月部分:用月逻辑
BigDecimal monthPart = remainMonths == 0
? BigDecimal.ZERO
: monthAverageAmount
.multiply(new BigDecimal(remainMonths))
.multiply(discount)
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
totalAmount = yearPart.add(monthPart);
} else {
// 小于 12 个月直接按月逻辑
totalAmount = monthAverageAmount
.multiply(new BigDecimal(months))
.multiply(discount)
.divide(new BigDecimal(100), 2, RoundingMode.DOWN);
}
} }
default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT); default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT);
} }
@@ -953,17 +912,37 @@ public class PayProductProcessor {
} }
/** /**
* 计算首次订购的月均单 * 计算首次订购的价
*/ */
private BigDecimal computeInitialProductPayMonthAverageAmount(BigDecimal price) { private BigDecimal computeInitialProductPayAmount() {
// 订购数量
Integer duration = productsDetailDO.getDuration(); Integer duration = productsDetailDO.getDuration();
BigDecimal monthAverageAmount; // 产品订购价格
BigDecimal orgPurchasePrice = productsDO.getPrice();
// 折扣
BigDecimal discount = productsDetailDO.getDiscount();
BigDecimal totalAmount;
switch (ProductDurationUnitEnum.fromStatus(productsDetailDO.getDurationUnit())) { switch (ProductDurationUnitEnum.fromStatus(productsDetailDO.getDurationUnit())) {
case YEAR -> monthAverageAmount = price.divide(new BigDecimal(duration * 12), 2, RoundingMode.DOWN); case YEAR -> {
case MONTH -> monthAverageAmount = price.divide(new BigDecimal(duration), 2, RoundingMode.DOWN); // 原价
originalPrice = orgPurchasePrice.multiply(BigDecimal.valueOf(duration));
// 折扣价格 = 原价 * 折扣
totalAmount = originalPrice.multiply(discount);
// 月均单价 = 折扣价格 / 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);
}
default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT); default -> throw new ServiceException(PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT);
} }
return monthAverageAmount; return totalAmount;
} }
} }
@@ -20,7 +20,7 @@ public class ProductDurationUnitValidator implements ConstraintValidator<Product
if (ObjectUtil.isNull(value)) { if (ObjectUtil.isNull(value)) {
return true; return true;
} }
for (ProductDurationUnitEnum unitEnum : ProductDurationUnitEnum.values()) { for (ProductDurationUnitEnum unitEnum : ProductDurationUnitEnum.validArr()) {
if (Objects.equals(unitEnum.getCode(), value)) { if (Objects.equals(unitEnum.getCode(), value)) {
return true; return true;
} }
@@ -39,7 +39,7 @@
END END
) AS invoiced ) AS invoiced
from purchase_record from purchase_record
where organ_id = #{organId} and deleted = false; where organ_id = #{organId} and deleted = false and initial != true;
</select> </select>
<select id="getInvoicableAmount" resultType="java.math.BigDecimal"> <select id="getInvoicableAmount" resultType="java.math.BigDecimal">
@@ -58,7 +58,7 @@
sum(total_amount - gift_money_spent) as amount, sum(total_amount - gift_money_spent) as amount,
sum(gift_money_spent) as giftAmount sum(gift_money_spent) as giftAmount
from purchase_record from purchase_record
where deleted = false; where deleted = false and initial != true;
</select> </select>
<select id="getTotalRechargeUsageAmount" resultType="java.math.BigDecimal"> <select id="getTotalRechargeUsageAmount" resultType="java.math.BigDecimal">