JDWA Green-U 积分系统模块
本文档详细介绍了JDWA Green-U项目中积分系统模块的设计思路、业务规则和核心实现。该模块负责用户积分的获取、消费、统计和交易管理,是激励用户参与绿色低碳活动的核心机制。
模块概述
积分系统是整个JDWA Green-U平台的核心激励机制,通过量化用户的绿色行为并转化为可兑换的积分,鼓励用户持续参与环保活动。积分系统不仅记录用户的积分变动,还提供积分兑换、积分统计和积分任务等功能,形成完整的用户激励闭环。
业务价值
- 为用户提供参与环保活动的实际激励
- 通过积分兑换机制增强用户粘性
- 量化用户的环保贡献
- 支持多种激励场景和营销活动
- 提供数据分析基础,优化用户激励策略
功能范围
- 积分获取和消费管理
- 积分明细记录和查询
- 积分统计和分析
- 积分兑换商城
- 积分任务和活动
- 积分规则配置管理
核心功能设计
积分类型定义
系统支持以下几种积分类型:
积分类型 | 代码标识 | 获取方式 | 有效期 | 用途 |
---|---|---|---|---|
绿色积分 | GREEN_POINTS | 环保活动奖励 | 永久有效 | 兑换实物奖品和虚拟权益 |
任务积分 | TASK_POINTS | 完成平台任务 | 1年 | 兑换虚拟权益 |
活动积分 | EVENT_POINTS | 参与特殊活动 | 活动期限 | 兑换活动专属奖品 |
邀请积分 | INVITE_POINTS | 邀请新用户 | 永久有效 | 通用积分,可与绿色积分通用 |
积分操作类型
系统定义以下几种积分操作类型:
积分收入(INCOME):
- 绿色活动奖励
- 任务完成奖励
- 成就达成奖励
- 邀请奖励
- 系统赠送
积分支出(EXPENSE):
- 兑换商品
- 兑换权益
- 积分过期
- 系统扣减
积分获取规则
绿色活动积分:
- 步行、骑行等绿色出行活动按碳减排量给予积分
- 公式:积分 = 碳减排量(kg) × 10
- 单次活动积分上限100分
- 每日积分上限300分
任务积分:
- 每日签到:5分/天
- 连续签到额外奖励:7天+10分,30天+50分
- 完善个人信息:20分(一次性)
- 分享平台:2分/次(每日上限10分)
邀请积分:
- 成功邀请新用户注册:20分/人
- 邀请用户完成首次绿色活动:额外10分/人
- 邀请积分每月上限200分
积分消费规则
积分兑换商品:
- 不同商品设置不同的积分价格
- 部分商品可组合使用绿色积分+任务积分
- 兑换流程:选择商品 → 确认兑换 → 扣减积分 → 生成兑换订单
- 虚拟商品立即到账,实物商品需填写收货信息
积分兑换权益:
- 会员等级提升:累计一定积分可提升会员等级
- 特权解锁:使用积分解锁平台特权功能
- 勋章兑换:使用积分兑换特殊勋章和头像框
积分过期规则:
- 绿色积分和邀请积分永久有效
- 任务积分有效期1年,到期自动作废
- 活动积分随活动结束而过期
- 系统每日凌晨自动检查过期积分并处理
积分优先级:
- 消费时优先使用即将过期的积分
- 同等条件下,优先消费活动积分 > 任务积分 > 绿色积分
积分统计分析
用户积分统计:
- 当前可用积分余额
- 累计获得积分
- 累计消费积分
- 即将过期积分
- 积分消费排行
平台积分统计:
- 各类型积分发放总量
- 积分消费比例
- 热门兑换商品
- 用户积分活跃度
- 积分流通速率
数据模型设计
核心表结构
积分系统模块主要涉及以下数据表:
- 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='积分规则表';
规则类型示例
活动奖励规则:
- 步行:基础积分0,公式"碳减排量(kg) × 10",单次上限100分,每日上限300分
- 骑行:基础积分0,公式"碳减排量(kg) × 10",单次上限100分,每日上限300分
- 公交:基础积分0,公式"碳减排量(kg) × 10",单次上限100分,每日上限300分
任务奖励规则:
- 每日签到:基础积分5,公式无,连续7天额外10分
- 完善信息:基础积分20,公式无,一次性任务
- 分享平台:基础积分2,公式无,每日上限10分
特殊奖励规则:
- 邀请好友:基础积分20,公式无,每月上限200分
- 成就达成:基础积分根据成就难度设定,公式无,无上限
系统集成
与其他模块的集成
积分系统模块与系统其他模块紧密集成:
用户活动模块:
- 用户完成绿色出行活动后,触发积分奖励
- 通过碳减排量计算积分数量
- 提供活动相关的积分历史查询
碳减排统计模块:
- 获取碳减排数据作为积分计算依据
- 共享环保数据用于积分兑换展示
成就系统:
- 成就达成触发积分奖励
- 提供积分数据用于成就解锁判断
用户管理模块:
- 积分余额作为用户资产信息展示
- 积分等级影响用户权益
社交系统:
- 邀请好友获得积分奖励
- 好友间积分排行榜
集成流程示例
以下是用户完成绿色出行活动到获得积分奖励的完整流程:
- 用户提交绿色出行活动记录
- 活动模块验证数据并计算碳减排量
- 活动模块调用积分服务,传入用户ID、活动ID、碳减排量等信息
- 积分服务根据规则计算应得积分
- 积分服务检查当日积分限额
- 积分服务创建积分记录并更新用户积分余额
- 返回积分奖励结果
- 活动模块更新活动记录中的积分奖励信息
- 通知用户获得积分奖励
/**
* 活动完成后的积分奖励流程
*/
@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;
}
积分安全措施
防刷机制
为防止用户刷积分,系统实施以下安全措施:
活动频率限制:
- 同类型活动需间隔一定时间才能重复记录
- 异常频繁提交将触发风控规则
单日积分上限:
- 每种积分类型设置每日获取上限
- 超过上限的活动不再产生积分
积分异常监控:
- 系统监控用户积分获取速率
- 对异常用户进行风险标记
特殊活动审核:
- 高额积分奖励活动需人工审核
- 大额积分兑换需二次验证
事务安全
确保积分操作的原子性,避免因系统异常导致积分丢失或重复:
@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;
}
性能优化
高并发处理
在用户活动高峰期,积分操作可能面临高并发场景,系统采取以下优化措施:
数据库优化:
- 为高频查询字段创建索引
- 使用行级锁避免并发问题
- 分库分表存储积分记录
异步处理:
- 积分奖励异步发放
- 积分明细异步记录
- 积分统计定时更新
缓存策略:
- 使用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);
}
}
统计性能优化
针对积分统计查询的性能优化:
预聚合数据:
- 定时任务聚合汇总用户积分数据
- 分级缓存统计结果
多级查询:
- 先查询内存缓存
- 缓存未命中查询Redis
- Redis未命中查询数据库
- 查询结果回填缓存
分页查询优化:
- 积分明细采用游标分页
- 避免深度分页问题
常见问题解决
积分未到账
问题描述:用户完成活动后未收到积分奖励。
可能原因:
- 活动数据未通过验证
- 已达到当日积分上限
- 积分发放事务失败
- 系统延迟未及时显示
解决方案:
- 检查活动记录状态是否正常
- 确认用户当日积分是否达到上限
- 查看积分服务日志定位错误
- 检查异步任务执行状态
- 必要时手动补发积分
积分兑换失败
问题描述:用户兑换商品时提示失败。
可能原因:
- 积分余额不足
- 商品库存不足
- 超出单用户兑换限制
- 兑换事务处理异常
解决方案:
- 验证用户积分余额
- 检查商品库存状态
- 确认是否超出兑换限制
- 查看系统日志定位具体错误
- 必要时手动处理订单
积分变动异常
问题描述:用户积分余额发生异常变动。
可能原因:
- 并发操作导致数据不一致
- 过期积分扣减
- 系统补偿或回滚操作
- 管理员手动调整
解决方案:
- 查询积分变动记录追踪来源
- 检查过期积分处理日志
- 验证积分账户数据一致性
- 必要时修正积分余额
未来优化计划
短期优化
积分玩法丰富:
- 积分抽奖功能
- 积分众筹活动
- 限时积分翻倍
用户体验改进:
- 积分变动实时推送
- 积分使用建议
- 积分到期提醒
商品体系扩展:
- 增加积分兑换品类
- 引入第三方商品
- 支持积分+现金组合支付
长期规划
积分生态建设:
- 积分交换平台
- 积分转赠功能
- 品牌联合积分计划
智能推荐系统:
- 基于用户行为的积分任务推荐
- 个性化积分商品推荐
- 智能积分使用建议
区块链积分:
- 探索基于区块链的积分通证
- 积分真实性验证
- 跨平台积分互通
参考资源
总结
积分系统是JDWA Green-U平台的核心激励机制,通过将用户的绿色行为量化为可兑换的积分,有效提升了用户参与环保活动的积极性。系统设计了灵活的积分类型、获取规则和消费机制,满足不同场景的激励需求。
通过与活动记录、碳减排统计、成就系统等模块的紧密集成,积分系统形成了完整的用户激励闭环,用户可以通过绿色出行、任务完成、成就达成等多种方式获取积分,并通过积分兑换各类实物和虚拟商品,实现环保行为的价值转化。
系统在设计中充分考虑了高并发处理、数据一致性、安全防刷等关键问题,采用了异步处理、缓存策略、事务安全等技术手段确保系统的稳定性和可靠性。未来,随着业务的发展,积分系统将不断丰富玩法、优化体验,打造更加完善的用户激励生态。