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项目中用户活动模块的设计思路、数据处理流程和核心实现。该模块负责记录用户的绿色出行活动,计算碳减排量,并发放积分奖励。

  • 模块概述
    • 业务价值
    • 功能范围
  • 核心功能设计
    • 活动类型定义
    • 数据采集方式
    • 数据处理流程
  • 数据模型设计
    • 核心表结构
    • 核心实体类
  • 核心类与接口
    • 控制器 (JDWAActivityController)
    • 服务接口 (JDWAActivityService)
    • 服务实现 (JDWAActivityServiceImpl)
  • API接口规范
    • 提交活动数据
    • 获取活动列表
    • 获取活动详情
    • 获取活动统计数据
  • 数据验证与安全性
    • 验证规则
    • 防作弊策略
  • 碳减排计算方法
    • 计算原理
    • 各活动类型碳减排系数
    • 积分奖励规则
  • 系统集成
    • 与其他模块的集成
    • 外部系统集成
  • 性能优化
  • 常见问题解决
  • 未来优化计划
    • 短期优化
    • 长期规划
  • 参考资源

模块概述

用户活动模块是整个系统的核心业务模块,负责记录和管理用户的各类绿色低碳活动。主要包括步行、骑行、乘坐公共交通等环保出行方式的数据收集、验证、碳减排计算和积分奖励等功能。

业务价值

  • 记录用户的绿色低碳行为,量化环保贡献
  • 通过碳减排统计,让用户直观感受自己的环保成果
  • 积分奖励机制激励用户持续参与绿色出行
  • 为成就系统和积分兑换提供基础数据支持

功能范围

  • 用户绿色活动数据采集与存储
  • 活动数据有效性验证
  • 碳减排量计算
  • 积分奖励规则管理
  • 用户活动统计与分析

核心功能设计

活动类型定义

系统支持以下几种绿色活动类型:

活动类型代码标识数据要素碳减排计算方式
步行WALK步数、距离、时间0.22kg/km
骑行BIKE距离、时间、速度0.15kg/km
公交出行BUS距离、线路、站点0.05kg/km
地铁出行SUBWAY距离、线路、站点0.03kg/km

数据采集方式

  1. APP手动记录:

    • 用户在APP中手动开始/结束活动
    • 系统自动记录GPS轨迹、时间、距离等数据
  2. 设备数据同步:

    • 同步手机健康应用数据(如步数)
    • 支持智能手环、运动手表数据导入
  3. 公共交通数据:

    • 通过扫码乘车记录公交/地铁出行
    • 手动输入起点站和终点站

数据处理流程

数据模型设计

核心表结构

用户活动模块主要涉及以下数据表:

  • 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
      },
      // 更多日期...
    ]
  }
}

数据验证与安全性

为确保系统数据的真实性,系统对用户提交的活动数据进行验证,防止作弊行为。

验证规则

  1. 距离与时间合理性检查:

    • 步行速度通常在2-7km/h,超出范围需额外验证
    • 骑行速度通常在5-25km/h,超出范围需额外验证
  2. 步数与距离合理性检查:

    • 平均步幅在60-80cm之间(成人)
    • 步数与距离的比例应符合合理范围
  3. 活动频率限制:

    • 同类型活动需间隔一定时间(如5分钟)才能重复提交
    • 每日同类型活动次数限制,超出限制需额外验证
  4. 数据来源验证:

    • 设备同步数据需携带设备标识和认证信息
    • 第三方数据接入需进行数字签名验证

防作弊策略

  1. 异常数据监测:

    • 系统监控异常数据模式,如连续提交相似数据
    • 对异常用户进行风险标记,提高验证要求
  2. GPS轨迹验证:

    • 长距离活动需录制GPS轨迹
    • 系统分析轨迹合理性,检测跳点、异常速度等问题
  3. 活动截图验证:

    • 可要求用户提供活动App截图
    • 对高风险用户实施人工审核
  4. 奖励封顶机制:

    • 单次活动积分奖励上限
    • 每日积分获取上限

碳减排计算方法

计算原理

碳减排量的计算基于"替代原则",即用户选择绿色出行方式所替代的传统出行方式(主要是私家车)产生的碳排放差值。

各活动类型碳减排系数

