getCombinationActivityPage(PageParam pageParam) {
return combinationActivityMapper.selectPage(pageParam, CommonStatusEnum.ENABLE.getStatus());
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
index 27c392ba4..c24cf3ac9 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
@@ -4,8 +4,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
-import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
-import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
@@ -19,26 +17,6 @@ import java.util.*;
*/
public interface CouponService {
- /**
- * 校验优惠劵,包括状态、有限期
- *
- * 1. 如果校验通过,则返回优惠劵信息
- * 2. 如果校验不通过,则直接抛出业务异常
- *
- * @param id 优惠劵编号
- * @param userId 用户编号
- * @return 优惠劵信息
- */
- CouponDO validCoupon(Long id, Long userId);
-
- /**
- * 校验优惠劵,包括状态、有限期
- *
- * @param coupon 优惠劵
- * @see #validCoupon(Long, Long) 逻辑相同,只是入参不同
- */
- void validCoupon(CouponDO coupon);
-
/**
* 使用优惠劵
*
@@ -172,15 +150,6 @@ public interface CouponService {
return MapUtil.getInt(map, templateId, 0);
}
- /**
- * 获取用户匹配的优惠券列表
- *
- * @param userId 用户编号
- * @param matchReqVO 匹配参数
- * @return 优惠券列表
- */
- List getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
-
/**
* 获取用户是否可以领取优惠券
*
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
index c01d28849..cff17f9da 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
@@ -12,13 +12,10 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
-import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
-import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
-import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
@@ -58,18 +55,9 @@ public class CouponServiceImpl implements CouponService {
private MemberUserApi memberUserApi;
@Override
- public CouponDO validCoupon(Long id, Long userId) {
- CouponDO coupon = couponMapper.selectByIdAndUserId(id, userId);
- if (coupon == null) {
- throw exception(COUPON_NOT_EXISTS);
- }
- validCoupon(coupon);
- return coupon;
- }
-
- @Override
- public void validCoupon(CouponDO coupon) {
+ public void useCoupon(Long id, Long userId, Long orderId) {
// 校验状态
+ CouponDO coupon = couponMapper.selectByIdAndUserId(id, userId);
if (ObjectUtil.notEqual(coupon.getStatus(), CouponStatusEnum.UNUSED.getStatus())) {
throw exception(COUPON_STATUS_NOT_UNUSED);
}
@@ -77,12 +65,6 @@ public class CouponServiceImpl implements CouponService {
if (!LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime())) {
throw exception(COUPON_VALID_TIME_NOT_NOW);
}
- }
-
- @Override
- public void useCoupon(Long id, Long userId, Long orderId) {
- // 校验优惠劵
- validCoupon(id, userId);
// 更新状态
int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),
@@ -286,10 +268,8 @@ public class CouponServiceImpl implements CouponService {
if (couponTemplate == null) {
throw exception(COUPON_TEMPLATE_NOT_EXISTS);
}
- // 校验剩余数量(仅在 CouponTakeTypeEnum.USER 用户领取时)
- if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeCount())
- && couponTemplate.getTotalCount() != null
- && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
+ // 校验剩余数量
+ if (couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
}
// 校验"固定日期"的有效期类型是否过期
@@ -299,7 +279,7 @@ public class CouponServiceImpl implements CouponService {
}
}
// 校验领取方式
- if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) {
+ if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getValue())) {
throw exception(COUPON_TEMPLATE_CANNOT_TAKE);
}
}
@@ -311,7 +291,7 @@ public class CouponServiceImpl implements CouponService {
* @param couponTemplate 优惠劵模版
*/
private void removeTakeLimitUser(Set userIds, CouponTemplateDO couponTemplate) {
- if (couponTemplate.getTakeLimitCount() == null || couponTemplate.getTakeLimitCount() <= 0) {
+ if (couponTemplate.getTakeLimitCount() <= 0) {
return;
}
// 查询已领过券的用户
@@ -358,48 +338,6 @@ public class CouponServiceImpl implements CouponService {
return couponMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);
}
- @Override
- public List getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
- List couponMatchist = new ArrayList<>();
- List list = couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId,
- CouponStatusEnum.UNUSED.getStatus());
- for (CouponDO couponDO : list) {
- AppCouponMatchRespVO appCouponMatchRespVO = CouponConvert.INSTANCE.convert2(couponDO);
- Integer productScope = appCouponMatchRespVO.getProductScope();
- List productScopeValues = appCouponMatchRespVO.getProductScopeValues();
- Integer usePrice = appCouponMatchRespVO.getUsePrice();
- if(matchReqVO.getPrice() < usePrice){
- // 价格小于等于,满足价格使用条件
- appCouponMatchRespVO.setMatch(false);
- appCouponMatchRespVO.setDescription("未达到使用门槛");
- }else if(!LocalDateTimeUtils.isBetween(appCouponMatchRespVO.getValidStartTime(), appCouponMatchRespVO.getValidEndTime())) {
- //判断时间
- appCouponMatchRespVO.setMatch(false);
- appCouponMatchRespVO.setDescription("使用时间未到");
- }else if (PromotionProductScopeEnum.ALL.getScope().equals(productScope)){
- appCouponMatchRespVO.setMatch(true);
- }else if (PromotionProductScopeEnum.SPU.getScope().equals(productScope)){
- boolean spu = new HashSet<>(productScopeValues).containsAll(matchReqVO.getSpuIds());
- if(spu){
- appCouponMatchRespVO.setMatch(true);
- }else {
- appCouponMatchRespVO.setMatch(false);
- appCouponMatchRespVO.setDescription("与商品不匹配");
- }
- }else if (PromotionProductScopeEnum.CATEGORY.getScope().equals(productScope)){
- boolean category = new HashSet<>(productScopeValues).containsAll(matchReqVO.getCategoryIds());
- if(category){
- appCouponMatchRespVO.setMatch(true);
- }else {
- appCouponMatchRespVO.setMatch(false);
- appCouponMatchRespVO.setDescription("与商品类型不匹配");
- }
- }
- couponMatchist.add(appCouponMatchRespVO);
- }
- return couponMatchist;
- }
-
@Override
public Map getUserCanCanTakeMap(Long userId, List templates) {
// 1. 未登录时,都显示可以领取
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
index 3112a3b80..b01aa6b77 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
@@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.Se
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
-
import jakarta.validation.Valid;
+
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
@@ -98,7 +98,7 @@ public interface SeckillActivityService {
* @param activityIds 活动编号
* @return 活动商品列表
*/
- List getSeckillProductListByActivityId(Collection activityIds);
+ List getSeckillProductListByActivityIds(Collection activityIds);
/**
* 通过活动时段编号获取指定 status 的秒杀活动
@@ -139,4 +139,12 @@ public interface SeckillActivityService {
*/
List getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(Collection spuIds, Integer status, LocalDateTime dateTime);
+ /**
+ * 获得拼团活动列表
+ *
+ * @param ids 拼团活动编号数组
+ * @return 拼团活动的列表
+ */
+ List getSeckillActivityListByIds(Collection ids);
+
}
diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
index 5613cae8e..2ab726ec4 100644
--- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
+++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -51,6 +51,7 @@ public interface ErrorCodeConstants {
ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, "退款失败,售后单状态不是【待退款】");
ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY =
new ErrorCode(1_011_000_111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
+ ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_COMBINATION_IN_PROGRESS = new ErrorCode(1_011_000_112, "订单拼团中,无法申请售后");
// ========== Cart 模块 1-011-002-000 ==========
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在");
@@ -61,7 +62,7 @@ public interface ErrorCodeConstants {
ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_004, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵");
ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_005, "参与秒杀的商品,超过了秒杀总限购数量");
ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL = new ErrorCode(1_011_003_006, "计算快递运费异常,配送方式不匹配");
- ErrorCode PRICE_CALCULATE_COUPON_PRICE_TOO_MUCH = new ErrorCode(1_011_003_007, "该优惠劵无法使用,原因:优惠金额超过订单金额");
+ ErrorCode PRICE_CALCULATE_COUPON_CAN_NOT_USE = new ErrorCode(1_011_003_007, "该优惠劵无法使用,原因:{}」");
// ========== 物流 Express 模块 1-011-004-000 ==========
ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公司不存在");
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java
index feb6eae89..83d473825 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/withdraw/AppBrokerageWithdrawCreateReqVO.java
@@ -44,7 +44,7 @@ public class AppBrokerageWithdrawCreateReqVO {
private String name;
@Schema(description = "提现银行", example = "1")
@NotNull(message = "提现银行不能为空", groups = {Bank.class})
- private Integer bankName;
+ private String bankName;
@Schema(description = "开户地址", example = "海淀支行")
private String bankAddress;
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java
index 576703794..19b659982 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java
@@ -3,11 +3,11 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo;
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
+import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 App - 交易订单结算信息 Response VO")
@@ -20,6 +20,9 @@ public class AppTradeOrderSettlementRespVO {
@Schema(description = "购物项数组", requiredMode = Schema.RequiredMode.REQUIRED)
private List- items;
+ @Schema(description = "优惠劵数组", requiredMode = Schema.RequiredMode.REQUIRED)
+ private List coupons; // 可用 + 不可用
+
@Schema(description = "费用", requiredMode = Schema.RequiredMode.REQUIRED)
private Price price;
@@ -117,7 +120,6 @@ public class AppTradeOrderSettlementRespVO {
private String mobile;
@Schema(description = "地区编号", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotNull(message = "地区编号不能为空")
private Long areaId;
@Schema(description = "地区名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海上海市普陀区")
private String areaName;
@@ -130,4 +132,43 @@ public class AppTradeOrderSettlementRespVO {
}
+ @Schema(description = "优惠劵信息")
+ @Data
+ public static class Coupon {
+
+ @Schema(description = "优惠劵编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long id;
+
+ @Schema(description = "优惠劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节送送送")
+ private String name;
+
+ @Schema(description = "是否设置满多少金额可用", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 单位:分;0 - 不限制
+ private Integer usePrice;
+
+ @Schema(description = "固定日期 - 生效开始时间")
+ private LocalDateTime validStartTime;
+
+ @Schema(description = "固定日期 - 生效结束时间")
+ private LocalDateTime validEndTime;
+
+ @Schema(description = "优惠类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer discountType;
+
+ @Schema(description = "折扣百分比", example = "80") // 例如说,80% 为 80
+ private Integer discountPercent;
+
+ @Schema(description = "优惠金额", example = "10")
+ private Integer discountPrice;
+
+ @Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
+ private Integer discountLimitPrice;
+
+ @Schema(description = "是否可用", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ private Boolean match;
+
+ @Schema(description = "不可用原因", example = "优惠劵已过期")
+ private String mismatchReason;
+
+ }
+
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
index 2a332aad2..f8e4a16f0 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
@@ -8,6 +8,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
+import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
+import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
@@ -26,6 +29,7 @@ import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleTypeEnum;
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleWayEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
+import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.framework.aftersale.core.annotations.AfterSaleLog;
import cn.iocoder.yudao.module.trade.framework.aftersale.core.utils.AfterSaleLogUtils;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
@@ -71,6 +75,8 @@ public class AfterSaleServiceImpl implements AfterSaleService {
@Resource
private PayRefundApi payRefundApi;
+ @Resource
+ private CombinationRecordApi combinationRecordApi;
@Resource
private TradeOrderProperties tradeOrderProperties;
@@ -148,6 +154,14 @@ public class AfterSaleServiceImpl implements AfterSaleService {
&& !TradeOrderStatusEnum.haveDelivered(order.getStatus())) {
throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_NO_DELIVERED);
}
+ // 如果是拼团订单,则进行中不允许售后
+ if (TradeOrderTypeEnum.isCombination(order.getType())) {
+ CombinationRecordRespDTO combinationRecord = combinationRecordApi.getCombinationRecordByOrderId(
+ order.getUserId(), order.getId());
+ if (combinationRecord != null && CombinationRecordStatusEnum.isInProgress(combinationRecord.getStatus())) {
+ throw exception(AFTER_SALE_CREATE_FAIL_ORDER_STATUS_COMBINATION_IN_PROGRESS);
+ }
+ }
return orderItem;
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
index ce0c953e1..300da3f9f 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -887,7 +887,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
.setAppKey(tradeOrderProperties.getPayAppKey()).setUserIp(getClientIP()) // 支付应用
.setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
.setMerchantRefundId(String.valueOf(order.getId()))
- .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice()));// 价格信息
+ .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice())); // 价格信息
}
@Override
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationOrderHandler.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationOrderHandler.java
index 9216258db..6fdcf24f6 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationOrderHandler.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationOrderHandler.java
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO;
+import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
+import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@@ -84,7 +86,9 @@ public class TradeCombinationOrderHandler implements TradeOrderHandler {
return;
}
// 校验订单拼团是否成功
- if (!combinationRecordApi.isCombinationRecordSuccess(order.getUserId(), order.getId())) {
+ CombinationRecordRespDTO combinationRecord = combinationRecordApi.getCombinationRecordByOrderId(order.getUserId(), order.getId());
+ Assert.notNull(combinationRecord, "订单({})对应的拼团记录不存在", order.getId());
+ if (!CombinationRecordStatusEnum.isSuccess(combinationRecord.getStatus())) {
throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);
}
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
index 2ad327208..b98ca4495 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import lombok.Data;
+import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@@ -45,9 +46,13 @@ public class TradePriceCalculateRespBO {
private List promotions;
/**
- * 优惠劵编号
+ * 使用的优惠劵编号
*/
private Long couponId;
+ /**
+ * 用户的优惠劵列表(可用 + 不可用)
+ */
+ private List coupons;
/**
* 会员剩余积分
@@ -340,4 +345,62 @@ public class TradePriceCalculateRespBO {
}
+ /**
+ * 优惠劵信息
+ */
+ @Data
+ public static class Coupon {
+
+ /**
+ * 优惠劵编号
+ */
+ private Long id;
+ /**
+ * 优惠劵名
+ */
+ private String name;
+
+ /**
+ * 是否设置满多少金额可用,单位:分
+ */
+ private Integer usePrice;
+
+ /**
+ * 生效开始时间
+ */
+ private LocalDateTime validStartTime;
+ /**
+ * 生效结束时间
+ */
+ private LocalDateTime validEndTime;
+
+ /**
+ * 优惠类型
+ */
+ private Integer discountType;
+ /**
+ * 折扣百分比
+ */
+ private Integer discountPercent;
+ /**
+ * 优惠金额,单位:分
+ */
+ private Integer discountPrice;
+ /**
+ * 折扣上限,单位:分
+ */
+ private Integer discountLimitPrice;
+
+ /**
+ * 是否匹配
+ */
+ private Boolean match;
+ /**
+ * 不匹配的原因
+ */
+ private String mismatchReason;
+
+ }
+
+
}
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java
index 1c7294be5..1292a2f85 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java
@@ -1,31 +1,31 @@
package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
-import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
+import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
+import jakarta.annotation.Resource;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
-import jakarta.annotation.Resource;
import java.util.List;
import java.util.function.Predicate;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
-import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_MIN_PRICE;
-import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_SPU;
+import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_CAN_NOT_USE;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER;
-import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_PRICE_TOO_MUCH;
/**
* 优惠劵的 {@link TradePriceCalculator} 实现类
@@ -41,34 +41,37 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
@Override
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
- // 1.1 校验优惠劵
+ // 只有【普通】订单,才允许使用优惠劵
+ if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
+ if (param.getCouponId() != null) {
+ throw exception(PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER);
+ }
+ return;
+ }
+
+ // 1.1 加载用户的优惠劵列表
+ List coupons = couponApi.getCouponListByUserId(param.getUserId(), CouponStatusEnum.UNUSED.getStatus());
+ coupons.removeIf(coupon -> LocalDateTimeUtils.beforeNow(coupon.getValidEndTime()));
+ // 1.2 计算优惠劵的使用条件
+ result.setCoupons(calculateCoupons(coupons, result));
+
+ // 2. 校验优惠劵是否可用
if (param.getCouponId() == null) {
return;
}
- CouponRespDTO coupon = couponApi.validateCoupon(new CouponValidReqDTO()
- .setId(param.getCouponId()).setUserId(param.getUserId()));
- Assert.notNull(coupon, "校验通过的优惠劵({}),不能为空", param.getCouponId());
- // 1.2 只有【普通】订单,才允许使用优惠劵
- if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
- throw exception(PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER);
+ TradePriceCalculateRespBO.Coupon couponBO = CollUtil.findOne(result.getCoupons(), item -> item.getId().equals(param.getCouponId()));
+ CouponRespDTO coupon = CollUtil.findOne(coupons, item -> item.getId().equals(param.getCouponId()));
+ if (couponBO == null || coupon == null) {
+ throw exception(PRICE_CALCULATE_COUPON_CAN_NOT_USE, "优惠劵不存在");
}
-
- // 2.1 获得匹配的商品 SKU 数组
- List orderItems = filterMatchCouponOrderItems(result, coupon);
- if (CollUtil.isEmpty(orderItems)) {
- throw exception(COUPON_NO_MATCH_SPU);
- }
- // 2.2 计算是否满足优惠劵的使用金额
- Integer totalPayPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
- if (totalPayPrice < coupon.getUsePrice()) {
- throw exception(COUPON_NO_MATCH_MIN_PRICE);
+ if (Boolean.FALSE.equals(couponBO.getMatch())) {
+ throw exception(PRICE_CALCULATE_COUPON_CAN_NOT_USE, couponBO.getMismatchReason());
}
// 3.1 计算可以优惠的金额
+ List orderItems = filterMatchCouponOrderItems(result, coupon);
+ Integer totalPayPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
Integer couponPrice = getCouponPrice(coupon, totalPayPrice);
- if (couponPrice <= totalPayPrice) {
- throw exception(PRICE_CALCULATE_COUPON_PRICE_TOO_MUCH);
- }
// 3.2 计算分摊的优惠金额
List divideCouponPrices = TradePriceCalculatorHelper.dividePrice(orderItems, couponPrice);
@@ -76,7 +79,7 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
result.setCouponId(param.getCouponId());
// 4.2 记录优惠明细
TradePriceCalculatorHelper.addPromotion(result, orderItems,
- param.getCouponId(), coupon.getName(), PromotionTypeEnum.COUPON.getType(),
+ param.getCouponId(), couponBO.getName(), PromotionTypeEnum.COUPON.getType(),
StrUtil.format("优惠劵:省 {} 元", TradePriceCalculatorHelper.formatPrice(couponPrice)),
divideCouponPrices);
// 4.3 更新 SKU 优惠金额
@@ -88,6 +91,43 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
TradePriceCalculatorHelper.recountAllPrice(result);
}
+ /**
+ * 计算用户的优惠劵列表(可用 + 不可用)
+ *
+ * @param coupons 优惠劵
+ * @param result 计算结果
+ * @return 优惠劵列表
+ */
+ private List calculateCoupons(List coupons,
+ TradePriceCalculateRespBO result) {
+ return convertList(coupons, coupon -> {
+ TradePriceCalculateRespBO.Coupon matchCoupon = BeanUtils.toBean(coupon, TradePriceCalculateRespBO.Coupon.class);
+ // 1.1 优惠劵未到使用时间
+ if (LocalDateTimeUtils.afterNow(coupon.getValidStartTime())) {
+ return matchCoupon.setMatch(false).setMismatchReason("优惠劵未到使用时间");
+ }
+ // 1.2 优惠劵没有匹配的商品
+ List orderItems = filterMatchCouponOrderItems(result, coupon);
+ if (CollUtil.isEmpty(orderItems)) {
+ return matchCoupon.setMatch(false).setMismatchReason("优惠劵没有匹配的商品");
+ }
+ // 1.3 差 %1$,.2f 元可用优惠劵
+ Integer totalPayPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
+ if (totalPayPrice < coupon.getUsePrice()) {
+ return matchCoupon.setMatch(false)
+ .setMismatchReason(String.format("差 %1$,.2f 元可用优惠劵", (coupon.getUsePrice() - totalPayPrice) / 100D));
+ }
+ // 1.4 优惠金额超过订单金额
+ Integer couponPrice = getCouponPrice(coupon, totalPayPrice);
+ if (couponPrice >= totalPayPrice) {
+ return matchCoupon.setMatch(false).setMismatchReason("优惠金额超过订单金额");
+ }
+
+ // 2. 满足条件
+ return matchCoupon.setMatch(true);
+ });
+ }
+
private Integer getCouponPrice(CouponRespDTO coupon, Integer totalPayPrice) {
if (PromotionDiscountTypeEnum.PRICE.getType().equals(coupon.getDiscountType())) { // 减价
return coupon.getDiscountPrice();
diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java
index 06655e0b2..373a4581d 100644
--- a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java
+++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java
@@ -1,12 +1,13 @@
package cn.iocoder.yudao.module.trade.service.price.calculator;
+import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
-import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
+import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
@@ -14,8 +15,10 @@ import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
+import java.time.Duration;
import java.util.ArrayList;
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -69,8 +72,10 @@ public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest {
CouponRespDTO coupon = randomPojo(CouponRespDTO.class, o -> o.setId(1024L).setName("程序员节")
.setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L))
.setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType())
- .setDiscountPercent(50).setDiscountLimitPrice(70));
- when(couponApi.validateCoupon(eq(new CouponValidReqDTO().setId(1024L).setUserId(233L)))).thenReturn(coupon);
+ .setDiscountPercent(50).setDiscountLimitPrice(70))
+ .setValidStartTime(addTime(Duration.ofDays(1))).setValidEndTime(addTime(Duration.ofDays(2)));
+ when(couponApi.getCouponListByUserId(eq(233L), eq(CouponStatusEnum.UNUSED.getStatus())))
+ .thenReturn(ListUtil.toList(coupon));
// 调用
tradeCouponPriceCalculator.calculate(param, result);
diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
index 31c1f8b55..1111daa26 100755
--- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
+++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
@@ -431,9 +431,7 @@ public class PayOrderServiceImpl implements PayOrderService {
return;
}
- // TODO 芋艿:应该 new 出来更新
- order.setPrice(payPrice);
- orderMapper.updateById(order);
+ orderMapper.updateById(new PayOrderDO().setId(order.getId()).setPrice(payPrice));
}
@Override
diff --git a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
index 36c305553..298e314d8 100644
--- a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
+++ b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
@@ -266,7 +266,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient oauth2TokenService.refreshAccessToken(refreshToken, clientId),
new ErrorCode(401, "刷新令牌已过期"));
- assertEquals(0, oauth2RefreshTokenMapper.selectCount());
+ assertEquals(0, oauth2AccessTokenMapper.selectCount());
}
@Test
diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml
index d0c429aab..3b16fa192 100644
--- a/yudao-server/pom.xml
+++ b/yudao-server/pom.xml
@@ -33,11 +33,11 @@
-
- cn.iocoder.boot
- yudao-module-member-biz
- ${revision}
-
+
+
+
+
+
@@ -52,11 +52,11 @@
-
- cn.iocoder.boot
- yudao-module-pay-biz
- ${revision}
-
+
+
+
+
+
@@ -66,26 +66,26 @@
-
- cn.iocoder.boot
- yudao-module-promotion-biz
- ${revision}
-
-
- cn.iocoder.boot
- yudao-module-product-biz
- ${revision}
-
-
- cn.iocoder.boot
- yudao-module-trade-biz
- ${revision}
-
-
- cn.iocoder.boot
- yudao-module-statistics-biz
- ${revision}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml
index efe1eb34f..baf68657e 100644
--- a/yudao-server/src/main/resources/application.yaml
+++ b/yudao-server/src/main/resources/application.yaml
@@ -154,9 +154,9 @@ spring:
embedding:
transformer:
onnx:
- model-uri: http://test.yudao.iocoder.cn/model.onnx
+ model-uri: https://raw.gitcode.com/yudaocode/yudao-demo/raw/master/yudao-static/ai/model.onnx
tokenizer:
- uri: http://test.yudao.iocoder.cn/tokenizer.json
+ uri: https://raw.gitcode.com/yudaocode/yudao-demo/raw/master/yudao-static/ai/tokenizer.json
qianfan: # 文心一言
api-key: x0cuLZ7XsaTCU08vuJWO87Lg
secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK
@@ -245,7 +245,7 @@ yudao:
codegen:
base-package: ${yudao.info.base-package}
db-schemas: ${spring.datasource.dynamic.datasource.master.name}
- front-type: 10 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类
+ front-type: 20 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类
tenant: # 多租户相关配置项
enable: true
ignore-urls: