From 4e7683739d39a25535a766fcb894538500a8bf1b Mon Sep 17 00:00:00 2001 From: glx <783262171@qq.com> Date: Mon, 20 Apr 2026 17:35:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=A8=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E7=A7=AF=E5=88=86=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/WxMaAuthServiceImpl.java | 7 ++ .../boot/common/event/UserRegisterEvent.java | 28 +++++++ .../boot/mini/controller/PointController.java | 50 ++++++------ .../listener/UserRegisterPointListener.java | 77 +++++++++++++++++++ .../mini/mapper/MiniPointRecordMapper.java | 16 +++- .../mini/model/query/MyPointRecordQuery.java | 25 ++++++ .../boot/mini/model/vo/MyPointRecordVO.java | 31 ++++++++ .../youlai/boot/mini/model/vo/MyPointVO.java | 31 ++++++++ .../mini/service/MiniPointAccountService.java | 3 + .../mini/service/MiniPointRecordService.java | 10 +++ .../impl/MiniPointAccountServiceImpl.java | 20 ++++- .../impl/MiniPointRecordServiceImpl.java | 12 ++- .../mapper/mini/MiniPointRecordMapper.xml | 36 ++++++++- 13 files changed, 318 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/youlai/boot/common/event/UserRegisterEvent.java create mode 100644 src/main/java/com/youlai/boot/mini/listener/UserRegisterPointListener.java create mode 100644 src/main/java/com/youlai/boot/mini/model/query/MyPointRecordQuery.java create mode 100644 src/main/java/com/youlai/boot/mini/model/vo/MyPointRecordVO.java create mode 100644 src/main/java/com/youlai/boot/mini/model/vo/MyPointVO.java diff --git a/src/main/java/com/youlai/boot/auth/service/impl/WxMaAuthServiceImpl.java b/src/main/java/com/youlai/boot/auth/service/impl/WxMaAuthServiceImpl.java index 02d85bb..7053024 100644 --- a/src/main/java/com/youlai/boot/auth/service/impl/WxMaAuthServiceImpl.java +++ b/src/main/java/com/youlai/boot/auth/service/impl/WxMaAuthServiceImpl.java @@ -20,6 +20,7 @@ import com.youlai.boot.system.service.UserService; import com.youlai.boot.system.service.UserRoleService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -27,6 +28,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.youlai.boot.common.event.UserRegisterEvent; import java.time.LocalDateTime; import java.util.Collections; @@ -49,6 +51,7 @@ public class WxMaAuthServiceImpl implements WxMaAuthService { private final UserSocialService userSocialService; private final UserRoleService userRoleService; private final RedisTemplate redisTemplate; + private final ApplicationEventPublisher eventPublisher; /** * 静默登录 @@ -192,6 +195,10 @@ public class WxMaAuthServiceImpl implements WxMaAuthService { // 分配 GUEST 角色(角色ID=3) userRoleService.saveUserRoles(user.getId(), Collections.singletonList(3L)); + // 发布用户注册成功事件,后续所有注册附加逻辑(送积分、优惠券等)都监听这个事件即可 + eventPublisher.publishEvent(new UserRegisterEvent(user.getId(), "MINI_PROGRAM")); + log.info("新用户{}注册成功,事件已发布", user.getId()); + return user; } diff --git a/src/main/java/com/youlai/boot/common/event/UserRegisterEvent.java b/src/main/java/com/youlai/boot/common/event/UserRegisterEvent.java new file mode 100644 index 0000000..a1a0257 --- /dev/null +++ b/src/main/java/com/youlai/boot/common/event/UserRegisterEvent.java @@ -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; + } +} diff --git a/src/main/java/com/youlai/boot/mini/controller/PointController.java b/src/main/java/com/youlai/boot/mini/controller/PointController.java index 2169ba7..14e88a3 100644 --- a/src/main/java/com/youlai/boot/mini/controller/PointController.java +++ b/src/main/java/com/youlai/boot/mini/controller/PointController.java @@ -1,12 +1,14 @@ package com.youlai.boot.mini.controller; import com.baomidou.mybatisplus.core.metadata.IPage; -import com.youlai.boot.common.annotation.Log; -import com.youlai.boot.common.enums.ActionTypeEnum; -import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.result.PageResult; +import com.youlai.boot.common.result.Result; +import com.youlai.boot.framework.security.util.SecurityUtils; +import com.youlai.boot.common.result.PageResult; +import com.youlai.boot.mini.model.query.MyPointRecordQuery; import com.youlai.boot.mini.model.query.PointAccountQuery; -import com.youlai.boot.mini.model.vo.PointAccountVO; +import com.youlai.boot.mini.model.vo.MyPointRecordVO; +import com.youlai.boot.mini.model.vo.MyPointVO; import com.youlai.boot.mini.service.MiniPointAccountService; import com.youlai.boot.mini.service.MiniPointRecordService; import com.youlai.boot.mini.service.MiniPointRuleService; @@ -14,7 +16,6 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @Tag(name = "用户端积分服务相关接口") @@ -27,23 +28,28 @@ public class PointController { private final MiniPointRuleService pointRuleService; private final MiniPointRecordService recordService; -// @Operation(summary = "查询用户积分") -// @GetMapping(value = "/getListPage") -// @PreAuthorize("@ss.hasPerm('mini:point:account:list')") -// @Log(module = LogModuleEnum.POINT_ACCOUNT, value = ActionTypeEnum.LIST) -// public PageResult page(@ParameterObject PointAccountQuery queryParams) { -// IPage result = pointAccountService.page(queryParams); -// return PageResult.success(result); -// } - - //查询用户积分 - //查询积分明细 - //查询积分统计 - - //用户注册赠送积分 - //每日签到 - //分享奖励 + @Operation(summary = "查询当前用户的积分账户信息") + @GetMapping("/my") + public Result getMyPoint() { + Long userId = SecurityUtils.getUserId(); + MyPointVO myPoint = pointAccountService.getUserPoint(userId); + return Result.success(myPoint); + } + + @Operation(summary = "分页查询当前用户的积分流水列表") + @GetMapping("/records") + public PageResult getMyPointRecords(@ParameterObject MyPointRecordQuery query) { + Long userId = SecurityUtils.getUserId(); + return PageResult.success(recordService.pageMyPointRecord(userId, query)); + } + + //事件监听 实现 用户注册赠送积分 + + //每日签到,加签到表, + + //分享奖励,分享链接带上分享人ID,其他用户点击链接进入小程序后才给分享人发奖励 + //AI生成图片扣费 - //AI生成视频扣费 + //AI生成视频扣费 } diff --git a/src/main/java/com/youlai/boot/mini/listener/UserRegisterPointListener.java b/src/main/java/com/youlai/boot/mini/listener/UserRegisterPointListener.java new file mode 100644 index 0000000..b4b6d74 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/listener/UserRegisterPointListener.java @@ -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() + .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() + .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); + // 这里可以加告警通知,比如钉钉/企业微信推送,不影响用户登录 + } + } +} diff --git a/src/main/java/com/youlai/boot/mini/mapper/MiniPointRecordMapper.java b/src/main/java/com/youlai/boot/mini/mapper/MiniPointRecordMapper.java index 13311db..835fd4d 100644 --- a/src/main/java/com/youlai/boot/mini/mapper/MiniPointRecordMapper.java +++ b/src/main/java/com/youlai/boot/mini/mapper/MiniPointRecordMapper.java @@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.youlai.boot.mini.model.entity.MiniPointRecord; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.youlai.boot.mini.model.query.MyPointRecordQuery; import com.youlai.boot.mini.model.query.PointRecordQuery; +import com.youlai.boot.mini.model.vo.MyPointRecordVO; import com.youlai.boot.mini.model.vo.PointRecordVO; import org.apache.ibatis.annotations.Param; @@ -12,16 +14,26 @@ import org.apache.ibatis.annotations.Param; * 用户积分流水表 Mapper 接口 * * @author jwy -* @since +* @since */ public interface MiniPointRecordMapper extends BaseMapper { /** - * 分页查询所有积分记录(关联用户信息) + * 分页查询所有积分记录(关联用户信息,管理端用) * @param page 分页参数 * @param query 查询条件 * @return 分页结果 */ IPage pageAllRecord(Page page, @Param("query") PointRecordQuery query); + /** + * 分页查询指定用户的积分流水(用户端专属) + * @param page 分页参数 + * @param userId 用户ID,内部传参,避免越权 + * @param query 用户端查询条件 + * @return 用户端积分流水分页 + */ + IPage pageMyPointRecord(Page page, @Param("userId") Long userId, + @Param("query") MyPointRecordQuery query); + } diff --git a/src/main/java/com/youlai/boot/mini/model/query/MyPointRecordQuery.java b/src/main/java/com/youlai/boot/mini/model/query/MyPointRecordQuery.java new file mode 100644 index 0000000..891248d --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/query/MyPointRecordQuery.java @@ -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; + +} diff --git a/src/main/java/com/youlai/boot/mini/model/vo/MyPointRecordVO.java b/src/main/java/com/youlai/boot/mini/model/vo/MyPointRecordVO.java new file mode 100644 index 0000000..3ff8947 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/vo/MyPointRecordVO.java @@ -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; + +} diff --git a/src/main/java/com/youlai/boot/mini/model/vo/MyPointVO.java b/src/main/java/com/youlai/boot/mini/model/vo/MyPointVO.java new file mode 100644 index 0000000..aeee9e6 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/vo/MyPointVO.java @@ -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; + +} diff --git a/src/main/java/com/youlai/boot/mini/service/MiniPointAccountService.java b/src/main/java/com/youlai/boot/mini/service/MiniPointAccountService.java index 1ad35d1..0386bfe 100644 --- a/src/main/java/com/youlai/boot/mini/service/MiniPointAccountService.java +++ b/src/main/java/com/youlai/boot/mini/service/MiniPointAccountService.java @@ -4,10 +4,13 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.youlai.boot.mini.model.entity.MiniPointAccount; import com.youlai.boot.mini.model.query.PointAccountQuery; +import com.youlai.boot.mini.model.vo.MyPointVO; import com.youlai.boot.mini.model.vo.PointAccountVO; public interface MiniPointAccountService extends IService { IPage pageAccount(PointAccountQuery queryParams); + MyPointVO getUserPoint(Long userId); + } diff --git a/src/main/java/com/youlai/boot/mini/service/MiniPointRecordService.java b/src/main/java/com/youlai/boot/mini/service/MiniPointRecordService.java index 82b29b8..60f76a8 100644 --- a/src/main/java/com/youlai/boot/mini/service/MiniPointRecordService.java +++ b/src/main/java/com/youlai/boot/mini/service/MiniPointRecordService.java @@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.youlai.boot.mini.model.entity.MiniPointRecord; import com.youlai.boot.mini.model.form.AdjustUserPointForm; +import com.youlai.boot.mini.model.query.MyPointRecordQuery; import com.youlai.boot.mini.model.query.PointRecordQuery; +import com.youlai.boot.mini.model.vo.MyPointRecordVO; import com.youlai.boot.mini.model.vo.PointRecordVO; public interface MiniPointRecordService extends IService { @@ -22,4 +24,12 @@ public interface MiniPointRecordService extends IService { */ IPage pageAllRecord(PointRecordQuery query); + /** + * 分页查询当前用户的积分流水(用户端专属) + * @param userId 当前登录用户ID,内部传参避免越权 + * @param query 用户端查询条件 + * @return 用户端积分流水分页 + */ + IPage pageMyPointRecord(Long userId, MyPointRecordQuery query); + } diff --git a/src/main/java/com/youlai/boot/mini/service/impl/MiniPointAccountServiceImpl.java b/src/main/java/com/youlai/boot/mini/service/impl/MiniPointAccountServiceImpl.java index a7674ff..8fea592 100644 --- a/src/main/java/com/youlai/boot/mini/service/impl/MiniPointAccountServiceImpl.java +++ b/src/main/java/com/youlai/boot/mini/service/impl/MiniPointAccountServiceImpl.java @@ -4,11 +4,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.youlai.boot.framework.security.util.SecurityUtils; +import com.youlai.boot.common.exception.MsgException; import com.youlai.boot.mini.converter.MiniPointAccountConverter; import com.youlai.boot.mini.mapper.*; import com.youlai.boot.mini.model.entity.MiniPointAccount; import com.youlai.boot.mini.model.query.PointAccountQuery; +import com.youlai.boot.mini.model.vo.MyPointVO; import com.youlai.boot.mini.model.vo.PointAccountVO; import com.youlai.boot.mini.service.MiniPointAccountService; import lombok.RequiredArgsConstructor; @@ -30,4 +31,21 @@ public class MiniPointAccountServiceImpl extends ServiceImpl() + .eq(MiniPointAccount::getUserId, userId) + .eq(MiniPointAccount::getDeleted, false) + .last("LIMIT 1")); + if (account == null) { + throw new MsgException("用户积分账户不存在,请先完成注册激活"); + } + + MyPointVO myPoint = new MyPointVO(); + myPoint.setPoints(account.getPoints()); + myPoint.setUuid(account.getUuid()); + myPoint.setUpdateTime(account.getUpdateTime()); + return myPoint; + } + } diff --git a/src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java b/src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java index 697805c..d9b5b9f 100644 --- a/src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java +++ b/src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java @@ -10,7 +10,9 @@ import com.youlai.boot.mini.mapper.MiniPointRecordMapper; import com.youlai.boot.mini.model.entity.MiniPointAccount; import com.youlai.boot.mini.model.entity.MiniPointRecord; import com.youlai.boot.mini.model.form.AdjustUserPointForm; +import com.youlai.boot.mini.model.query.MyPointRecordQuery; import com.youlai.boot.mini.model.query.PointRecordQuery; +import com.youlai.boot.mini.model.vo.MyPointRecordVO; import com.youlai.boot.mini.model.vo.PointRecordVO; import com.youlai.boot.mini.service.MiniPointAccountService; import com.youlai.boot.mini.service.MiniPointRecordService; @@ -75,10 +77,16 @@ public class MiniPointRecordServiceImpl extends ServiceImpl pageAllRecord(PointRecordQuery query) { Page page = new Page<>(query.getPageNum(), query.getPageSize()); - // 调用mapper关联查询,直接返回VO分页结果 return baseMapper.pageAllRecord(page, query); } + + @Override + public IPage pageMyPointRecord(Long userId, MyPointRecordQuery query) { + Page page = new Page<>(query.getPageNum(), query.getPageSize()); + return baseMapper.pageMyPointRecord(page, userId, query); + } + } diff --git a/src/main/resources/mapper/mini/MiniPointRecordMapper.xml b/src/main/resources/mapper/mini/MiniPointRecordMapper.xml index 79d44dd..d83ab4c 100644 --- a/src/main/resources/mapper/mini/MiniPointRecordMapper.xml +++ b/src/main/resources/mapper/mini/MiniPointRecordMapper.xml @@ -5,7 +5,7 @@ - + @@ -15,6 +15,7 @@ + @@ -57,4 +58,37 @@ r.create_time DESC + + + + + + + + + +