feat(subscription): 新增订阅订单价格计算接口
- 添加 SubscriptionOrderController 控制器,提供 /calculate-price 接口 - 实现订阅价格试算逻辑,支持续费、升级及支付方式系数计算 - 新增 SubscriptionOrderParam 入参类,定义请求参数结构 - 新增 SubscriptionPriceResult 返回结果类,封装价格计算结果 - 支持从配置中读取套餐价格、折扣因子及支付方式调整系数 - 添加多配置键兼容机制,增强系统健壮性 - 提供详细的价格计算说明与异常提示信息
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
package com.gxwebsoft.common.system.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.common.core.web.ApiResult;
|
||||
import com.gxwebsoft.common.core.web.BaseController;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import com.gxwebsoft.common.system.param.SubscriptionOrderParam;
|
||||
import com.gxwebsoft.common.system.result.SubscriptionPriceResult;
|
||||
import com.gxwebsoft.common.system.service.SettingService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 订阅订单接口
|
||||
*/
|
||||
@Tag(name = "订阅订单")
|
||||
@RestController
|
||||
@RequestMapping("/api/system/subscription-order")
|
||||
public class SubscriptionOrderController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private SettingService settingService;
|
||||
|
||||
@Operation(summary = "计算订阅订单价格")
|
||||
@PostMapping("/calculate-price")
|
||||
public ApiResult<SubscriptionPriceResult> calculatePrice(@RequestBody SubscriptionOrderParam param) {
|
||||
final User loginUser = getLoginUser();
|
||||
if (loginUser == null) {
|
||||
return fail("请先登录", null);
|
||||
}
|
||||
if (param.getPackageId() == null) {
|
||||
return fail("套餐ID不能为空", null);
|
||||
}
|
||||
|
||||
JSONObject config = loadSubscriptionConfig();
|
||||
final SubscriptionPriceResult result = buildPriceResult(param, config);
|
||||
return success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置表读取订阅套餐配置(尝试多种key以兼容历史数据)
|
||||
*/
|
||||
private JSONObject loadSubscriptionConfig() {
|
||||
final List<String> keys = Arrays.asList("subscription", "subscription-package", "subscriptionOrder");
|
||||
for (String key : keys) {
|
||||
try {
|
||||
JSONObject cfg = settingService.getBySettingKey(key);
|
||||
if (cfg != null) {
|
||||
return cfg;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return new JSONObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算价格结果
|
||||
*/
|
||||
private SubscriptionPriceResult buildPriceResult(SubscriptionOrderParam param, JSONObject config) {
|
||||
BigDecimal originalPrice = resolveBasePrice(param.getPackageId(), config);
|
||||
BigDecimal factor = BigDecimal.ONE;
|
||||
StringBuilder remark = new StringBuilder();
|
||||
|
||||
if (config == null || config.isEmpty()) {
|
||||
remark.append("未查询到订阅价格配置,已按0元试算;");
|
||||
}
|
||||
if (originalPrice.compareTo(BigDecimal.ZERO) == 0 && config != null && !config.isEmpty()) {
|
||||
remark.append("未找到套餐价格,已按0元试算;");
|
||||
}
|
||||
|
||||
if (isTrue(param.getIsRenewal())) {
|
||||
BigDecimal renewalFactor = resolveFactor(config, "renewalDiscount");
|
||||
factor = factor.multiply(renewalFactor);
|
||||
remark.append("续费系数:").append(renewalFactor).append(";");
|
||||
}
|
||||
|
||||
if (isTrue(param.getIsUpgrade())) {
|
||||
BigDecimal upgradeFactor = resolveFactor(config, "upgradeDiscount");
|
||||
factor = factor.multiply(upgradeFactor);
|
||||
remark.append("升级系数:").append(upgradeFactor).append(";");
|
||||
}
|
||||
|
||||
BigDecimal payTypeFactor = resolvePayTypeFactor(config, param.getPayType());
|
||||
factor = factor.multiply(payTypeFactor);
|
||||
if (param.getPayType() != null) {
|
||||
remark.append("支付系数:").append(payTypeFactor).append(";");
|
||||
}
|
||||
|
||||
BigDecimal payPrice = originalPrice.multiply(factor).setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal discount = originalPrice.subtract(payPrice);
|
||||
if (discount.compareTo(BigDecimal.ZERO) < 0) {
|
||||
discount = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
SubscriptionPriceResult result = new SubscriptionPriceResult();
|
||||
result.setPackageId(param.getPackageId());
|
||||
result.setIsRenewal(param.getIsRenewal());
|
||||
result.setIsUpgrade(param.getIsUpgrade());
|
||||
result.setPayType(param.getPayType());
|
||||
result.setOriginalPrice(originalPrice.setScale(2, RoundingMode.HALF_UP));
|
||||
result.setTotalPrice(originalPrice.setScale(2, RoundingMode.HALF_UP));
|
||||
result.setPayPrice(payPrice);
|
||||
result.setDiscountAmount(discount.setScale(2, RoundingMode.HALF_UP));
|
||||
result.setRemark(remark.toString());
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isTrue(Integer value) {
|
||||
return value != null && value.equals(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析套餐价格,支持 packages 数组、priceMap 或单价配置
|
||||
*/
|
||||
private BigDecimal resolveBasePrice(Integer packageId, JSONObject config) {
|
||||
if (config != null) {
|
||||
JSONArray packages = config.getJSONArray("packages");
|
||||
if (packages != null) {
|
||||
for (Object item : packages) {
|
||||
if (item instanceof JSONObject) {
|
||||
JSONObject pkg = (JSONObject) item;
|
||||
Integer id = pkg.getInteger("id");
|
||||
if (id == null) {
|
||||
id = pkg.getInteger("packageId");
|
||||
}
|
||||
if (packageId.equals(id)) {
|
||||
BigDecimal price = pkg.getBigDecimal("price");
|
||||
if (price != null) {
|
||||
return price;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject priceMap = config.getJSONObject("priceMap");
|
||||
if (priceMap != null) {
|
||||
BigDecimal price = priceMap.getBigDecimal(packageId.toString());
|
||||
if (price != null) {
|
||||
return price;
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal fallbackPrice = config.getBigDecimal("price");
|
||||
if (fallbackPrice != null) {
|
||||
return fallbackPrice;
|
||||
}
|
||||
}
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取折扣系数,默认 1
|
||||
*/
|
||||
private BigDecimal resolveFactor(JSONObject config, String key) {
|
||||
if (config != null) {
|
||||
BigDecimal factor = config.getBigDecimal(key);
|
||||
if (factor != null) {
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
return BigDecimal.ONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付方式系数,可在配置中通过 payTypeDiscount 或 payTypeAdjustments 定义
|
||||
*/
|
||||
private BigDecimal resolvePayTypeFactor(JSONObject config, Integer payType) {
|
||||
if (config != null && payType != null) {
|
||||
JSONObject payTypeDiscount = config.getJSONObject("payTypeDiscount");
|
||||
if (payTypeDiscount == null) {
|
||||
payTypeDiscount = config.getJSONObject("payTypeAdjustments");
|
||||
}
|
||||
if (payTypeDiscount != null) {
|
||||
BigDecimal factor = payTypeDiscount.getBigDecimal(payType.toString());
|
||||
if (factor != null) {
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
return BigDecimal.ONE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.gxwebsoft.common.system.param;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 订阅订单价格试算入参
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "订阅订单价格试算参数")
|
||||
public class SubscriptionOrderParam {
|
||||
|
||||
@Schema(description = "是否续费,1=续费 0=新购")
|
||||
private Integer isRenewal;
|
||||
|
||||
@Schema(description = "是否升级,1=升级 0=非升级")
|
||||
private Integer isUpgrade;
|
||||
|
||||
@Schema(description = "套餐ID")
|
||||
private Integer packageId;
|
||||
|
||||
@Schema(description = "支付方式")
|
||||
private Integer payType;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.gxwebsoft.common.system.result;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 订阅订单价格试算结果
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "订阅订单价格试算结果")
|
||||
public class SubscriptionPriceResult {
|
||||
|
||||
@Schema(description = "套餐ID")
|
||||
private Integer packageId;
|
||||
|
||||
@Schema(description = "是否续费,1=续费 0=新购")
|
||||
private Integer isRenewal;
|
||||
|
||||
@Schema(description = "是否升级,1=升级 0=非升级")
|
||||
private Integer isUpgrade;
|
||||
|
||||
@Schema(description = "支付方式")
|
||||
private Integer payType;
|
||||
|
||||
@Schema(description = "原价")
|
||||
private BigDecimal originalPrice;
|
||||
|
||||
@Schema(description = "优惠金额")
|
||||
private BigDecimal discountAmount;
|
||||
|
||||
@Schema(description = "应付金额")
|
||||
private BigDecimal payPrice;
|
||||
|
||||
@Schema(description = "总价(同原价,用于兼容前端字段)")
|
||||
private BigDecimal totalPrice;
|
||||
|
||||
@Schema(description = "价格说明")
|
||||
private String remark;
|
||||
}
|
||||
Reference in New Issue
Block a user