JDWA 技术文档
首页
  • 数据库
  • 前端开发
  • 后端开发
  • 开发工具
  • 虚拟化技术
  • KVM显卡直通
  • FPGA仿真固件
  • 项目实战
  • 踩坑记录
  • 开发心得
  • 软件工具
  • 学习资料
  • 开发环境
更新日志
关于我
Gitee
GitHub
首页
  • 数据库
  • 前端开发
  • 后端开发
  • 开发工具
  • 虚拟化技术
  • KVM显卡直通
  • FPGA仿真固件
  • 项目实战
  • 踩坑记录
  • 开发心得
  • 软件工具
  • 学习资料
  • 开发环境
更新日志
关于我
Gitee
GitHub
  • Green-U低碳生活平台(后端)

    • JDWA Green-U 后端技术文档索引
    • JDWA Green-U 项目架构概述
    • JDWA Green-U 数据库设计文档
    • JDWA Green-U 用户认证模块技术文档
    • JDWA Green-U 用户活动模块技术文档
    • JDWA Green-U 碳减排统计模块技术文档
    • JDWA Green-U 积分系统模块技术文档
    • JDWA Green-U 成就系统模块技术文档
    • JDWA Green-U 奖品兑换模块技术文档
    • JDWA Green-U API接口文档
    • JDWA Green-U 系统安全机制与防护措施
    • JDWA Green-U 部署与运维指南

JDWA Green-U 积分系统模块

本文档详细介绍了JDWA Green-U项目中积分系统模块的设计思路、业务规则和核心实现。该模块负责用户积分的获取、消费、统计和交易管理,是激励用户参与绿色低碳活动的核心机制。

  • 模块概述
    • 业务价值
    • 功能范围
  • 核心功能设计
    • 积分类型定义
    • 积分操作类型
    • 积分获取规则
    • 积分消费规则
    • 积分统计分析
  • 数据模型设计
    • 核心表结构
    • 补充表结构
    • 核心实体类
  • 核心类与接口
    • 控制器 (JDWAPointsController)
    • 服务接口 (JDWAPointsService)
    • 任务服务接口 (JDWAPointsTaskService)
    • 商品服务接口 (JDWAPointsGoodsService)
    • 服务实现 (JDWAPointsServiceImpl)
  • API接口规范
    • 获取用户积分概览
    • 获取积分明细记录
    • 获取积分任务列表
    • 完成任务
    • 领取任务奖励
    • 获取积分商品列表
    • 兑换商品
    • 获取兑换订单列表
  • 积分规则管理
    • 积分规则配置
    • 规则类型示例
  • 系统集成
    • 与其他模块的集成
    • 集成流程示例
  • 积分安全措施
    • 防刷机制
    • 事务安全
  • 性能优化
    • 高并发处理
    • 异步积分处理实现
    • 统计性能优化
  • 常见问题解决
  • 未来优化计划
    • 短期优化
    • 长期规划
  • 参考资源
  • 总结

模块概述

积分系统是整个JDWA Green-U平台的核心激励机制,通过量化用户的绿色行为并转化为可兑换的积分,鼓励用户持续参与环保活动。积分系统不仅记录用户的积分变动,还提供积分兑换、积分统计和积分任务等功能,形成完整的用户激励闭环。

业务价值

  • 为用户提供参与环保活动的实际激励
  • 通过积分兑换机制增强用户粘性
  • 量化用户的环保贡献
  • 支持多种激励场景和营销活动
  • 提供数据分析基础,优化用户激励策略

功能范围

  • 积分获取和消费管理
  • 积分明细记录和查询
  • 积分统计和分析
  • 积分兑换商城
  • 积分任务和活动
  • 积分规则配置管理

核心功能设计

积分类型定义

系统支持以下几种积分类型:

积分类型代码标识获取方式有效期用途
绿色积分GREEN_POINTS环保活动奖励永久有效兑换实物奖品和虚拟权益
任务积分TASK_POINTS完成平台任务1年兑换虚拟权益
活动积分EVENT_POINTS参与特殊活动活动期限兑换活动专属奖品
邀请积分INVITE_POINTS邀请新用户永久有效通用积分,可与绿色积分通用

积分操作类型

系统定义以下几种积分操作类型:

  1. 积分收入(INCOME):

    • 绿色活动奖励
    • 任务完成奖励
    • 成就达成奖励
    • 邀请奖励
    • 系统赠送
  2. 积分支出(EXPENSE):

    • 兑换商品
    • 兑换权益
    • 积分过期
    • 系统扣减

积分获取规则

  1. 绿色活动积分:

    • 步行、骑行等绿色出行活动按碳减排量给予积分
    • 公式:积分 = 碳减排量(kg) × 10
    • 单次活动积分上限100分
    • 每日积分上限300分
  2. 任务积分:

    • 每日签到:5分/天
    • 连续签到额外奖励:7天+10分,30天+50分
    • 完善个人信息:20分(一次性)
    • 分享平台:2分/次(每日上限10分)
  3. 邀请积分:

    • 成功邀请新用户注册:20分/人
    • 邀请用户完成首次绿色活动:额外10分/人
    • 邀请积分每月上限200分

积分消费规则

  1. 积分兑换商品:

    • 不同商品设置不同的积分价格
    • 部分商品可组合使用绿色积分+任务积分
    • 兑换流程:选择商品 → 确认兑换 → 扣减积分 → 生成兑换订单
    • 虚拟商品立即到账,实物商品需填写收货信息
  2. 积分兑换权益:

    • 会员等级提升:累计一定积分可提升会员等级
    • 特权解锁:使用积分解锁平台特权功能
    • 勋章兑换:使用积分兑换特殊勋章和头像框
  3. 积分过期规则:

    • 绿色积分和邀请积分永久有效
    • 任务积分有效期1年,到期自动作废
    • 活动积分随活动结束而过期
    • 系统每日凌晨自动检查过期积分并处理
  4. 积分优先级:

    • 消费时优先使用即将过期的积分
    • 同等条件下,优先消费活动积分 > 任务积分 > 绿色积分

