feat(pricing): 添加一口价购买功能

- 新增 OnePricePurchaseController 控制器
- 新增 OnePriceConfigFilterRequest、OnePriceConfigRequest、OnePriceInfo等 DTO 类
- 新增 PriceOnePriceConfig 实体类和对应的 Mapper 接口
- 实现 OnePricePurchaseDiscountProvider 优惠提供者
- 实现 OnePricePurchaseServiceImpl 服务实现类
-定义 IOnePricePurchaseService服务接口
- 优化 DiscountDetail 类,添加创建一口价折扣的方法
- 修改 CLAUDE.md,将 error 方法改为 fail 方法
This commit is contained in:
2025-09-05 11:09:54 +08:00
parent 5210b50adb
commit 50c84ac1c9
11 changed files with 1104 additions and 2 deletions

View File

@@ -239,7 +239,7 @@ public class PriceCalculationException extends RuntimeException {
// 在PricingExceptionHandler中统一处理
@ExceptionHandler(PriceCalculationException.class)
public ApiResponse<String> handlePriceCalculationException(PriceCalculationException e) {
return ApiResponse.error(ErrorCode.PRICE_CALCULATION_ERROR, e.getMessage());
return ApiResponse.fail(ErrorCode.PRICE_CALCULATION_ERROR, e.getMessage());
}
```

View File

@@ -0,0 +1,192 @@
package com.ycwl.basic.pricing.controller;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.pricing.dto.OnePriceConfigFilterRequest;
import com.ycwl.basic.pricing.dto.OnePriceConfigRequest;
import com.ycwl.basic.pricing.entity.PriceOnePriceConfig;
import com.ycwl.basic.pricing.service.IOnePricePurchaseService;
import com.ycwl.basic.utils.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 一口价购买管理控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/pricing/admin/one-price")
@RequiredArgsConstructor
public class OnePricePurchaseController {
private final IOnePricePurchaseService onePricePurchaseService;
/**
* 分页查询一口价配置
*/
@GetMapping("/")
public ApiResponse<PageInfo<PriceOnePriceConfig>> pageConfigs(OnePriceConfigFilterRequest request) {
log.info("分页查询一口价配置: {}", request);
PageInfo<PriceOnePriceConfig> pageInfo = onePricePurchaseService.pageConfigs(request);
log.info("查询到一口价配置数量: {}", pageInfo.getList().size());
return ApiResponse.success(pageInfo);
}
/**
* 查询所有一口价配置
*/
@GetMapping("/all")
public ApiResponse<List<PriceOnePriceConfig>> getAllConfigs() {
log.info("查询所有一口价配置");
List<PriceOnePriceConfig> configs = onePricePurchaseService.getAllConfigsForAdmin();
log.info("查询到一口价配置数量: {}", configs.size());
return ApiResponse.success(configs);
}
/**
* 根据ID查询一口价配置
*/
@GetMapping("/{id}")
public ApiResponse<PriceOnePriceConfig> getConfigById(@PathVariable Long id) {
log.info("根据ID查询一口价配置: id={}", id);
PriceOnePriceConfig config = onePricePurchaseService.getConfigById(id);
if (config == null) {
return ApiResponse.fail("配置不存在");
}
return ApiResponse.success(config);
}
/**
* 创建一口价配置
*/
@PostMapping("/")
public ApiResponse<Long> createConfig(@RequestBody OnePriceConfigRequest request) {
log.info("创建一口价配置: {}", request);
// 转换为实体对象
PriceOnePriceConfig config = new PriceOnePriceConfig();
config.setConfigName(request.getConfigName());
config.setScenicId(request.getScenicId());
config.setOnePrice(request.getOnePrice());
config.setOriginalPrice(request.getOriginalPrice());
config.setDescription(request.getDescription());
config.setIsActive(request.getIsActive());
config.setStartTime(request.getStartTime());
config.setEndTime(request.getEndTime());
config.setCanUseCoupon(request.getCanUseCoupon());
config.setCanUseVoucher(request.getCanUseVoucher());
Long configId = onePricePurchaseService.createConfig(config);
if (configId != null) {
log.info("一口价配置创建成功: configId={}", configId);
return ApiResponse.success(configId);
} else {
return ApiResponse.fail("创建一口价配置失败");
}
}
/**
* 更新一口价配置
*/
@PutMapping("/{id}")
public ApiResponse<Boolean> updateConfig(@PathVariable Long id, @RequestBody OnePriceConfigRequest request) {
log.info("更新一口价配置: id={}, request={}", id, request);
// 转换为实体对象
PriceOnePriceConfig config = new PriceOnePriceConfig();
config.setId(id);
config.setConfigName(request.getConfigName());
config.setScenicId(request.getScenicId());
config.setOnePrice(request.getOnePrice());
config.setOriginalPrice(request.getOriginalPrice());
config.setDescription(request.getDescription());
config.setIsActive(request.getIsActive());
config.setStartTime(request.getStartTime());
config.setEndTime(request.getEndTime());
config.setCanUseCoupon(request.getCanUseCoupon());
config.setCanUseVoucher(request.getCanUseVoucher());
boolean success = onePricePurchaseService.updateConfig(config);
if (success) {
log.info("一口价配置更新成功: id={}", id);
return ApiResponse.success(true);
} else {
return ApiResponse.fail("更新一口价配置失败");
}
}
/**
* 删除一口价配置
*/
@DeleteMapping("/{id}")
public ApiResponse<Boolean> deleteConfig(@PathVariable Long id) {
log.info("删除一口价配置: id={}", id);
boolean success = onePricePurchaseService.deleteConfig(id);
if (success) {
log.info("一口价配置删除成功: id={}", id);
return ApiResponse.success(true);
} else {
return ApiResponse.fail("删除一口价配置失败");
}
}
/**
* 启用/禁用一口价配置
*/
@PutMapping("/{id}/status")
public ApiResponse<Boolean> updateConfigStatus(@PathVariable Long id, @RequestParam Boolean isActive) {
log.info("更新一口价配置状态: id={}, isActive={}", id, isActive);
boolean success = onePricePurchaseService.updateConfigStatus(id, isActive);
if (success) {
log.info("一口价配置状态更新成功: id={}, isActive={}", id, isActive);
return ApiResponse.success(true);
} else {
return ApiResponse.fail("更新一口价配置状态失败");
}
}
/**
* 根据景区查询启用的一口价配置
*/
@GetMapping("/scenic/{scenicId}")
public ApiResponse<List<PriceOnePriceConfig>> getConfigsByScenic(@PathVariable Long scenicId) {
log.info("根据景区查询启用的一口价配置: scenicId={}", scenicId);
List<PriceOnePriceConfig> configs = onePricePurchaseService.getActiveConfigsByScenic(scenicId);
log.info("景区 {} 查询到一口价配置数量: {}", scenicId, configs.size());
return ApiResponse.success(configs);
}
/**
* 检查景区是否适用一口价
*/
@GetMapping("/check/{scenicId}")
public ApiResponse<Boolean> checkOnePriceApplicable(@PathVariable Long scenicId) {
log.info("检查景区是否适用一口价: scenicId={}", scenicId);
boolean applicable = onePricePurchaseService.isOnePriceApplicable(scenicId);
log.info("景区 {} 一口价适用性: {}", scenicId, applicable);
return ApiResponse.success(applicable);
}
}

View File

@@ -87,4 +87,16 @@ public class DiscountDetail {
return detail;
}
/**
* 创建一口价购买折扣明细
*/
public static DiscountDetail createOnePriceDiscount(BigDecimal discountAmount) {
DiscountDetail detail = new DiscountDetail();
detail.setDiscountType("ONE_PRICE_PURCHASE");
detail.setDiscountName("一口价购买优惠");
detail.setDiscountAmount(discountAmount);
detail.setDescription("一口价购买,价格更优惠");
detail.setSortOrder(4); // 一口价排在打包购买之前
return detail;
}
}

View File

@@ -0,0 +1,37 @@
package com.ycwl.basic.pricing.dto;
import lombok.Data;
/**
* 一口价配置筛选请求DTO
*/
@Data
public class OnePriceConfigFilterRequest {
/**
* 页码
*/
private Integer pageNum = 1;
/**
* 页大小
*/
private Integer pageSize = 10;
/**
* 景区ID(筛选条件)
*/
private Long scenicId;
/**
* 配置名称(模糊查询)
*/
private String configName;
// 移除faceId字段,一口价是景区级别的统一价格
/**
* 启用状态
*/
private Boolean isActive;
}

View File

@@ -0,0 +1,65 @@
package com.ycwl.basic.pricing.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 一口价配置请求DTO
*/
@Data
public class OnePriceConfigRequest {
/**
* 配置名称
*/
private String configName;
/**
* 景区ID
*/
private Long scenicId;
// 移除faceId字段,一口价是景区级别的统一价格
/**
* 一口价格
*/
private BigDecimal onePrice;
/**
* 原价(用于计算和展示优惠金额)
*/
private BigDecimal originalPrice;
/**
* 描述
*/
private String description;
/**
* 是否启用
*/
private Boolean isActive;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
/**
* 是否可与优惠券叠加使用
*/
private Boolean canUseCoupon;
/**
* 是否可与券码叠加使用
*/
private Boolean canUseVoucher;
}

View File

@@ -0,0 +1,59 @@
package com.ycwl.basic.pricing.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
* 一口价信息DTO(用于优惠检测)
*/
@Data
public class OnePriceInfo {
/**
* 配置ID
*/
private Long configId;
/**
* 配置名称
*/
private String configName;
/**
* 景区ID
*/
private Long scenicId;
// 移除faceId字段,一口价是景区级别的统一价格
/**
* 一口价格
*/
private BigDecimal onePrice;
/**
* 原价(用于计算和展示优惠金额)
*/
private BigDecimal originalPrice;
/**
* 实际优惠金额(当前总价 - 一口价)
*/
private BigDecimal actualDiscountAmount;
/**
* 是否可与优惠券叠加使用
*/
private Boolean canUseCoupon;
/**
* 是否可与券码叠加使用
*/
private Boolean canUseVoucher;
/**
* 描述
*/
private String description;
}

View File

@@ -0,0 +1,122 @@
package com.ycwl.basic.pricing.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 一口价购买配置实体
* 一口价:用户(faceId)在特定景区内所有商品的一次性购买优惠价格
*/
@Data
@TableName("price_one_price_config")
public class PriceOnePriceConfig {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 配置名称
*/
private String configName;
/**
* 景区ID
*/
private Long scenicId;
// 移除faceId字段,一口价是景区级别的统一价格
/**
* 一口价格(该景区内所有商品的统一价格)
*/
private BigDecimal onePrice;
/**
* 原价(用于计算和展示优惠金额)
*/
private BigDecimal originalPrice;
/**
* 描述
*/
private String description;
/**
* 是否启用
*/
private Boolean isActive;
/**
* 开始时间(可选,用于限时一口价)
*/
private LocalDateTime startTime;
/**
* 结束时间(可选,用于限时一口价)
*/
private LocalDateTime endTime;
/**
* 是否可与优惠券叠加使用
*/
private Boolean canUseCoupon;
/**
* 是否可与券码叠加使用
*/
private Boolean canUseVoucher;
@TableField("create_time")
private LocalDateTime createTime;
@TableField("update_time")
private LocalDateTime updateTime;
private Long createBy;
private Long updateBy;
private Integer deleted;
@TableField("deleted_at")
private LocalDateTime deletedAt;
/**
* 检查是否在有效时间范围内
*/
public boolean isTimeValid() {
LocalDateTime now = LocalDateTime.now();
// 如果没有设置时间范围,则始终有效
if (startTime == null && endTime == null) {
return true;
}
// 检查开始时间
if (startTime != null && now.isBefore(startTime)) {
return false;
}
// 检查结束时间
if (endTime != null && now.isAfter(endTime)) {
return false;
}
return true;
}
// 移除isUserMatch方法,一口价适用于所有用户
/**
* 检查景区是否匹配
*/
public boolean isScenicMatch(Long userScenicId) {
return scenicId != null && scenicId.equals(userScenicId);
}
}

View File

@@ -0,0 +1,93 @@
package com.ycwl.basic.pricing.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycwl.basic.pricing.entity.PriceOnePriceConfig;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* 一口价配置Mapper
*/
@Mapper
public interface PriceOnePriceConfigMapper extends BaseMapper<PriceOnePriceConfig> {
/**
* 查询启用的一口价配置
*/
@Select("SELECT * FROM price_one_price_config WHERE is_active = 1 AND deleted = 0 " +
"ORDER BY scenic_id")
List<PriceOnePriceConfig> selectActiveConfigs();
/**
* 根据景区ID查询启用的配置
*/
@Select("SELECT * FROM price_one_price_config " +
"WHERE is_active = 1 AND deleted = 0 " +
"AND scenic_id = #{scenicId} " +
"LIMIT 1")
PriceOnePriceConfig selectConfigByScenic(@Param("scenicId") Long scenicId);
/**
* 查询特定景区的所有配置
*/
@Select("SELECT * FROM price_one_price_config " +
"WHERE is_active = 1 AND deleted = 0 " +
"AND scenic_id = #{scenicId}")
List<PriceOnePriceConfig> selectConfigsByScenic(@Param("scenicId") Long scenicId);
/**
* 根据ID查询启用的配置
*/
@Select("SELECT * FROM price_one_price_config " +
"WHERE id = #{id} AND is_active = 1 AND deleted = 0")
PriceOnePriceConfig selectActiveConfigById(Long id);
/**
* 查询所有配置(包含禁用的)- 管理端使用
*/
@Select("SELECT * FROM price_one_price_config WHERE deleted = 0 " +
"ORDER BY is_active DESC, create_time DESC")
List<PriceOnePriceConfig> selectAllConfigsForAdmin();
/**
* 插入配置
*/
@Insert("INSERT INTO price_one_price_config " +
"(config_name, scenic_id, one_price, original_price, description, is_active, " +
"start_time, end_time, can_use_coupon, can_use_voucher, " +
"create_time, update_time, create_by, deleted) " +
"VALUES " +
"(#{configName}, #{scenicId}, #{onePrice}, #{originalPrice}, #{description}, #{isActive}, " +
"#{startTime}, #{endTime}, #{canUseCoupon}, #{canUseVoucher}, " +
"NOW(), NOW(), #{createBy}, 0)")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertConfig(PriceOnePriceConfig config);
/**
* 更新配置
*/
@Update("UPDATE price_one_price_config SET " +
"config_name = #{configName}, scenic_id = #{scenicId}, " +
"one_price = #{onePrice}, original_price = #{originalPrice}, " +
"description = #{description}, is_active = #{isActive}, " +
"start_time = #{startTime}, end_time = #{endTime}, " +
"can_use_coupon = #{canUseCoupon}, can_use_voucher = #{canUseVoucher}, " +
"update_time = NOW(), update_by = #{updateBy} " +
"WHERE id = #{id} AND deleted = 0")
int updateConfig(PriceOnePriceConfig config);
/**
* 更新配置状态
*/
@Update("UPDATE price_one_price_config SET is_active = #{isActive}, update_time = NOW() " +
"WHERE id = #{id} AND deleted = 0")
int updateConfigStatus(@Param("id") Long id, @Param("isActive") Boolean isActive);
/**
* 逻辑删除配置
*/
@Update("UPDATE price_one_price_config SET deleted = 1, deleted_at = NOW() " +
"WHERE id = #{id}")
int deleteConfig(Long id);
}

View File

@@ -0,0 +1,106 @@
package com.ycwl.basic.pricing.service;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.pricing.dto.OnePriceConfigFilterRequest;
import com.ycwl.basic.pricing.dto.OnePriceInfo;
import com.ycwl.basic.pricing.entity.PriceOnePriceConfig;
import java.math.BigDecimal;
import java.util.List;
/**
* 一口价购买服务接口
* 一口价:特定景区内所有商品的统一优惠价格
*/
public interface IOnePricePurchaseService {
/**
* 检查指定景区是否适用一口价
*
* @param scenicId 景区ID
* @return 是否适用
*/
boolean isOnePriceApplicable(Long scenicId);
/**
* 获取指定景区的一口价信息
*
* @param scenicId 景区ID
* @param currentAmount 当前总金额(用于计算优惠)
* @return 一口价信息,如果不适用则返回null
*/
OnePriceInfo getOnePriceInfo(Long scenicId, BigDecimal currentAmount);
/**
* 获取所有启用的一口价配置
*
* @return 一口价配置列表
*/
List<PriceOnePriceConfig> getActiveConfigs();
/**
* 根据景区查询启用的一口价配置
*
* @param scenicId 景区ID
* @return 一口价配置列表
*/
List<PriceOnePriceConfig> getActiveConfigsByScenic(Long scenicId);
// ==================== 管理端接口 ====================
/**
* 创建一口价配置
*
* @param config 配置信息
* @return 配置ID
*/
Long createConfig(PriceOnePriceConfig config);
/**
* 更新一口价配置
*
* @param config 配置信息
* @return 是否更新成功
*/
boolean updateConfig(PriceOnePriceConfig config);
/**
* 删除一口价配置
*
* @param id 配置ID
* @return 是否删除成功
*/
boolean deleteConfig(Long id);
/**
* 启用/禁用一口价配置
*
* @param id 配置ID
* @param isActive 是否启用
* @return 是否操作成功
*/
boolean updateConfigStatus(Long id, Boolean isActive);
/**
* 根据ID查询配置
*
* @param id 配置ID
* @return 配置信息
*/
PriceOnePriceConfig getConfigById(Long id);
/**
* 分页查询一口价配置
*
* @param request 筛选请求参数
* @return 分页结果
*/
PageInfo<PriceOnePriceConfig> pageConfigs(OnePriceConfigFilterRequest request);
/**
* 查询所有配置(包含禁用的)- 管理端使用
*
* @return 配置列表
*/
List<PriceOnePriceConfig> getAllConfigsForAdmin();
}

View File

@@ -0,0 +1,197 @@
package com.ycwl.basic.pricing.service.impl;
import com.ycwl.basic.pricing.dto.DiscountDetectionContext;
import com.ycwl.basic.pricing.dto.DiscountInfo;
import com.ycwl.basic.pricing.dto.DiscountResult;
import com.ycwl.basic.pricing.dto.OnePriceInfo;
import com.ycwl.basic.pricing.service.IDiscountProvider;
import com.ycwl.basic.pricing.service.IOnePricePurchaseService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* 一口价购买优惠提供者
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class OnePricePurchaseDiscountProvider implements IDiscountProvider {
private final IOnePricePurchaseService onePricePurchaseService;
@Override
public String getProviderType() {
return "ONE_PRICE_PURCHASE";
}
@Override
public int getPriority() {
return 60; // 中等优先级,在券码和优惠券之间
}
@Override
public List<DiscountInfo> detectAvailableDiscounts(DiscountDetectionContext context) {
List<DiscountInfo> discounts = new ArrayList<>();
try {
if (context.getScenicId() == null || context.getCurrentAmount() == null) {
log.debug("一口价检测失败: 景区ID或当前金额为空");
return discounts;
}
// 检查是否适用一口价
if (!onePricePurchaseService.isOnePriceApplicable(context.getScenicId())) {
log.debug("景区 {} 不适用一口价", context.getScenicId());
return discounts;
}
// 获取一口价信息
OnePriceInfo onePriceInfo = onePricePurchaseService.getOnePriceInfo(
context.getScenicId(), context.getCurrentAmount());
if (onePriceInfo == null) {
log.debug("景区 {} 未找到一口价配置", context.getScenicId());
return discounts;
}
// 只有当一口价小于当前金额时才算优惠
if (onePriceInfo.getOnePrice().compareTo(context.getCurrentAmount()) >= 0) {
log.debug("景区 {} 一口价 {} 不小于当前金额 {}",
context.getScenicId(), onePriceInfo.getOnePrice(), context.getCurrentAmount());
return discounts;
}
// 创建优惠信息
DiscountInfo discountInfo = new DiscountInfo();
discountInfo.setProviderType(getProviderType());
discountInfo.setDiscountName("一口价购买优惠");
discountInfo.setDiscountAmount(onePriceInfo.getActualDiscountAmount());
discountInfo.setDiscountDescription("景区一口价购买,价格更优惠");
discountInfo.setOnePriceInfo(onePriceInfo);
discounts.add(discountInfo);
log.info("检测到一口价优惠: 景区={}, 一口价={}, 优惠金额={}",
context.getScenicId(), onePriceInfo.getOnePrice(),
onePriceInfo.getActualDiscountAmount());
} catch (Exception e) {
log.error("一口价优惠检测失败", e);
}
return discounts;
}
@Override
public DiscountResult applyDiscount(DiscountInfo discountInfo, DiscountDetectionContext context) {
DiscountResult result = new DiscountResult();
result.setDiscountInfo(discountInfo);
result.setSuccess(false);
try {
if (!getProviderType().equals(discountInfo.getProviderType())) {
result.setFailureReason("优惠类型不匹配");
return result;
}
OnePriceInfo onePriceInfo = discountInfo.getOnePriceInfo();
if (onePriceInfo == null) {
result.setFailureReason("一口价信息为空");
return result;
}
// 检查优惠的叠加限制
boolean canUseWithOtherDiscounts = checkDiscountCombinationRules(onePriceInfo, context);
if (!canUseWithOtherDiscounts) {
result.setFailureReason("一口价不可与其他优惠叠加使用");
return result;
}
// 应用一口价优惠
BigDecimal discountAmount = onePriceInfo.getActualDiscountAmount();
BigDecimal finalAmount = context.getCurrentAmount().subtract(discountAmount);
// 如果使用一口价,则最终金额就是一口价金额
finalAmount = onePriceInfo.getOnePrice();
discountAmount = context.getCurrentAmount().subtract(finalAmount);
result.setSuccess(true);
result.setActualDiscountAmount(discountAmount);
result.setFinalAmount(finalAmount);
result.setFailureReason("一口价购买优惠已应用");
log.info("一口价优惠应用成功: 优惠金额={}, 最终金额={}", discountAmount, finalAmount);
} catch (Exception e) {
log.error("一口价优惠应用失败", e);
result.setFailureReason("一口价优惠应用失败: " + e.getMessage());
}
return result;
}
@Override
public boolean canApply(DiscountInfo discountInfo, DiscountDetectionContext context) {
try {
if (!getProviderType().equals(discountInfo.getProviderType())) {
return false;
}
OnePriceInfo onePriceInfo = discountInfo.getOnePriceInfo();
if (onePriceInfo == null) {
return false;
}
// 检查一口价是否仍然有效(时间范围等)
return onePricePurchaseService.isOnePriceApplicable(context.getScenicId());
} catch (Exception e) {
log.error("检查一口价可用性失败", e);
return false;
}
}
@Override
public BigDecimal getMaxPossibleDiscount(DiscountInfo discountInfo, DiscountDetectionContext context) {
try {
OnePriceInfo onePriceInfo = discountInfo.getOnePriceInfo();
if (onePriceInfo != null && onePriceInfo.getActualDiscountAmount() != null) {
return onePriceInfo.getActualDiscountAmount();
}
} catch (Exception e) {
log.error("获取一口价最大优惠金额失败", e);
}
return BigDecimal.ZERO;
}
/**
* 检查优惠叠加规则
*/
private boolean checkDiscountCombinationRules(OnePriceInfo onePriceInfo, DiscountDetectionContext context) {
// 根据配置检查是否可以与优惠券、券码叠加使用
// 这里可以根据业务需求实现具体的叠加规则
// 如果配置了不可与优惠券叠加,且请求中要使用优惠券,则返回false
if (Boolean.FALSE.equals(onePriceInfo.getCanUseCoupon()) &&
Boolean.TRUE.equals(context.getAutoUseCoupon())) {
log.debug("一口价配置不允许与优惠券叠加使用");
return false;
}
// 如果配置了不可与券码叠加,且请求中要使用券码,则返回false
if (Boolean.FALSE.equals(onePriceInfo.getCanUseVoucher()) &&
(Boolean.TRUE.equals(context.getAutoUseVoucher()) ||
context.getVoucherCode() != null)) {
log.debug("一口价配置不允许与券码叠加使用");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,219 @@
package com.ycwl.basic.pricing.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.pricing.dto.OnePriceConfigFilterRequest;
import com.ycwl.basic.pricing.dto.OnePriceInfo;
import com.ycwl.basic.pricing.entity.PriceOnePriceConfig;
import com.ycwl.basic.pricing.mapper.PriceOnePriceConfigMapper;
import com.ycwl.basic.pricing.service.IOnePricePurchaseService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.util.List;
/**
* 一口价购买服务实现
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OnePricePurchaseServiceImpl implements IOnePricePurchaseService {
private final PriceOnePriceConfigMapper onePriceConfigMapper;
@Override
public boolean isOnePriceApplicable(Long scenicId) {
PriceOnePriceConfig config = findConfigByScenic(scenicId);
return config != null;
}
@Override
public OnePriceInfo getOnePriceInfo(Long scenicId, BigDecimal currentAmount) {
PriceOnePriceConfig config = findConfigByScenic(scenicId);
if (config == null) {
return null;
}
OnePriceInfo info = new OnePriceInfo();
info.setConfigId(config.getId());
info.setConfigName(config.getConfigName());
info.setScenicId(scenicId);
info.setOnePrice(config.getOnePrice());
info.setOriginalPrice(config.getOriginalPrice());
info.setDescription(config.getDescription());
info.setCanUseCoupon(config.getCanUseCoupon());
info.setCanUseVoucher(config.getCanUseVoucher());
// 计算实际优惠金额
if (currentAmount != null && config.getOnePrice().compareTo(currentAmount) < 0) {
BigDecimal discountAmount = currentAmount.subtract(config.getOnePrice());
info.setActualDiscountAmount(discountAmount);
} else {
info.setActualDiscountAmount(BigDecimal.ZERO);
}
return info;
}
@Override
public List<PriceOnePriceConfig> getActiveConfigs() {
return onePriceConfigMapper.selectActiveConfigs();
}
@Override
public List<PriceOnePriceConfig> getActiveConfigsByScenic(Long scenicId) {
return onePriceConfigMapper.selectConfigsByScenic(scenicId);
}
// ==================== 管理端接口实现 ====================
@Override
public Long createConfig(PriceOnePriceConfig config) {
try {
// 设置默认值
if (config.getIsActive() == null) {
config.setIsActive(true);
}
if (config.getCanUseCoupon() == null) {
config.setCanUseCoupon(true);
}
if (config.getCanUseVoucher() == null) {
config.setCanUseVoucher(true);
}
if (config.getDeleted() == null) {
config.setDeleted(0);
}
int result = onePriceConfigMapper.insertConfig(config);
if (result > 0) {
log.info("创建一口价配置成功: id={}, name={}", config.getId(), config.getConfigName());
return config.getId();
}
return null;
} catch (Exception e) {
log.error("创建一口价配置失败", e);
throw new RuntimeException("创建一口价配置失败: " + e.getMessage());
}
}
@Override
public boolean updateConfig(PriceOnePriceConfig config) {
try {
int result = onePriceConfigMapper.updateConfig(config);
if (result > 0) {
log.info("更新一口价配置成功: id={}", config.getId());
return true;
}
return false;
} catch (Exception e) {
log.error("更新一口价配置失败: id={}", config.getId(), e);
throw new RuntimeException("更新一口价配置失败: " + e.getMessage());
}
}
@Override
public boolean deleteConfig(Long id) {
try {
int result = onePriceConfigMapper.deleteConfig(id);
if (result > 0) {
log.info("删除一口价配置成功: id={}", id);
return true;
}
return false;
} catch (Exception e) {
log.error("删除一口价配置失败: id={}", id, e);
throw new RuntimeException("删除一口价配置失败: " + e.getMessage());
}
}
@Override
public boolean updateConfigStatus(Long id, Boolean isActive) {
try {
int result = onePriceConfigMapper.updateConfigStatus(id, isActive);
if (result > 0) {
log.info("更新一口价配置状态成功: id={}, isActive={}", id, isActive);
return true;
}
return false;
} catch (Exception e) {
log.error("更新一口价配置状态失败: id={}", id, e);
throw new RuntimeException("更新一口价配置状态失败: " + e.getMessage());
}
}
@Override
public PriceOnePriceConfig getConfigById(Long id) {
return onePriceConfigMapper.selectById(id);
}
@Override
public PageInfo<PriceOnePriceConfig> pageConfigs(OnePriceConfigFilterRequest request) {
// 开启分页
PageHelper.startPage(request.getPageNum(), request.getPageSize());
// 构建查询条件
QueryWrapper<PriceOnePriceConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("deleted", 0); // 未删除的记录
// 添加筛选条件
if (request.getScenicId() != null) {
queryWrapper.eq("scenic_id", request.getScenicId());
}
if (StringUtils.hasText(request.getConfigName())) {
queryWrapper.like("config_name", request.getConfigName()); // 模糊查询
}
if (request.getIsActive() != null) {
queryWrapper.eq("is_active", request.getIsActive());
}
// 按创建时间降序排列
queryWrapper.orderByDesc("create_time");
// 执行查询
List<PriceOnePriceConfig> list = onePriceConfigMapper.selectList(queryWrapper);
// 返回分页结果
return new PageInfo<>(list);
}
@Override
public List<PriceOnePriceConfig> getAllConfigsForAdmin() {
return onePriceConfigMapper.selectAllConfigsForAdmin();
}
// ==================== 私有方法 ====================
/**
* 根据景区查找配置
*/
private PriceOnePriceConfig findConfigByScenic(Long scenicId) {
if (scenicId == null) {
return null;
}
// 查询匹配的配置
PriceOnePriceConfig config = onePriceConfigMapper.selectConfigByScenic(scenicId);
if (config == null) {
return null;
}
// 检查时间有效性
if (!config.isTimeValid()) {
return null;
}
// 检查景区匹配性
if (!config.isScenicMatch(scenicId)) {
return null;
}
return config;
}
}