活动类型碳减排系数(kg/km)计算依据
步行0.22平均汽车碳排放量 - 步行碳排放量
骑行0.15平均汽车碳排放量 - 骑行碳排放量
公交0.05平均汽车碳排放量 - 公交人均碳排放量
地铁0.03平均汽车碳排放量 - 地铁人均碳排放量

积分奖励规则

系统根据用户减少的碳排放量计算奖励积分:

  1. 基础计算公式:

    • 积分 = 碳减排量(kg) × 10
    • 例如:减少2.5kg碳排放 = 25积分
  2. 积分上下限:

    • 单次活动最低1积分,最高100积分
    • 防止异常数据导致积分计算错误
  3. 积分调节机制:

    • 不同活动类型可设置积分倍率
    • 特殊活动期间可提高积分奖励
  4. 日积分上限:

    • 每日最多获取300积分
    • 防止恶意刷积分行为

系统集成

与其他模块的集成

用户活动模块与系统其他模块紧密集成:

  1. 用户模块:

    • 获取用户信息
    • 更新用户积分和碳减排统计
  2. 成就系统:

    • 活动记录后触发成就检查
    • 达成条件时解锁相应成就
  3. 积分系统:

    • 将活动奖励积分记录到积分系统
    • 提供积分明细和使用历史
  4. 数据统计模块:

    • 提供活动数据用于统计分析
    • 生成环保影响报告

外部系统集成

  1. 设备集成:

    • 与智能手环、手表API对接
    • 支持Apple Health、Google Fit等数据同步
  2. 地图服务集成:

    • 集成高德/百度地图API
    • 提供路线规划和距离计算
  3. 公共交通数据接口:

    • 对接公交/地铁线路信息
    • 验证站点间距离的真实性

性能优化

为保证系统在高并发场景下的稳定性,采取以下性能优化措施:

  1. 数据库优化:

    • 为高频查询字段创建索引
    • 对大表实施分表策略(按用户ID和时间分表)
  2. 缓存策略:

    • 使用Redis缓存用户近期活动统计
    • 设置合理的缓存过期时间
  3. 批量处理:

    • 积分更新采用批处理方式
    • 非关键路径任务异步处理
  4. 负载均衡:

    • 高峰期活动提交接口分布式部署
    • 读写分离减轻主库压力

常见问题解决

活动数据提交失败

问题描述:用户提交活动数据时返回错误。

可能原因:

  • 活动数据不符合验证规则
  • 数据格式错误
  • 网络连接问题

解决方案:

  1. 检查请求日志,确认具体错误信息
  2. 验证活动数据是否符合系统规则
  3. 确认客户端与服务端数据格式一致
  4. 检查网络连接状态
碳减排量计算不准确

问题描述:用户反馈碳减排量计算与预期不符。

可能原因:

  • 系数设置不合理
  • 距离计算错误
  • 活动类型判断有误

解决方案:

  1. 检查碳减排系数是否符合最新研究数据
  2. 验证距离计算算法的准确性
  3. 确认活动类型判断逻辑正确
  4. 考虑引入更精确的计算模型
积分未正确发放

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

可能原因:

  • 事务处理失败
  • 用户积分更新异常
  • 达到日积分上限

解决方案:

  1. 检查活动记录状态是否正常
  2. 验证用户积分变更记录
  3. 确认是否达到日积分限额
  4. 检查事务日志定位异常

未来优化计划

短期优化

  1. 数据验证增强:

    • 引入机器学习模型识别异常活动
    • 增加社交验证机制
  2. 用户体验改进:

    • 活动数据可视化展示
    • 活动历史轨迹回放
  3. 集成更多活动类型:

    • 支持更多环保活动类型
    • 提供自定义活动类型

长期规划

  1. 社交功能:

    • 活动分享与点赞
    • 好友间活动PK
  2. 精细化奖励机制:

    • 动态调整积分奖励算法
    • 引入活动推荐系统
  3. 大数据分析:

    • 用户行为模式分析
    • 环保贡献预测模型

参考资源

  • 碳排放计算方法研究
  • SpringBoot数据验证最佳实践
  • 环保活动碳减排影响报告
  • MyBatis-Plus高性能查询优化
Prev
JDWA Green-U 用户认证模块技术文档
Next
JDWA Green-U 碳减排统计模块技术文档