积分统计分析

  1. 用户积分统计:

    • 当前可用积分余额
    • 累计获得积分
    • 累计消费积分
    • 即将过期积分
    • 积分消费排行
  2. 平台积分统计:

    • 各类型积分发放总量
    • 积分消费比例
    • 热门兑换商品
    • 用户积分活跃度
    • 积分流通速率

数据模型设计

核心表结构

积分系统模块主要涉及以下数据表:

  • jdwa_user_points:用户积分余额表
  • jdwa_points_record:积分变动记录表
  • jdwa_points_task:积分任务表
  • jdwa_points_rule:积分规则表
  • jdwa_points_goods:积分商品表
  • jdwa_points_order:积分兑换订单表

详细表结构参见数据库设计文档

补充表结构

CREATE TABLE `jdwa_user_points` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '记录ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `points_type` varchar(20) NOT NULL COMMENT '积分类型',
  `points_balance` int(11) NOT NULL DEFAULT '0' COMMENT '积分余额',
  `total_income` int(11) NOT NULL DEFAULT '0' COMMENT '总收入积分',
  `total_expense` int(11) NOT NULL DEFAULT '0' COMMENT '总支出积分',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_points_type` (`user_id`,`points_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户积分余额表';

CREATE TABLE `jdwa_points_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '记录ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `points` int(11) NOT NULL COMMENT '积分数量',
  `points_type` varchar(20) NOT NULL COMMENT '积分类型:GREEN_POINTS/TASK_POINTS/EVENT_POINTS/INVITE_POINTS',
  `operation_type` varchar(20) NOT NULL COMMENT '操作类型:INCOME-收入,EXPENSE-支出',
  `business_type` varchar(50) NOT NULL COMMENT '业务类型',
  `business_id` varchar(50) DEFAULT NULL COMMENT '业务ID',
  `description` varchar(255) DEFAULT NULL COMMENT '描述',
  `before_balance` int(11) NOT NULL COMMENT '变更前余额',
  `after_balance` int(11) NOT NULL COMMENT '变更后余额',
  `expire_time` datetime DEFAULT NULL COMMENT '过期时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_create_time` (`create_time`),
  KEY `idx_business` (`business_type`,`business_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分变动记录表';

CREATE TABLE `jdwa_points_task` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务ID',
  `task_code` varchar(50) NOT NULL COMMENT '任务编码',
  `task_name` varchar(100) NOT NULL COMMENT '任务名称',
  `task_type` varchar(20) NOT NULL COMMENT '任务类型:DAILY-每日任务,ONE_TIME-一次性任务,CONTINUOUS-连续任务',
  `points_type` varchar(20) NOT NULL COMMENT '奖励积分类型',
  `points_amount` int(11) NOT NULL COMMENT '奖励积分数量',
  `task_condition` varchar(255) DEFAULT NULL COMMENT '任务条件JSON',
  `task_icon` varchar(255) DEFAULT NULL COMMENT '任务图标',
  `task_desc` varchar(500) DEFAULT NULL COMMENT '任务描述',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
  `start_time` datetime DEFAULT NULL COMMENT '开始时间',
  `end_time` datetime DEFAULT NULL COMMENT '结束时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_task_code` (`task_code`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分任务表';

核心实体类

用户积分实体 (JDWAUserPoints)

@Data
@TableName("jdwa_user_points")
public class JDWAUserPoints {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private String pointsType;
    private Integer pointsBalance;
    private Integer totalIncome;
    private Integer totalExpense;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

积分记录实体 (JDWAPointsRecord)

@Data
@TableName("jdwa_points_record")
public class JDWAPointsRecord {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private Integer points;
    private String pointsType;
    private String operationType;
    private String businessType;
    private String businessId;
    private String description;
    private Integer beforeBalance;
    private Integer afterBalance;
    private LocalDateTime expireTime;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

积分任务实体 (JDWAPointsTask)

@Data
@TableName("jdwa_points_task")
public class JDWAPointsTask {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String taskCode;
    private String taskName;
    private String taskType;
    private String pointsType;
    private Integer pointsAmount;
    private String taskCondition;
    private String taskIcon;
    private String taskDesc;
    private Integer status;
    private LocalDateTime startTime;
    private LocalDateTime endTime;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

用户任务实体 (JDWAUserTask)

@Data
@TableName("jdwa_user_task")
public class JDWAUserTask {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private Long taskId;
    private String taskCode;
    private Integer progress;
    private Integer targetValue;
    private Integer status;  // 0-未完成,1-已完成,2-已领取奖励
    private LocalDateTime completeTime;
    private LocalDateTime rewardTime;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

积分商品实体 (JDWAPointsGoods)

@Data
@TableName("jdwa_points_goods")
public class JDWAPointsGoods {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String goodsCode;
    private String goodsName;
    private String goodsType;  // VIRTUAL-虚拟商品,PHYSICAL-实物商品
    private Integer pointsPrice;
    private String pointsType;
    private String goodsImg;
    private String goodsDesc;
    private Integer stock;
    private Integer exchangeLimit;  // 单用户兑换限制
    private Integer status;  // 0-下架,1-上架
    private LocalDateTime startTime;
    private LocalDateTime endTime;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

核心类与接口

控制器 (JDWAPointsController)

@RestController
@RequestMapping("/api/points")
public class JDWAPointsController extends JDWABaseController {

    @Autowired
    private JDWAPointsService pointsService;
    
    @Autowired
    private JDWAPointsTaskService taskService;
    
    @Autowired
    private JDWAPointsGoodsService goodsService;
    
    /**
     * 获取用户积分概览
     */
    @GetMapping("/overview")
    public JDWAResult<Object> getPointsOverview() {
        Long userId = getCurrentUserId();
        Map<String, Object> overview = pointsService.getPointsOverview(userId);
        
        return JDWAResult.success("获取成功", overview);
    }
    
    /**
     * 获取积分明细列表
     */
    @GetMapping("/records")
    public JDWAResult<Object> getPointsRecords(
            @RequestParam(required = false) String pointsType,
            @RequestParam(required = false) String operationType,
            @RequestParam(required = false) String startDate,
            @RequestParam(required = false) String endDate,
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize) {
        
        Long userId = getCurrentUserId();
        Page<JDWAPointsRecord> page = pointsService.getPointsRecords(
                userId, pointsType, operationType, startDate, endDate, pageNum, pageSize);
        
        return JDWAResult.success("获取成功", page);
    }
    
    /**
     * 获取任务列表
     */
    @GetMapping("/tasks")
    public JDWAResult<Object> getPointsTasks(
            @RequestParam(required = false) String taskType) {
        
        Long userId = getCurrentUserId();
        List<Map<String, Object>> tasks = taskService.getUserTasks(userId, taskType);
        
        return JDWAResult.success("获取成功", tasks);
    }
    
    /**
     * 完成任务
     */
    @PostMapping("/task/complete")
    public JDWAResult<Object> completeTask(@RequestBody Map<String, Object> params) {
        Long userId = getCurrentUserId();
        String taskCode = (String) params.get("taskCode");
        
        Map<String, Object> result = taskService.completeTask(userId, taskCode);
        
        return JDWAResult.success("任务完成", result);
    }
    
    /**
     * 领取任务奖励
     */
    @PostMapping("/task/reward")
    public JDWAResult<Object> getTaskReward(@RequestBody Map<String, Object> params) {
        Long userId = getCurrentUserId();
        String taskCode = (String) params.get("taskCode");
        
        Map<String, Object> result = taskService.getTaskReward(userId, taskCode);
        
        return JDWAResult.success("奖励领取成功", result);
    }
    
    /**
     * 获取积分商品列表
     */
    @GetMapping("/goods")
    public JDWAResult<Object> getPointsGoods(
            @RequestParam(required = false) String goodsType,
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize) {
        
        Page<JDWAPointsGoods> page = goodsService.getPointsGoods(
                goodsType, pageNum, pageSize);
        
        return JDWAResult.success("获取成功", page);
    }
    
    /**
     * 兑换商品
     */
    @PostMapping("/exchange")
    public JDWAResult<Object> exchangeGoods(@RequestBody Map<String, Object> params) {
        Long userId = getCurrentUserId();
        Long goodsId = Long.valueOf(params.get("goodsId").toString());
        Integer quantity = Integer.valueOf(params.get("quantity").toString());
        
        Map<String, Object> result = goodsService.exchangeGoods(userId, goodsId, quantity);
        
        return JDWAResult.success("兑换成功", result);
    }
    
    /**
     * 获取兑换订单列表
     */
    @GetMapping("/orders")
    public JDWAResult<Object> getExchangeOrders(
            @RequestParam(required = false) Integer status,
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize) {
        
        Long userId = getCurrentUserId();
        Page<JDWAPointsOrder> page = goodsService.getExchangeOrders(
                userId, status, pageNum, pageSize);
        
        return JDWAResult.success("获取成功", page);
    }
}

服务接口 (JDWAPointsService)

public interface JDWAPointsService {
    
    /**
     * 增加用户积分
     *
     * @param userId 用户ID
     * @param points 积分数量
     * @param pointsType 积分类型
     * @param businessType 业务类型
     * @param businessId 业务ID
     * @param description 描述
     * @param expireTime 过期时间
     * @return 积分记录
     */
    JDWAPointsRecord addPoints(Long userId, Integer points, String pointsType, 
            String businessType, String businessId, String description, LocalDateTime expireTime);
    
    /**
     * 扣减用户积分
     *
     * @param userId 用户ID
     * @param points 积分数量
     * @param pointsType 积分类型
     * @param businessType 业务类型
     * @param businessId 业务ID
     * @param description 描述
     * @return 积分记录
     */
    JDWAPointsRecord deductPoints(Long userId, Integer points, String pointsType, 
            String businessType, String businessId, String description);
    
    /**
     * 获取用户积分概览
     *
     * @param userId 用户ID
     * @return 积分概览信息
     */
    Map<String, Object> getPointsOverview(Long userId);
    
    /**
     * 获取用户积分余额
     *
     * @param userId 用户ID
     * @param pointsType 积分类型
     * @return 积分余额
     */
    Integer getPointsBalance(Long userId, String pointsType);
    
    /**
     * 获取用户积分记录
     *
     * @param userId 用户ID
     * @param pointsType 积分类型
     * @param operationType 操作类型
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @return 分页积分记录
     */
    Page<JDWAPointsRecord> getPointsRecords(Long userId, String pointsType, String operationType, 
            String startDate, String endDate, Integer pageNum, Integer pageSize);
    
    /**
     * 检查并处理过期积分
     *
     * @param executeDate 执行日期
     */
    void processExpiredPoints(LocalDate executeDate);
}

任务服务接口 (JDWAPointsTaskService)

public interface JDWAPointsTaskService {
    
    /**
     * 获取用户任务列表
     *
     * @param userId 用户ID
     * @param taskType 任务类型
     * @return 任务列表
     */
    List<Map<String, Object>> getUserTasks(Long userId, String taskType);
    
    /**
     * 完成任务
     *
     * @param userId 用户ID
     * @param taskCode 任务编码
     * @return 结果
     */
    Map<String, Object> completeTask(Long userId, String taskCode);
    
    /**
     * 领取任务奖励
     *
     * @param userId 用户ID
     * @param taskCode 任务编码
     * @return 结果
     */
    Map<String, Object> getTaskReward(Long userId, String taskCode);
    
    /**
     * 检查任务完成状态
     *
     * @param userId 用户ID
     * @param taskCode 任务编码
     * @return 任务状态
     */
    Integer checkTaskStatus(Long userId, String taskCode);
    
    /**
     * 重置每日任务
     */
    void resetDailyTasks();
}

商品服务接口 (JDWAPointsGoodsService)

public interface JDWAPointsGoodsService {
    
    /**
     * 获取积分商品列表
     *
     * @param goodsType 商品类型
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @return 分页商品列表
     */
    Page<JDWAPointsGoods> getPointsGoods(String goodsType, Integer pageNum, Integer pageSize);
    
    /**
     * 兑换商品
     *
     * @param userId 用户ID
     * @param goodsId 商品ID
     * @param quantity 数量
     * @return 结果
     */
    Map<String, Object> exchangeGoods(Long userId, Long goodsId, Integer quantity);
    
    /**
     * 获取兑换订单列表
     *
     * @param userId 用户ID
     * @param status 订单状态
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @return 分页订单列表
     */
    Page<JDWAPointsOrder> getExchangeOrders(Long userId, Integer status, Integer pageNum, Integer pageSize);
}

服务实现 (JDWAPointsServiceImpl)

由于代码较长,这里只展示核心方法实现:

@Slf4j
@Service
public class JDWAPointsServiceImpl implements JDWAPointsService {
    
    @Autowired
    private JDWAUserPointsMapper userPointsMapper;
    
    @Autowired
    private JDWAPointsRecordMapper pointsRecordMapper;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JDWAPointsRecord addPoints(Long userId, Integer points, String pointsType, 
            String businessType, String businessId, String description, LocalDateTime expireTime) {
        
        // 参数校验
        if (points <= 0) {
            throw new JDWAServiceException("积分数量必须大于0");
        }
        
        // 获取或创建用户积分账户
        JDWAUserPoints userPoints = getUserPointsAccount(userId, pointsType);
        
        // 更新用户积分
        Integer beforeBalance = userPoints.getPointsBalance();
        Integer afterBalance = beforeBalance + points;
        
        userPoints.setPointsBalance(afterBalance);
        userPoints.setTotalIncome(userPoints.getTotalIncome() + points);
        userPoints.setUpdateTime(LocalDateTime.now());
        
        userPointsMapper.updateById(userPoints);
        
        // 创建积分记录
        JDWAPointsRecord record = new JDWAPointsRecord();
        record.setUserId(userId);
        record.setPoints(points);
        record.setPointsType(pointsType);
        record.setOperationType("INCOME");
        record.setBusinessType(businessType);
        record.setBusinessId(businessId);
        record.setDescription(description);
        record.setBeforeBalance(beforeBalance);
        record.setAfterBalance(afterBalance);
        record.setExpireTime(expireTime);
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());
        
        pointsRecordMapper.insert(record);
        
        return record;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JDWAPointsRecord deductPoints(Long userId, Integer points, String pointsType, 
            String businessType, String businessId, String description) {
        
        // 参数校验
        if (points <= 0) {
            throw new JDWAServiceException("积分数量必须大于0");
        }
        
        // 获取用户积分账户
        JDWAUserPoints userPoints = getUserPointsAccount(userId, pointsType);
        
        // 检查积分余额
        Integer beforeBalance = userPoints.getPointsBalance();
        if (beforeBalance < points) {
            throw new JDWAServiceException("积分余额不足");
        }
        
        // 更新用户积分
        Integer afterBalance = beforeBalance - points;
        
        userPoints.setPointsBalance(afterBalance);
        userPoints.setTotalExpense(userPoints.getTotalExpense() + points);
        userPoints.setUpdateTime(LocalDateTime.now());
        
        userPointsMapper.updateById(userPoints);
        
        // 创建积分记录
        JDWAPointsRecord record = new JDWAPointsRecord();
        record.setUserId(userId);
        record.setPoints(points);
        record.setPointsType(pointsType);
        record.setOperationType("EXPENSE");
        record.setBusinessType(businessType);
        record.setBusinessId(businessId);
        record.setDescription(description);
        record.setBeforeBalance(beforeBalance);
        record.setAfterBalance(afterBalance);
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());
        
        pointsRecordMapper.insert(record);
        
        return record;
    }
    
    /**
     * 获取用户积分账户,不存在则创建
     */
    private JDWAUserPoints getUserPointsAccount(Long userId, String pointsType) {
        LambdaQueryWrapper<JDWAUserPoints> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(JDWAUserPoints::getUserId, userId)
                .eq(JDWAUserPoints::getPointsType, pointsType);
        
        JDWAUserPoints userPoints = userPointsMapper.selectOne(queryWrapper);
        
        if (userPoints == null) {
            // 创建新账户
            userPoints = new JDWAUserPoints();
            userPoints.setUserId(userId);
            userPoints.setPointsType(pointsType);
            userPoints.setPointsBalance(0);
            userPoints.setTotalIncome(0);
            userPoints.setTotalExpense(0);
            userPoints.setCreateTime(LocalDateTime.now());
            userPoints.setUpdateTime(LocalDateTime.now());
            
            userPointsMapper.insert(userPoints);
        }
        
        return userPoints;
    }
    
    @Override
    @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
    public void processExpiredPoints(LocalDate executeDate) {
        if (executeDate == null) {
            executeDate = LocalDate.now();
        }
        
        log.info("开始处理过期积分,执行日期: {}", executeDate);
        
        // 查询今天过期的积分记录
        LocalDateTime startTime = executeDate.atStartOfDay();
        LocalDateTime endTime = executeDate.plusDays(1).atStartOfDay();
        
        List<JDWAPointsRecord> expiredRecords = pointsRecordMapper.selectExpiredPointsRecords(startTime, endTime);
        
        if (expiredRecords.isEmpty()) {
            log.info("没有需要处理的过期积分");
            return;
        }
        
        log.info("找到{}条过期积分记录", expiredRecords.size());
        
        // 按用户和积分类型分组处理
        Map<String, Integer> expiredPointsMap = new HashMap<>();
        
        for (JDWAPointsRecord record : expiredRecords) {
            String key = record.getUserId() + "_" + record.getPointsType();
            Integer expiredPoints = expiredPointsMap.getOrDefault(key, 0);
            expiredPoints += record.getPoints();
            expiredPointsMap.put(key, expiredPoints);
        }
        
        // 扣减过期积分
        for (Map.Entry<String, Integer> entry : expiredPointsMap.entrySet()) {
            String[] keyParts = entry.getKey().split("_");
            Long userId = Long.valueOf(keyParts[0]);
            String pointsType = keyParts[1];
            Integer points = entry.getValue();
            
            try {
                deductPoints(userId, points, pointsType, "POINTS_EXPIRE", null, "积分过期");
                log.info("用户{}的{}类型积分过期扣减{}积分", userId, pointsType, points);
            } catch (Exception e) {
                log.error("处理用户{}的过期积分失败: {}", userId, e.getMessage(), e);
            }
        }
        
        log.info("过期积分处理完成");
    }
}

API接口规范

获取用户积分概览

  • 接口URL: /api/points/overview

  • 请求方式: GET

  • 接口说明: 获取用户积分概览信息

  • 请求参数: 无

  • 响应结果:

{
  "code": 200,
  "message": "获取成功",
  "data": {
    "totalPoints": 620,
    "pointsBalances": {
      "GREEN_POINTS": 450,
      "TASK_POINTS": 120,
      "EVENT_POINTS": 50,
      "INVITE_POINTS": 0
    },
    "expiringSoon": 50,
    "todayEarned": 35,
    "thisMonthEarned": 180,
    "totalEarned": 750,
    "totalConsumed": 130
  }
}

获取积分明细记录

  • 接口URL: /api/points/records

  • 请求方式: GET

  • 接口说明: 获取用户积分变动记录

  • 请求参数:

    • pointsType: 积分类型(可选)
    • operationType: 操作类型(可选,INCOME/EXPENSE)
    • startDate: 开始日期(可选,格式:YYYY-MM-DD)
    • endDate: 结束日期(可选,格式:YYYY-MM-DD)
    • pageNum: 页码(默认1)
    • pageSize: 每页大小(默认10)
  • 响应结果:

{
  "code": 200,
  "message": "获取成功",
  "data": {
    "records": [
      {
        "id": 10001,
        "userId": 1001,
        "points": 10,
        "pointsType": "GREEN_POINTS",
        "operationType": "INCOME",
        "businessType": "ACTIVITY_REWARD",
        "businessId": "2001",
        "description": "步行活动奖励",
        "beforeBalance": 440,
        "afterBalance": 450,
        "createTime": "2025-05-20T09:01:05"
      },
      {
        "id": 10000,
        "userId": 1001,
        "points": 100,
        "pointsType": "GREEN_POINTS",
        "operationType": "EXPENSE",
        "businessType": "GOODS_EXCHANGE",
        "businessId": "3001",
        "description": "兑换环保袋",
        "beforeBalance": 550,
        "afterBalance": 450,
        "createTime": "2025-05-19T16:30:22"
      },
      // 更多记录...
    ],
    "total": 42,
    "size": 10,
    "current": 1,
    "pages": 5
  }
}

获取积分任务列表

  • 接口URL: /api/points/tasks

  • 请求方式: GET

  • 接口说明: 获取用户积分任务列表

  • 请求参数:

    • taskType: 任务类型(可选,DAILY/ONE_TIME/CONTINUOUS)
  • 响应结果:

{
  "code": 200,
  "message": "获取成功",
  "data": [
    {
      "taskId": 1,
      "taskCode": "DAILY_SIGN",
      "taskName": "每日签到",
      "taskType": "DAILY",
      "pointsType": "TASK_POINTS",
      "pointsAmount": 5,
      "taskDesc": "每日登录签到获取积分",
      "taskIcon": "/images/task/sign.png",
      "status": 0,  // 0-未完成,1-已完成,2-已领取奖励
      "progress": 0,
      "targetValue": 1,
      "progressText": "0/1"
    },
    {
      "taskId": 2,
      "taskCode": "COMPLETE_INFO",
      "taskName": "完善个人信息",
      "taskType": "ONE_TIME",
      "pointsType": "TASK_POINTS",
      "pointsAmount": 20,
      "taskDesc": "完善个人基本信息获取积分",
      "taskIcon": "/images/task/profile.png",
      "status": 1,  // 已完成
      "progress": 1,
      "targetValue": 1,
      "progressText": "已完成"
    },
    // 更多任务...
  ]
}

完成任务

  • 接口URL: /api/points/task/complete
  • 请求方式: POST
  • 接口说明: 提交任务完成
  • 请求参数:
{
  "taskCode": "DAILY_SIGN"
}
  • 响应结果:
{
  "code": 200,
  "message": "任务完成",
  "data": {
    "taskCode": "DAILY_SIGN",
    "status": 1,
    "canReward": true,
    "pointsAmount": 5
  }
}

领取任务奖励

  • 接口URL: /api/points/task/reward
  • 请求方式: POST
  • 接口说明: 领取任务完成奖励
  • 请求参数:
{
  "taskCode": "DAILY_SIGN"
}
  • 响应结果:
{
  "code": 200,
  "message": "奖励领取成功",
  "data": {
    "taskCode": "DAILY_SIGN",
    "status": 2,
    "pointsType": "TASK_POINTS",
    "pointsAmount": 5,
    "recordId": 10002
  }
}

获取积分商品列表

  • 接口URL: /api/points/goods

  • 请求方式: GET

  • 接口说明: 获取积分兑换商品列表

  • 请求参数:

    • goodsType: 商品类型(可选,VIRTUAL/PHYSICAL)
    • pageNum: 页码(默认1)
    • pageSize: 每页大小(默认10)
  • 响应结果:

{
  "code": 200,
  "message": "获取成功",
  "data": {
    "records": [
      {
        "id": 1,
        "goodsCode": "ECO_BAG",
        "goodsName": "环保袋",
        "goodsType": "PHYSICAL",
        "pointsPrice": 100,
        "pointsType": "GREEN_POINTS",
        "goodsImg": "/images/goods/eco_bag.jpg",
        "goodsDesc": "可降解环保购物袋",
        "stock": 500,
        "exchangeLimit": 2,
        "status": 1
      },
      {
        "id": 2,
        "goodsCode": "VIP_BADGE",
        "goodsName": "VIP徽章",
        "goodsType": "VIRTUAL",
        "pointsPrice": 50,
        "pointsType": "TASK_POINTS",
        "goodsImg": "/images/goods/vip_badge.jpg",
        "goodsDesc": "专属VIP用户标识",
        "stock": -1,  // -1表示不限库存
        "exchangeLimit": 1,
        "status": 1
      },
      // 更多商品...
    ],
    "total": 12,
    "size": 10,
    "current": 1,
    "pages": 2
  }
}

兑换商品

  • 接口URL: /api/points/exchange
  • 请求方式: POST
  • 接口说明: 使用积分兑换商品
  • 请求参数:
{
  "goodsId": 1,
  "quantity": 1,
  "address": {  // 实物商品需要
    "name": "张三",
    "phone": "13812345678",
    "province": "广东省",
    "city": "深圳市",
    "district": "南山区",
    "address": "科技园路1号"
  }
}
  • 响应结果:
{
  "code": 200,
  "message": "兑换成功",
  "data": {
    "orderId": 5001,
    "goodsName": "环保袋",
    "quantity": 1,
    "pointsType": "GREEN_POINTS",
    "pointsCost": 100,
    "orderTime": "2025-05-20T10:30:45",
    "status": 1  // 0-待处理,1-处理中,2-已完成,3-已取消
  }
}

获取兑换订单列表

  • 接口URL: /api/points/orders

  • 请求方式: GET

  • 接口说明: 获取用户积分兑换订单列表

  • 请求参数:

    • status: 订单状态(可选)
    • pageNum: 页码(默认1)
    • pageSize: 每页大小(默认10)
  • 响应结果:

{
  "code": 200,
  "message": "获取成功",
  "data": {
    "records": [
      {
        "id": 5001,
        "userId": 1001,
        "goodsId": 1,
        "goodsName": "环保袋",
        "goodsImg": "/images/goods/eco_bag.jpg",
        "quantity": 1,
        "pointsType": "GREEN_POINTS",
        "pointsCost": 100,
        "orderTime": "2025-05-20T10:30:45",
        "status": 1,
        "statusText": "处理中",
        "receiveInfo": "{\"name\":\"张三\",\"phone\":\"13812345678\",\"address\":\"广东省深圳市南山区科技园路1号\"}"
      },
      // 更多订单...
    ],
    "total": 8,
    "size": 10,
    "current": 1,
    "pages": 1
  }
}

积分规则管理

积分规则配置

系统通过积分规则配置表灵活管理各类积分规则,支持运营人员在后台动态调整:

CREATE TABLE `jdwa_points_rule` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '规则ID',
  `rule_code` varchar(50) NOT NULL COMMENT '规则编码',
  `rule_name` varchar(100) NOT NULL COMMENT '规则名称',
  `rule_type` varchar(20) NOT NULL COMMENT '规则类型',
  `business_type` varchar(50) NOT NULL COMMENT '业务类型',
  `points_type` varchar(20) NOT NULL COMMENT '积分类型',
  `base_points` int(11) NOT NULL COMMENT '基础积分',
  `points_formula` varchar(255) DEFAULT NULL COMMENT '积分计算公式',
  `max_points` int(11) DEFAULT NULL COMMENT '单次最大积分',
  `daily_limit` int(11) DEFAULT NULL COMMENT '每日限额',
  `monthly_limit` int(11) DEFAULT NULL COMMENT '每月限额',
  `expire_days` int(11) DEFAULT NULL COMMENT '有效期(天)',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
  `start_time` datetime DEFAULT NULL COMMENT '开始时间',
  `end_time` datetime DEFAULT NULL COMMENT '结束时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_rule_code` (`rule_code`),
  KEY `idx_rule_type` (`rule_type`),
  KEY `idx_business_type` (`business_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分规则表';

规则类型示例

  1. 活动奖励规则:

    • 步行:基础积分0,公式"碳减排量(kg) × 10",单次上限100分,每日上限300分
    • 骑行:基础积分0,公式"碳减排量(kg) × 10",单次上限100分,每日上限300分
    • 公交:基础积分0,公式"碳减排量(kg) × 10",单次上限100分,每日上限300分
  2. 任务奖励规则:

    • 每日签到:基础积分5,公式无,连续7天额外10分
    • 完善信息:基础积分20,公式无,一次性任务
    • 分享平台:基础积分2,公式无,每日上限10分
  3. 特殊奖励规则:

    • 邀请好友:基础积分20,公式无,每月上限200分
    • 成就达成:基础积分根据成就难度设定,公式无,无上限

系统集成

与其他模块的集成

积分系统模块与系统其他模块紧密集成:

  1. 用户活动模块:

    • 用户完成绿色出行活动后,触发积分奖励
    • 通过碳减排量计算积分数量
    • 提供活动相关的积分历史查询
  2. 碳减排统计模块:

    • 获取碳减排数据作为积分计算依据
    • 共享环保数据用于积分兑换展示
  3. 成就系统:

    • 成就达成触发积分奖励
    • 提供积分数据用于成就解锁判断
  4. 用户管理模块:

    • 积分余额作为用户资产信息展示
    • 积分等级影响用户权益
  5. 社交系统:

    • 邀请好友获得积分奖励
    • 好友间积分排行榜

集成流程示例

以下是用户完成绿色出行活动到获得积分奖励的完整流程:

  1. 用户提交绿色出行活动记录
  2. 活动模块验证数据并计算碳减排量
  3. 活动模块调用积分服务,传入用户ID、活动ID、碳减排量等信息
  4. 积分服务根据规则计算应得积分
  5. 积分服务检查当日积分限额
  6. 积分服务创建积分记录并更新用户积分余额
  7. 返回积分奖励结果
  8. 活动模块更新活动记录中的积分奖励信息
  9. 通知用户获得积分奖励
/**
 * 活动完成后的积分奖励流程
 */
@Transactional(rollbackFor = Exception.class)
public JDWAActivity createActivity(Long userId, JDWAActivityRequest request) {
    // 创建活动记录...
    
    // 计算碳减排量
    BigDecimal carbonReduction = calculateCarbonReduction(
            request.getActivityType(), request.getDistance());
    activity.setCarbonReduction(carbonReduction);
    
    // 保存活动记录
    save(activity);
    
    // 计算并发放积分奖励
    JDWAPointsRecord pointsRecord = pointsService.rewardActivityPoints(
            userId, 
            activity.getId(), 
            "ACTIVITY_" + request.getActivityType(), 
            carbonReduction);
    
    // 更新活动记录中的积分奖励信息
    if (pointsRecord != null) {
        activity.setPointsEarned(pointsRecord.getPoints());
        updateById(activity);
    }
    
    // 检查是否达成成就
    achievementService.checkUserAchievements(userId);
    
    return activity;
}

积分安全措施

防刷机制

为防止用户刷积分,系统实施以下安全措施:

  1. 活动频率限制:

    • 同类型活动需间隔一定时间才能重复记录
    • 异常频繁提交将触发风控规则
  2. 单日积分上限:

    • 每种积分类型设置每日获取上限
    • 超过上限的活动不再产生积分
  3. 积分异常监控:

    • 系统监控用户积分获取速率
    • 对异常用户进行风险标记
  4. 特殊活动审核:

    • 高额积分奖励活动需人工审核
    • 大额积分兑换需二次验证

事务安全

确保积分操作的原子性,避免因系统异常导致积分丢失或重复:

@Transactional(rollbackFor = Exception.class)
public JDWAPointsRecord addPoints(Long userId, Integer points, String pointsType, 
        String businessType, String businessId, String description, LocalDateTime expireTime) {
    // 获取用户积分账户(加锁)
    JDWAUserPoints userPoints = getUserPointsAccountWithLock(userId, pointsType);
    
    try {
        // 更新用户积分
        Integer beforeBalance = userPoints.getPointsBalance();
        Integer afterBalance = beforeBalance + points;
        
        userPoints.setPointsBalance(afterBalance);
        userPoints.setTotalIncome(userPoints.getTotalIncome() + points);
        userPoints.setUpdateTime(LocalDateTime.now());
        
        userPointsMapper.updateById(userPoints);
        
        // 创建积分记录
        JDWAPointsRecord record = new JDWAPointsRecord();
        // 设置记录信息...
        
        pointsRecordMapper.insert(record);
        
        // 记录积分操作日志
        log.info("用户{}增加{}积分,业务类型:{},业务ID:{}", 
                userId, points, businessType, businessId);
        
        return record;
    } catch (Exception e) {
        log.error("积分增加失败:{},用户ID:{},积分:{}", 
                e.getMessage(), userId, points, e);
        throw new JDWAServiceException("积分增加失败");
    }
}

/**
 * 获取用户积分账户(加锁)
 */
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public JDWAUserPoints getUserPointsAccountWithLock(Long userId, String pointsType) {
    LambdaQueryWrapper<JDWAUserPoints> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(JDWAUserPoints::getUserId, userId)
            .eq(JDWAUserPoints::getPointsType, pointsType)
            .last("FOR UPDATE");
    
    JDWAUserPoints userPoints = userPointsMapper.selectOne(queryWrapper);
    
    if (userPoints == null) {
        // 创建新账户
        userPoints = new JDWAUserPoints();
        // 设置账户信息...
        
        userPointsMapper.insert(userPoints);
    }
    
    return userPoints;
}

性能优化

高并发处理

在用户活动高峰期,积分操作可能面临高并发场景,系统采取以下优化措施:

  1. 数据库优化:

    • 为高频查询字段创建索引
    • 使用行级锁避免并发问题
    • 分库分表存储积分记录
  2. 异步处理:

    • 积分奖励异步发放
    • 积分明细异步记录
    • 积分统计定时更新
  3. 缓存策略:

    • 使用Redis缓存用户积分余额
    • 缓存积分规则配置
    • 热点数据本地缓存

异步积分处理实现

/**
 * 异步发放积分奖励
 */
@Async("pointsTaskExecutor")
public void asyncRewardPoints(Long userId, Long businessId, String businessType, 
        BigDecimal value, String description) {
    try {
        // 获取积分规则
        JDWAPointsRule rule = getPointsRule(businessType);
        if (rule == null || rule.getStatus() == 0) {
            log.warn("积分规则不存在或已禁用:{}", businessType);
            return;
        }
        
        // 计算积分
        Integer points = calculatePoints(rule, value);
        if (points <= 0) {
            return;
        }
        
        // 检查日限额
        if (rule.getDailyLimit() != null && rule.getDailyLimit() > 0) {
            Integer todayPoints = getTodayPointsByBusinessType(userId, businessType);
            if (todayPoints >= rule.getDailyLimit()) {
                log.info("用户{}今日{}类型积分已达上限:{}", userId, businessType, rule.getDailyLimit());
                return;
            }
            
            // 调整为不超过日限额
            if (todayPoints + points > rule.getDailyLimit()) {
                points = rule.getDailyLimit() - todayPoints;
            }
        }
        
        // 设置过期时间
        LocalDateTime expireTime = null;
        if (rule.getExpireDays() != null && rule.getExpireDays() > 0) {
            expireTime = LocalDateTime.now().plusDays(rule.getExpireDays());
        }
        
        // 发放积分
        addPoints(userId, points, rule.getPointsType(), businessType, 
                businessId == null ? null : businessId.toString(), description, expireTime);
        
    } catch (Exception e) {
        log.error("异步发放积分失败:{},用户ID:{},业务类型:{}", 
                e.getMessage(), userId, businessType, e);
    }
}

统计性能优化

针对积分统计查询的性能优化:

  1. 预聚合数据:

    • 定时任务聚合汇总用户积分数据
    • 分级缓存统计结果
  2. 多级查询:

    • 先查询内存缓存
    • 缓存未命中查询Redis
    • Redis未命中查询数据库
    • 查询结果回填缓存
  3. 分页查询优化:

    • 积分明细采用游标分页
    • 避免深度分页问题

常见问题解决

积分未到账

问题描述:用户完成活动后未收到积分奖励。

可能原因:

  • 活动数据未通过验证
  • 已达到当日积分上限
  • 积分发放事务失败
  • 系统延迟未及时显示

解决方案:

  1. 检查活动记录状态是否正常
  2. 确认用户当日积分是否达到上限
  3. 查看积分服务日志定位错误
  4. 检查异步任务执行状态
  5. 必要时手动补发积分
积分兑换失败

问题描述:用户兑换商品时提示失败。

可能原因:

  • 积分余额不足
  • 商品库存不足
  • 超出单用户兑换限制
  • 兑换事务处理异常

解决方案:

  1. 验证用户积分余额
  2. 检查商品库存状态
  3. 确认是否超出兑换限制
  4. 查看系统日志定位具体错误
  5. 必要时手动处理订单
积分变动异常

问题描述:用户积分余额发生异常变动。

可能原因:

  • 并发操作导致数据不一致
  • 过期积分扣减
  • 系统补偿或回滚操作
  • 管理员手动调整

解决方案:

  1. 查询积分变动记录追踪来源
  2. 检查过期积分处理日志
  3. 验证积分账户数据一致性
  4. 必要时修正积分余额

未来优化计划

短期优化

  1. 积分玩法丰富:

    • 积分抽奖功能
    • 积分众筹活动
    • 限时积分翻倍
  2. 用户体验改进:

    • 积分变动实时推送
    • 积分使用建议
    • 积分到期提醒
  3. 商品体系扩展:

    • 增加积分兑换品类
    • 引入第三方商品
    • 支持积分+现金组合支付

长期规划

  1. 积分生态建设:

    • 积分交换平台
    • 积分转赠功能
    • 品牌联合积分计划
  2. 智能推荐系统:

    • 基于用户行为的积分任务推荐
    • 个性化积分商品推荐
    • 智能积分使用建议
  3. 区块链积分:

    • 探索基于区块链的积分通证
    • 积分真实性验证
    • 跨平台积分互通

参考资源

  • 用户激励系统设计最佳实践
  • 积分经济模型研究
  • 高并发积分系统架构
  • 电子商务积分解决方案
  • 用户成长体系设计

总结

积分系统是JDWA Green-U平台的核心激励机制,通过将用户的绿色行为量化为可兑换的积分,有效提升了用户参与环保活动的积极性。系统设计了灵活的积分类型、获取规则和消费机制,满足不同场景的激励需求。

通过与活动记录、碳减排统计、成就系统等模块的紧密集成,积分系统形成了完整的用户激励闭环,用户可以通过绿色出行、任务完成、成就达成等多种方式获取积分,并通过积分兑换各类实物和虚拟商品,实现环保行为的价值转化。

系统在设计中充分考虑了高并发处理、数据一致性、安全防刷等关键问题,采用了异步处理、缓存策略、事务安全等技术手段确保系统的稳定性和可靠性。未来,随着业务的发展,积分系统将不断丰富玩法、优化体验,打造更加完善的用户激励生态。

Prev
JDWA Green-U 碳减排统计模块技术文档
Next
JDWA Green-U 成就系统模块技术文档