JDWA Green-U 用户活动模块
本文档详细介绍了JDWA Green-U项目中用户活动模块的设计思路、数据处理流程和核心实现。该模块负责记录用户的绿色出行活动,计算碳减排量,并发放积分奖励。
模块概述
用户活动模块是整个系统的核心业务模块,负责记录和管理用户的各类绿色低碳活动。主要包括步行、骑行、乘坐公共交通等环保出行方式的数据收集、验证、碳减排计算和积分奖励等功能。
业务价值
- 记录用户的绿色低碳行为,量化环保贡献
- 通过碳减排统计,让用户直观感受自己的环保成果
- 积分奖励机制激励用户持续参与绿色出行
- 为成就系统和积分兑换提供基础数据支持
功能范围
- 用户绿色活动数据采集与存储
- 活动数据有效性验证
- 碳减排量计算
- 积分奖励规则管理
- 用户活动统计与分析
核心功能设计
活动类型定义
系统支持以下几种绿色活动类型:
活动类型 | 代码标识 | 数据要素 | 碳减排计算方式 |
---|---|---|---|
步行 | WALK | 步数、距离、时间 | 0.22kg/km |
骑行 | BIKE | 距离、时间、速度 | 0.15kg/km |
公交出行 | BUS | 距离、线路、站点 | 0.05kg/km |
地铁出行 | SUBWAY | 距离、线路、站点 | 0.03kg/km |
数据采集方式
APP手动记录:
- 用户在APP中手动开始/结束活动
- 系统自动记录GPS轨迹、时间、距离等数据
设备数据同步:
- 同步手机健康应用数据(如步数)
- 支持智能手环、运动手表数据导入
公共交通数据:
- 通过扫码乘车记录公交/地铁出行
- 手动输入起点站和终点站
数据处理流程
数据模型设计
核心表结构
用户活动模块主要涉及以下数据表:
- jdwa_activity:用户活动记录主表
- jdwa_carbon_record:碳减排记录表
- jdwa_point_record:积分记录表
详细表结构参见数据库设计文档
核心实体类
活动实体 (JDWAActivity)
@Data
@TableName("jdwa_activity")
public class JDWAActivity {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private String activityType;
private String activityData;
private LocalDateTime startTime;
private LocalDateTime endTime;
private BigDecimal distance;
private Integer stepCount;
private BigDecimal carbonReduction;
private Integer pointsEarned;
private Integer status;
private String dataSource;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
活动数据模型
@Data
public class JDWAActivityRequest {
private String activityType;
private LocalDateTime startTime;
private LocalDateTime endTime;
private BigDecimal distance;
private Integer stepCount;
private String startLocation;
private String endLocation;
private BigDecimal avgSpeed;
private Map<String, Object> additionalData;
}
活动统计模型
@Data
public class JDWAActivityStats {
private Long userId;
private Integer totalActivities;
private BigDecimal totalDistance;
private Integer totalSteps;
private BigDecimal totalCarbonReduction;
private Integer totalPointsEarned;
private Map<String, Integer> activityTypeCounts;
private List<DailyStats> dailyStats;
}
@Data
public class DailyStats {
private LocalDate date;
private Integer activityCount;
private BigDecimal carbonReduction;
private Integer pointsEarned;
}
核心类与接口
控制器 (JDWAActivityController)
@RestController
@RequestMapping("/api/activity")
public class JDWAActivityController extends JDWABaseController {
@Autowired
private JDWAActivityService activityService;
/**
* 提交活动数据
*/
@PostMapping("/submit")
public JDWAResult<Object> submitActivity(@RequestBody JDWAActivityRequest request) {
Long userId = getCurrentUserId();
JDWAActivity activity = activityService.createActivity(userId, request);
Map<String, Object> data = new HashMap<>();
data.put("activityId", activity.getId());
data.put("carbonReduction", activity.getCarbonReduction());
data.put("pointsEarned", activity.getPointsEarned());
return JDWAResult.success("活动记录成功", data);
}
/**
* 获取活动列表
*/
@GetMapping("/list")
public JDWAResult<Object> getActivityList(
@RequestParam(required = false) String activityType,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
Long userId = getCurrentUserId();
Page<JDWAActivity> page = activityService.getActivityList(
userId, activityType, startDate, endDate, pageNum, pageSize);
return JDWAResult.success("获取成功", page);
}
/**
* 获取活动详情
*/
@GetMapping("/detail/{id}")
public JDWAResult<Object> getActivityDetail(@PathVariable Long id) {
Long userId = getCurrentUserId();
JDWAActivity activity = activityService.getActivityDetail(userId, id);
if (activity == null) {
return JDWAResult.notFound("活动记录不存在");
}
return JDWAResult.success("获取成功", activity);
}
/**
* 获取活动统计数据
*/
@GetMapping("/stats")
public JDWAResult<Object> getActivityStats(
@RequestParam(required = false) String period,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
Long userId = getCurrentUserId();
JDWAActivityStats stats = activityService.getActivityStats(
userId, period, startDate, endDate);
return JDWAResult.success("获取成功", stats);
}
}
服务接口 (JDWAActivityService)
public interface JDWAActivityService extends JDWABaseService<JDWAActivity> {
/**
* 创建活动记录
*
* @param userId 用户ID
* @param request 活动请求数据
* @return 活动记录
*/
JDWAActivity createActivity(Long userId, JDWAActivityRequest request);
/**
* 获取用户活动列表
*
* @param userId 用户ID
* @param activityType 活动类型
* @param startDate 开始日期
* @param endDate 结束日期
* @param pageNum 页码
* @param pageSize 每页大小
* @return 分页活动列表
*/
Page<JDWAActivity> getActivityList(Long userId, String activityType,
String startDate, String endDate, Integer pageNum, Integer pageSize);
/**
* 获取活动详情
*
* @param userId 用户ID
* @param activityId 活动ID
* @return 活动详情
*/
JDWAActivity getActivityDetail(Long userId, Long activityId);
/**
* 获取活动统计数据
*
* @param userId 用户ID
* @param period 统计周期(day/week/month/year)
* @param startDate 开始日期
* @param endDate 结束日期
* @return 统计数据
*/
JDWAActivityStats getActivityStats(Long userId, String period,
String startDate, String endDate);
/**
* 计算碳减排量
*
* @param activityType 活动类型
* @param distance 距离(米)
* @return 碳减排量(kg)
*/
BigDecimal calculateCarbonReduction(String activityType, BigDecimal distance);
/**
* 计算奖励积分
*
* @param carbonReduction 碳减排量(kg)
* @return 积分
*/
Integer calculateRewardPoints(BigDecimal carbonReduction);
}
服务实现 (JDWAActivityServiceImpl)
@Slf4j
@Service
public class JDWAActivityServiceImpl extends JDWABaseServiceImpl<JDWAActivityMapper, JDWAActivity>
implements JDWAActivityService {
@Autowired
private JDWAUserService userService;
@Autowired
private JDWAAchievementService achievementService;
@Override
@Transactional(rollbackFor = Exception.class)
public JDWAActivity createActivity(Long userId, JDWAActivityRequest request) {
// 参数校验
validateActivityRequest(request);
// 创建活动记录
JDWAActivity activity = new JDWAActivity();
activity.setUserId(userId);
activity.setActivityType(request.getActivityType());
activity.setStartTime(request.getStartTime());
activity.setEndTime(request.getEndTime());
activity.setDistance(request.getDistance());
activity.setStepCount(request.getStepCount());
activity.setStatus(1); // 默认有效
activity.setDataSource("APP"); // 默认来源
// 保存活动数据(如果有额外数据)
if (request.getAdditionalData() != null && !request.getAdditionalData().isEmpty()) {
activity.setActivityData(JSON.toJSONString(request.getAdditionalData()));
}
// 计算碳减排量
BigDecimal carbonReduction = calculateCarbonReduction(
request.getActivityType(), request.getDistance());
activity.setCarbonReduction(carbonReduction);
// 计算奖励积分
Integer pointsEarned = calculateRewardPoints(carbonReduction);
activity.setPointsEarned(pointsEarned);
// 保存活动记录
save(activity);
// 更新用户碳减排和积分数据
userService.updateUserPoints(userId, carbonReduction, pointsEarned);
// 检查是否达成成就
achievementService.checkUserAchievements(userId);
return activity;
}
@Override
public Page<JDWAActivity> getActivityList(Long userId, String activityType,
String startDate, String endDate, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<JDWAActivity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(JDWAActivity::getUserId, userId);
queryWrapper.eq(JDWAActivity::getStatus, 1); // 只查询有效记录
// 活动类型筛选
if (StringUtils.hasText(activityType)) {
queryWrapper.eq(JDWAActivity::getActivityType, activityType);
}
// 日期范围筛选
if (StringUtils.hasText(startDate)) {
LocalDateTime startDateTime = LocalDate.parse(startDate).atStartOfDay();
queryWrapper.ge(JDWAActivity::getCreateTime, startDateTime);
}
if (StringUtils.hasText(endDate)) {
LocalDateTime endDateTime = LocalDate.parse(endDate).plusDays(1).atStartOfDay();
queryWrapper.lt(JDWAActivity::getCreateTime, endDateTime);
}
// 按创建时间倒序
queryWrapper.orderByDesc(JDWAActivity::getCreateTime);
// 分页查询
Page<JDWAActivity> page = new Page<>(pageNum, pageSize);
return page(page, queryWrapper);
}
@Override
public JDWAActivity getActivityDetail(Long userId, Long activityId) {
LambdaQueryWrapper<JDWAActivity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(JDWAActivity::getId, activityId);
queryWrapper.eq(JDWAActivity::getUserId, userId);
return getOne(queryWrapper);
}
@Override
public JDWAActivityStats getActivityStats(Long userId, String period,
String startDate, String endDate) {
JDWAActivityStats stats = new JDWAActivityStats();
stats.setUserId(userId);
// 设置查询条件
LambdaQueryWrapper<JDWAActivity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(JDWAActivity::getUserId, userId);
queryWrapper.eq(JDWAActivity::getStatus, 1); // 只统计有效记录
// 处理日期范围
LocalDateTime startDateTime = null;
LocalDateTime endDateTime = null;
if (StringUtils.hasText(period)) {
// 根据统计周期计算日期范围
LocalDate today = LocalDate.now();
switch (period.toLowerCase()) {
case "day":
startDateTime = today.atStartOfDay();
endDateTime = today.plusDays(1).atStartOfDay();
break;
case "week":
startDateTime = today.with(DayOfWeek.MONDAY).atStartOfDay();
endDateTime = today.plusDays(1).atStartOfDay();
break;
case "month":
startDateTime = today.withDayOfMonth(1).atStartOfDay();
endDateTime = today.plusDays(1).atStartOfDay();
break;
case "year":
startDateTime = today.withDayOfYear(1).atStartOfDay();
endDateTime = today.plusDays(1).atStartOfDay();
break;
default:
// 默认使用自定义日期范围
break;
}
}
// 自定义日期范围
if (startDateTime == null && StringUtils.hasText(startDate)) {
startDateTime = LocalDate.parse(startDate).atStartOfDay();
}
if (endDateTime == null && StringUtils.hasText(endDate)) {
endDateTime = LocalDate.parse(endDate).plusDays(1).atStartOfDay();
}
// 设置查询日期范围
if (startDateTime != null) {
queryWrapper.ge(JDWAActivity::getCreateTime, startDateTime);
}
if (endDateTime != null) {
queryWrapper.lt(JDWAActivity::getCreateTime, endDateTime);
}
// 查询统计数据
List<JDWAActivity> activities = list(queryWrapper);
if (activities.isEmpty()) {
stats.setTotalActivities(0);
stats.setTotalDistance(BigDecimal.ZERO);
stats.setTotalSteps(0);
stats.setTotalCarbonReduction(BigDecimal.ZERO);
stats.setTotalPointsEarned(0);
stats.setActivityTypeCounts(new HashMap<>());
stats.setDailyStats(new ArrayList<>());
return stats;
}
// 计算总计数据
stats.setTotalActivities(activities.size());
stats.setTotalDistance(activities.stream()
.map(JDWAActivity::getDistance)
.reduce(BigDecimal.ZERO, BigDecimal::add));
stats.setTotalSteps(activities.stream()
.map(JDWAActivity::getStepCount)
.reduce(0, Integer::sum));
stats.setTotalCarbonReduction(activities.stream()
.map(JDWAActivity::getCarbonReduction)
.reduce(BigDecimal.ZERO, BigDecimal::add));
stats.setTotalPointsEarned(activities.stream()
.map(JDWAActivity::getPointsEarned)
.reduce(0, Integer::sum));
// 按活动类型统计
Map<String, Integer> typeCounts = activities.stream()
.collect(Collectors.groupingBy(
JDWAActivity::getActivityType,
Collectors.summingInt(a -> 1)
));
stats.setActivityTypeCounts(typeCounts);
// 按日期统计
Map<LocalDate, List<JDWAActivity>> dailyActivities = activities.stream()
.collect(Collectors.groupingBy(
a -> a.getCreateTime().toLocalDate()
));
List<DailyStats> dailyStatsList = new ArrayList<>();
dailyActivities.forEach((date, dailyActivityList) -> {
DailyStats dailyStats = new DailyStats();
dailyStats.setDate(date);
dailyStats.setActivityCount(dailyActivityList.size());
dailyStats.setCarbonReduction(dailyActivityList.stream()
.map(JDWAActivity::getCarbonReduction)
.reduce(BigDecimal.ZERO, BigDecimal::add));
dailyStats.setPointsEarned(dailyActivityList.stream()
.map(JDWAActivity::getPointsEarned)
.reduce(0, Integer::sum));
dailyStatsList.add(dailyStats);
});
// 按日期排序
dailyStatsList.sort(Comparator.comparing(DailyStats::getDate));
stats.setDailyStats(dailyStatsList);
return stats;
}
@Override
public BigDecimal calculateCarbonReduction(String activityType, BigDecimal distance) {
// 距离单位转换:米 -> 千米
BigDecimal distanceInKm = distance.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
// 不同活动类型的碳减排系数(kg/km)
BigDecimal coefficient;
switch (activityType) {
case "WALK":
coefficient = new BigDecimal("0.22");
break;
case "BIKE":
coefficient = new BigDecimal("0.15");
break;
case "BUS":
coefficient = new BigDecimal("0.05");
break;
case "SUBWAY":
coefficient = new BigDecimal("0.03");
break;
default:
coefficient = new BigDecimal("0.10"); // 默认系数
}
// 计算碳减排量
return distanceInKm.multiply(coefficient).setScale(2, RoundingMode.HALF_UP);
}
@Override
public Integer calculateRewardPoints(BigDecimal carbonReduction) {
// 简单的积分计算规则:1kg碳减排 = 10积分
BigDecimal points = carbonReduction.multiply(BigDecimal.valueOf(10));
// 最低1积分,最高100积分
int value = points.intValue();
return Math.max(1, Math.min(value, 100));
}
/**
* 验证活动请求数据
*/
private void validateActivityRequest(JDWAActivityRequest request) {
// 检查必填字段
if (request.getActivityType() == null) {
throw new JDWAServiceException("活动类型不能为空");
}
if (request.getStartTime() == null || request.getEndTime() == null) {
throw new JDWAServiceException("活动开始和结束时间不能为空");
}
if (request.getEndTime().isBefore(request.getStartTime())) {
throw new JDWAServiceException("活动结束时间不能早于开始时间");
}
// 检查活动类型是否支持
List<String> supportedTypes = Arrays.asList("WALK", "BIKE", "BUS", "SUBWAY");
if (!supportedTypes.contains(request.getActivityType())) {
throw new JDWAServiceException("不支持的活动类型: " + request.getActivityType());
}
// 针对不同活动类型的特定验证
switch (request.getActivityType()) {
case "WALK":
validateWalkActivity(request);
break;
case "BIKE":
validateBikeActivity(request);
break;
case "BUS":
case "SUBWAY":
validateTransportActivity(request);
break;
default:
break;
}
}
/**
* 验证步行活动数据
*/
private void validateWalkActivity(JDWAActivityRequest request) {
// 步行必须有步数
if (request.getStepCount() == null || request.getStepCount() <= 0) {
throw new JDWAServiceException("步行活动的步数必须大于0");
}
// 步行必须有距离
if (request.getDistance() == null || request.getDistance().compareTo(BigDecimal.ZERO) <= 0) {
throw new JDWAServiceException("步行活动的距离必须大于0");
}
// 步行速度合理性检查
Duration duration = Duration.between(request.getStartTime(), request.getEndTime());
long minutes = duration.toMinutes();
if (minutes <= 0) {
throw new JDWAServiceException("活动时长必须大于0");
}
// 计算步行速度 (km/h)
BigDecimal distanceInKm = request.getDistance().divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
BigDecimal hoursSpent = BigDecimal.valueOf(minutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
BigDecimal speed = distanceInKm.divide(hoursSpent, 2, RoundingMode.HALF_UP);
// 步行速度通常在2-7km/h之间
if (speed.compareTo(new BigDecimal("2")) < 0 || speed.compareTo(new BigDecimal("7")) > 0) {
log.warn("步行速度异常: {}km/h,用户ID: {}", speed, "-");
throw new JDWAServiceException("步行速度异常,请确认数据准确性");
}
}
/**
* 验证骑行活动数据
*/
private void validateBikeActivity(JDWAActivityRequest request) {
// 骑行必须有距离
if (request.getDistance() == null || request.getDistance().compareTo(BigDecimal.ZERO) <= 0) {
throw new JDWAServiceException("骑行活动的距离必须大于0");
}
// 骑行速度合理性检查
Duration duration = Duration.between(request.getStartTime(), request.getEndTime());
long minutes = duration.toMinutes();
if (minutes <= 0) {
throw new JDWAServiceException("活动时长必须大于0");
}
// 计算骑行速度 (km/h)
BigDecimal distanceInKm = request.getDistance().divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
BigDecimal hoursSpent = BigDecimal.valueOf(minutes).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
BigDecimal speed = distanceInKm.divide(hoursSpent, 2, RoundingMode.HALF_UP);
// 普通骑行速度通常在5-25km/h之间
if (speed.compareTo(new BigDecimal("5")) < 0 || speed.compareTo(new BigDecimal("25")) > 0) {
log.warn("骑行速度异常: {}km/h,用户ID: {}", speed, "-");
throw new JDWAServiceException("骑行速度异常,请确认数据准确性");
}
}
/**
* 验证公共交通活动数据
*/
private void validateTransportActivity(JDWAActivityRequest request) {
// 公共交通必须有距离
if (request.getDistance() == null || request.getDistance().compareTo(BigDecimal.ZERO) <= 0) {
throw new JDWAServiceException("公共交通活动的距离必须大于0");
}
// 必须有起点和终点
if (!StringUtils.hasText(request.getStartLocation()) || !StringUtils.hasText(request.getEndLocation())) {
throw new JDWAServiceException("公共交通活动必须提供起点和终点");
}
}
}
API接口规范
提交活动数据
- 接口URL:
/api/activity/submit
- 请求方式: POST
- 接口说明: 用户提交绿色活动数据
- 请求参数:
{
"activityType": "WALK",
"startTime": "2025-05-18T08:30:00",
"endTime": "2025-05-18T09:00:00",
"distance": 2000,
"stepCount": 2800,
"startLocation": "",
"endLocation": "",
"avgSpeed": 4.0,
"additionalData": {
"heartRate": 75,
"calories": 120
}
}
- 响应结果:
{
"code": 200,
"message": "活动记录成功",
"data": {
"activityId": 10086,
"carbonReduction": 0.44,
"pointsEarned": 4
}
}
获取活动列表
接口URL:
/api/activity/list
请求方式: GET
接口说明: 获取用户活动记录列表
请求参数:
- activityType: 活动类型(可选)
- startDate: 开始日期(可选,格式:YYYY-MM-DD)
- endDate: 结束日期(可选,格式:YYYY-MM-DD)
- pageNum: 页码(默认1)
- pageSize: 每页大小(默认10)
响应结果:
{
"code": 200,
"message": "获取成功",
"data": {
"records": [
{
"id": 10086,
"userId": 1001,
"activityType": "WALK",
"startTime": "2025-05-18T08:30:00",
"endTime": "2025-05-18T09:00:00",
"distance": 2000,
"stepCount": 2800,
"carbonReduction": 0.44,
"pointsEarned": 4,
"createTime": "2025-05-18T09:01:05"
},
// 更多记录...
],
"total": 42,
"size": 10,
"current": 1,
"pages": 5
}
}
获取活动详情
接口URL:
/api/activity/detail/{id}
请求方式: GET
接口说明: 获取特定活动的详细信息
请求参数:
- id: 活动ID
响应结果:
{
"code": 200,
"message": "获取成功",
"data": {
"id": 10086,
"userId": 1001,
"activityType": "WALK",
"activityData": "{\"heartRate\":75,\"calories\":120}",
"startTime": "2025-05-18T08:30:00",
"endTime": "2025-05-18T09:00:00",
"distance": 2000,
"stepCount": 2800,
"carbonReduction": 0.44,
"pointsEarned": 4,
"status": 1,
"dataSource": "APP",
"createTime": "2025-05-18T09:01:05",
"updateTime": "2025-05-18T09:01:05"
}
}
获取活动统计数据
接口URL:
/api/activity/stats
请求方式: GET
接口说明: 获取用户活动统计数据
请求参数:
- period: 统计周期(day/week/month/year,可选)
- startDate: 开始日期(可选,格式:YYYY-MM-DD)
- endDate: 结束日期(可选,格式:YYYY-MM-DD)
响应结果:
{
"code": 200,
"message": "获取成功",
"data": {
"userId": 1001,
"totalActivities": 42,
"totalDistance": 85000,
"totalSteps": 112000,
"totalCarbonReduction": 18.7,
"totalPointsEarned": 187,
"activityTypeCounts": {
"WALK": 25,
"BIKE": 10,
"BUS": 5,
"SUBWAY": 2
},
"dailyStats": [
{
"date": "2025-05-15",
"activityCount": 3,
"carbonReduction": 1.2,
"pointsEarned": 12
},
{
"date": "2025-05-16",
"activityCount": 2,
"carbonReduction": 0.8,
"pointsEarned": 8
},
// 更多日期...
]
}
}
数据验证与安全性
为确保系统数据的真实性,系统对用户提交的活动数据进行验证,防止作弊行为。
验证规则
距离与时间合理性检查:
- 步行速度通常在2-7km/h,超出范围需额外验证
- 骑行速度通常在5-25km/h,超出范围需额外验证
步数与距离合理性检查:
- 平均步幅在60-80cm之间(成人)
- 步数与距离的比例应符合合理范围
活动频率限制:
- 同类型活动需间隔一定时间(如5分钟)才能重复提交
- 每日同类型活动次数限制,超出限制需额外验证
数据来源验证:
- 设备同步数据需携带设备标识和认证信息
- 第三方数据接入需进行数字签名验证
防作弊策略
异常数据监测:
- 系统监控异常数据模式,如连续提交相似数据
- 对异常用户进行风险标记,提高验证要求
GPS轨迹验证:
- 长距离活动需录制GPS轨迹
- 系统分析轨迹合理性,检测跳点、异常速度等问题
活动截图验证:
- 可要求用户提供活动App截图
- 对高风险用户实施人工审核
奖励封顶机制:
- 单次活动积分奖励上限
- 每日积分获取上限
碳减排计算方法
计算原理
碳减排量的计算基于"替代原则",即用户选择绿色出行方式所替代的传统出行方式(主要是私家车)产生的碳排放差值。
各活动类型碳减排系数
活动类型 | 碳减排系数(kg/km) | 计算依据 |
---|---|---|
步行 | 0.22 | 平均汽车碳排放量 - 步行碳排放量 |
骑行 | 0.15 | 平均汽车碳排放量 - 骑行碳排放量 |
公交 | 0.05 | 平均汽车碳排放量 - 公交人均碳排放量 |
地铁 | 0.03 | 平均汽车碳排放量 - 地铁人均碳排放量 |
积分奖励规则
系统根据用户减少的碳排放量计算奖励积分:
基础计算公式:
- 积分 = 碳减排量(kg) × 10
- 例如:减少2.5kg碳排放 = 25积分
积分上下限:
- 单次活动最低1积分,最高100积分
- 防止异常数据导致积分计算错误
积分调节机制:
- 不同活动类型可设置积分倍率
- 特殊活动期间可提高积分奖励
日积分上限:
- 每日最多获取300积分
- 防止恶意刷积分行为
系统集成
与其他模块的集成
用户活动模块与系统其他模块紧密集成:
用户模块:
- 获取用户信息
- 更新用户积分和碳减排统计
成就系统:
- 活动记录后触发成就检查
- 达成条件时解锁相应成就
积分系统:
- 将活动奖励积分记录到积分系统
- 提供积分明细和使用历史
数据统计模块:
- 提供活动数据用于统计分析
- 生成环保影响报告
外部系统集成
设备集成:
- 与智能手环、手表API对接
- 支持Apple Health、Google Fit等数据同步
地图服务集成:
- 集成高德/百度地图API
- 提供路线规划和距离计算
公共交通数据接口:
- 对接公交/地铁线路信息
- 验证站点间距离的真实性
性能优化
为保证系统在高并发场景下的稳定性,采取以下性能优化措施:
数据库优化:
- 为高频查询字段创建索引
- 对大表实施分表策略(按用户ID和时间分表)
缓存策略:
- 使用Redis缓存用户近期活动统计
- 设置合理的缓存过期时间
批量处理:
- 积分更新采用批处理方式
- 非关键路径任务异步处理
负载均衡:
- 高峰期活动提交接口分布式部署
- 读写分离减轻主库压力
常见问题解决
活动数据提交失败
问题描述:用户提交活动数据时返回错误。
可能原因:
- 活动数据不符合验证规则
- 数据格式错误
- 网络连接问题
解决方案:
- 检查请求日志,确认具体错误信息
- 验证活动数据是否符合系统规则
- 确认客户端与服务端数据格式一致
- 检查网络连接状态
碳减排量计算不准确
问题描述:用户反馈碳减排量计算与预期不符。
可能原因:
- 系数设置不合理
- 距离计算错误
- 活动类型判断有误
解决方案:
- 检查碳减排系数是否符合最新研究数据
- 验证距离计算算法的准确性
- 确认活动类型判断逻辑正确
- 考虑引入更精确的计算模型
积分未正确发放
问题描述:用户完成活动后未收到积分奖励。
可能原因:
- 事务处理失败
- 用户积分更新异常
- 达到日积分上限
解决方案:
- 检查活动记录状态是否正常
- 验证用户积分变更记录
- 确认是否达到日积分限额
- 检查事务日志定位异常
未来优化计划
短期优化
数据验证增强:
- 引入机器学习模型识别异常活动
- 增加社交验证机制
用户体验改进:
- 活动数据可视化展示
- 活动历史轨迹回放
集成更多活动类型:
- 支持更多环保活动类型
- 提供自定义活动类型
长期规划
社交功能:
- 活动分享与点赞
- 好友间活动PK
精细化奖励机制:
- 动态调整积分奖励算法
- 引入活动推荐系统
大数据分析:
- 用户行为模式分析
- 环保贡献预测模型