13 changed files with 318 additions and 28 deletions
@ -0,0 +1,28 @@ |
|||
package com.youlai.boot.common.event; |
|||
|
|||
import lombok.Getter; |
|||
import org.springframework.context.ApplicationEvent; |
|||
|
|||
/** |
|||
* 用户注册成功事件 |
|||
* 所有注册成功后需要执行的附加逻辑(送积分、送优惠券、发通知等)都监听这个事件即可 |
|||
*/ |
|||
@Getter |
|||
public class UserRegisterEvent extends ApplicationEvent { |
|||
|
|||
/** |
|||
* 新注册用户的ID |
|||
*/ |
|||
private final Long userId; |
|||
|
|||
/** |
|||
* 注册来源:MINI_PROGRAM小程序 / H5 / ADMIN后台等 |
|||
*/ |
|||
private final String registerSource; |
|||
|
|||
public UserRegisterEvent(Long userId, String registerSource) { |
|||
super(userId); |
|||
this.userId = userId; |
|||
this.registerSource = registerSource; |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
package com.youlai.boot.mini.listener; |
|||
|
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|||
import com.youlai.boot.common.event.UserRegisterEvent; |
|||
import com.youlai.boot.mini.model.entity.MiniPointRecord; |
|||
import com.youlai.boot.mini.model.entity.MiniPointRule; |
|||
import com.youlai.boot.mini.model.form.AdjustUserPointForm; |
|||
import com.youlai.boot.mini.service.MiniPointRecordService; |
|||
import com.youlai.boot.mini.service.MiniPointRuleService; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.context.event.EventListener; |
|||
import org.springframework.scheduling.annotation.Async; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
|
|||
/** |
|||
* 用户注册成功监听器:赠送新用户注册积分 |
|||
* 完全独立,和登录逻辑解耦,后续加其他注册福利只需要加新的监听器即可 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class UserRegisterPointListener { |
|||
|
|||
private final MiniPointRecordService pointRecordService; |
|||
private final MiniPointRuleService pointRuleService; |
|||
|
|||
/** |
|||
* 异步监听用户注册成功事件,赠送积分 |
|||
* 异步执行不影响主线登录流程,就算赠送积分失败也不会影响用户正常登录 |
|||
*/ |
|||
@Async |
|||
@Transactional(rollbackFor = Exception.class) |
|||
@EventListener(UserRegisterEvent.class) |
|||
public void handleUserRegisterGivePoint(UserRegisterEvent event) { |
|||
Long userId = event.getUserId(); |
|||
String source = event.getRegisterSource(); |
|||
log.info("监听到新用户注册成功,userId={}, 注册来源={}, 开始处理注册赠送积分逻辑", userId, source); |
|||
|
|||
try { |
|||
// ================= 1. 幂等校验:避免重复赠送积分 =================
|
|||
long count = pointRecordService.count(new LambdaQueryWrapper<MiniPointRecord>() |
|||
.eq(MiniPointRecord::getUserId, userId) |
|||
.eq(MiniPointRecord::getBizType, "REGISTER_GIFT") |
|||
.eq(MiniPointRecord::getDeleted, false)); |
|||
if (count > 0) { |
|||
log.info("用户{}已经领取过注册积分,无需重复赠送", userId); |
|||
return; |
|||
} |
|||
|
|||
// ================= 2. 查询注册积分规则,可配置无需改代码 =================
|
|||
MiniPointRule registerRule = pointRuleService.getOne(new LambdaQueryWrapper<MiniPointRule>() |
|||
.eq(MiniPointRule::getRuleCode, "REGISTER_GIFT") |
|||
.eq(MiniPointRule::getStatus, false) // 0=启用状态
|
|||
.last("LIMIT 1")); |
|||
if (registerRule == null || registerRule.getPoints() <= 0) { |
|||
log.info("注册赠送积分规则未配置/已关闭/赠送积分为0,不执行赠送"); |
|||
return; |
|||
} |
|||
Integer giftPoint = registerRule.getPoints(); |
|||
|
|||
// ================= 3. 调用现有积分调整接口赠送积分 =================
|
|||
AdjustUserPointForm adjustForm = new AdjustUserPointForm(); |
|||
adjustForm.setUserId(userId); |
|||
adjustForm.setBizType("REGISTER_GIFT"); |
|||
adjustForm.setChangeAmount(giftPoint); |
|||
// 调用之前写的调整积分接口,自动创建积分账户、生成流水
|
|||
pointRecordService.adjustPoint(adjustForm); |
|||
|
|||
log.info("新用户{}注册赠送{}积分成功", userId, giftPoint); |
|||
} catch (Exception e) { |
|||
log.error("新用户{}注册赠送积分失败,请手动排查补发", userId, e); |
|||
// 这里可以加告警通知,比如钉钉/企业微信推送,不影响用户登录
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
package com.youlai.boot.mini.model.query; |
|||
|
|||
import com.youlai.boot.common.base.BaseQuery; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
import java.time.LocalDateTime; |
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* 用户端专属积分流水查询参数,完全独立,不和管理端复用 |
|||
*/ |
|||
@Data |
|||
@Schema(description = "用户端积分流水查询参数") |
|||
public class MyPointRecordQuery extends BaseQuery { |
|||
|
|||
@Schema(description = "业务类型筛选,比如SIGN_IN/AI_GENERATE等,不传查全部") |
|||
private String bizType; |
|||
|
|||
@Schema(description = "查询开始时间,yyyy-MM-dd HH:mm:ss") |
|||
private Date startTime; |
|||
|
|||
@Schema(description = "查询结束时间,yyyy-MM-dd HH:mm:ss") |
|||
private Date endTime; |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
package com.youlai.boot.mini.model.vo; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonFormat; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* 用户端专属积分流水VO,完全独立,不和管理端复用,无数据库自增主键 |
|||
*/ |
|||
@Data |
|||
@Schema(description = "用户端积分流水信息VO") |
|||
public class MyPointRecordVO { |
|||
|
|||
@Schema(description = "流水唯一标识uuid,前端用这个作为唯一key,不用自增id") |
|||
private String uuid; |
|||
|
|||
@Schema(description = "积分变化值,正数为获得积分,负数为扣减积分") |
|||
private Integer changeAmount; |
|||
|
|||
@Schema(description = "本次变动后的积分余额") |
|||
private Integer balanceAfter; |
|||
|
|||
@Schema(description = "业务类型,比如SIGN_IN=签到、AI_GENERATE=AI生成消耗、SYSTEM_ADJUST=系统调整") |
|||
private String bizType; |
|||
|
|||
@Schema(description = "交易发生时间") |
|||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
|||
private Date createTime; |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
package com.youlai.boot.mini.model.vo; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonFormat; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.*; |
|||
|
|||
import java.util.Date; |
|||
|
|||
|
|||
@Data |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@EqualsAndHashCode(callSuper = false) |
|||
@Schema(description = "用户端我的积分信息VO") |
|||
public class MyPointVO { |
|||
|
|||
@Schema(description = "当前积分余额") |
|||
private Integer points; |
|||
|
|||
// @Schema(description = "今日已获得积分,可扩展业务")
|
|||
// private Integer todayEarned;
|
|||
|
|||
@Schema(description = "积分最后更新时间") |
|||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
|||
private Date updateTime; |
|||
|
|||
@Schema(description = "积分账户唯一标识uuid") |
|||
private String uuid; |
|||
|
|||
} |
|||
Loading…
Reference in new issue