diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java index 5bf5d6c..ef50157 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.framework.common.util.json.databind; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; @@ -7,10 +10,10 @@ import com.fasterxml.jackson.databind.JsonDeserializer; import java.io.IOException; import java.time.Instant; import java.time.LocalDateTime; -import java.time.ZoneId; +import java.time.format.DateTimeFormatter; /** - * 基于时间戳的 LocalDateTime 反序列化器 + * LocalDateTime 反序列化器,支持毫秒时间戳、常见日期时间字符串和 ISO-8601 日期。 * * @author 老五 */ @@ -20,8 +23,20 @@ public class TimestampLocalDateTimeDeserializer extends JsonDeserializer parse("\"not-a-date\"")); + + } + +} diff --git a/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java index dd433f3..6276194 100644 --- a/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java @@ -215,9 +215,32 @@ public class CouponServiceImpl implements CouponService { @Transactional(rollbackFor = Exception.class) public void takeCouponByRegister(Long userId) { List templates = couponTemplateService.getCouponTemplateListByTakeType(CouponTakeTypeEnum.REGISTER); - for (CouponTemplateDO template : templates) { - takeCoupon(template, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER); + // 1. 过滤不可领取(如已过期)的模板,避免单张异常中断整批 + List takeableTemplates = templates.stream() + .filter(this::isCouponTemplateNotExpired) + .collect(Collectors.toList()); + if (takeableTemplates.size() < templates.size()) { + log.info("[takeCouponByRegister][用户({}) 过滤 {} 张不可领取的模板]", userId, templates.size() - takeableTemplates.size()); } + // 2. 逐个发放,单张失败不影响其余 + int successCount = 0; + for (CouponTemplateDO template : takeableTemplates) { + try { + takeCoupon(template, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER); + successCount++; + } catch (Exception e) { + log.error("[takeCouponByRegister][用户({}) 模板({}) 发放失败]", userId, template.getId(), e); + } + } + // 3. 应发全失败时留一条 ERROR 标记,供监控告警 + if (CollUtil.isNotEmpty(takeableTemplates) && successCount == 0) { + log.error("[takeCouponByRegister][用户({}) 全部 {} 张新人券发放失败]", userId, takeableTemplates.size()); + } + } + + private boolean isCouponTemplateNotExpired(CouponTemplateDO template) { + return !CouponTemplateValidityTypeEnum.DATE.getType().equals(template.getValidityType()) + || !LocalDateTimeUtils.beforeNow(template.getValidEndTime()); } @Override