JDWA Green-U 碳减排统计模块
本文档详细介绍了JDWA Green-U项目中碳减排统计模块的设计思路、实现方法和核心功能。该模块负责收集和分析用户绿色行为的碳减排数据,提供多维度的统计分析和可视化展示。
模块概述
碳减排统计模块是系统中的核心分析模块,负责对用户的绿色行为进行碳减排量计算、统计分析和数据展示。通过多维度的数据分析,为用户提供直观的环保成果展示,同时为系统运营提供决策支持。
业务价值
- 量化用户环保行为的实际价值,提升用户参与感
- 通过数据可视化,增强用户环保意识和成就感
- 提供碳减排统计报告,支持环保项目评估
- 为系统运营和市场推广提供数据支持
功能范围
- 碳减排数据收集与存储
- 碳减排量计算与校验
- 多维度统计分析(时间、地区、活动类型等)
- 数据可视化展示
- 碳减排证书生成
- 环保影响评估报告
技术架构
整体架构
┌─────────────────┐ ┌────────────────┐ ┌────────────────┐
│ │ │ │ │ │
│ 数据采集层 │────►│ 计算分析层 │────►│ 展示服务层 │
│ │ │ │ │ │
└─────────────────┘ └────────────────┘ └────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌────────────────┐ ┌────────────────┐
│ • 活动数据收集 │ │ • 碳减排算法 │ │ • 实时仪表盘 │
│ • 外部数据接入 │ │ • 统计分析 │ │ • 趋势图表 │
│ • 历史数据导入 │ │ • 数据挖掘 │ │ • 排行榜 │
│ • 数据清洗 │ │ • 预测模型 │ │ • 报告生成 │
└─────────────────┘ └────────────────┘ └────────────────┘
数据流程
[用户活动数据] ──► [碳减排计算] ──► [碳减排记录] ──► [统计聚合] ──► [数据展示] │ │ │ ▼ ▼ ▼ [数据验证] [历史存档] [导出报告]
数据模型设计
核心表结构
碳减排统计模块主要涉及以下数据表:
- jdwa_carbon_record:碳减排记录表
- jdwa_carbon_stats:碳减排统计表
- jdwa_carbon_report:碳减排报告表
详细表结构参见数据库设计文档
补充表结构
CREATE TABLE `jdwa_carbon_stats` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '统计ID',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID(为空表示全局统计)',
`stats_type` varchar(20) NOT NULL COMMENT '统计类型:DAY/WEEK/MONTH/YEAR/ALL',
`stats_date` date NOT NULL COMMENT '统计日期',
`activity_type` varchar(20) DEFAULT NULL COMMENT '活动类型(为空表示所有类型)',
`record_count` int(11) DEFAULT '0' COMMENT '记录数量',
`total_distance` decimal(15,2) DEFAULT '0.00' COMMENT '总距离(米)',
`total_carbon` decimal(15,2) DEFAULT '0.00' COMMENT '总碳减排量(kg)',
`avg_carbon` decimal(10,2) DEFAULT '0.00' COMMENT '平均碳减排量(kg)',
`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_stats_index` (`user_id`,`stats_type`,`stats_date`,`activity_type`),
KEY `idx_stats_date` (`stats_date`),
KEY `idx_stats_type` (`stats_type`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='碳减排统计表';
CREATE TABLE `jdwa_carbon_report` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '报告ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`report_type` varchar(20) NOT NULL COMMENT '报告类型:MONTHLY/QUARTERLY/YEARLY',
`report_date` date NOT NULL COMMENT '报告日期',
`start_date` date NOT NULL COMMENT '开始日期',
`end_date` date NOT NULL COMMENT '结束日期',
`total_carbon` decimal(15,2) DEFAULT '0.00' COMMENT '总碳减排量(kg)',
`total_activities` int(11) DEFAULT '0' COMMENT '总活动次数',
`activity_stats` json DEFAULT NULL COMMENT '活动统计JSON',
`env_impact` json DEFAULT NULL COMMENT '环境影响JSON',
`certification_code` varchar(50) DEFAULT NULL COMMENT '证书编码',
`status` tinyint(4) DEFAULT '0' COMMENT '状态:0-生成中,1-已生成,2-已发送',
`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_report` (`user_id`,`report_type`,`report_date`),
KEY `idx_report_date` (`report_date`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='碳减排报告表';
核心实体类
碳减排记录实体 (JDWACarbonRecord)
@Data
@TableName("jdwa_carbon_record")
public class JDWACarbonRecord {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Long activityId;
private BigDecimal carbonAmount;
private String recordType;
private String description;
private LocalDateTime createTime;
}
碳减排统计实体 (JDWACarbonStats)
@Data
@TableName("jdwa_carbon_stats")
public class JDWACarbonStats {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private String statsType;
private LocalDate statsDate;
private String activityType;
private Integer recordCount;
private BigDecimal totalDistance;
private BigDecimal totalCarbon;
private BigDecimal avgCarbon;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
碳减排报告实体 (JDWACarbonReport)
@Data
@TableName("jdwa_carbon_report")
public class JDWACarbonReport {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private String reportType;
private LocalDate reportDate;
private LocalDate startDate;
private LocalDate endDate;
private BigDecimal totalCarbon;
private Integer totalActivities;
private String activityStats;
private String envImpact;
private String certificationCode;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
统计数据模型
@Data
public class CarbonStatsSummary {
private Long userId;
private BigDecimal totalCarbon;
private BigDecimal dailyAvgCarbon;
private BigDecimal weeklyAvgCarbon;
private BigDecimal monthlyAvgCarbon;
private Integer totalActivities;
private Map<String, BigDecimal> carbonByActivityType;
private List<DateCarbonStats> dailyStats;
private List<DateCarbonStats> weeklyStats;
private List<DateCarbonStats> monthlyStats;
}
@Data
public class DateCarbonStats {
private LocalDate date;
private BigDecimal carbonAmount;
private Integer activityCount;
}
@Data
public class EnvImpactData {
private BigDecimal carbonAmount;
private BigDecimal treeEquivalent;
private BigDecimal fuelSaved;
private BigDecimal electricitySaved;
}
核心类与接口
控制器 (JDWACarbonController)
@RestController
@RequestMapping("/api/carbon")
public class JDWACarbonController extends JDWABaseController {
@Autowired
private JDWACarbonService carbonService;
/**
* 获取碳减排记录列表
*/
@GetMapping("/records")
public JDWAResult<Object> getCarbonRecords(
@RequestParam(required = false) String recordType,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
Long userId = getCurrentUserId();
Page<JDWACarbonRecord> page = carbonService.getCarbonRecords(
userId, recordType, startDate, endDate, pageNum, pageSize);
return JDWAResult.success("获取成功", page);
}
/**
* 获取碳减排统计摘要
*/
@GetMapping("/stats/summary")
public JDWAResult<Object> getCarbonStatsSummary(
@RequestParam(required = false) String period,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
Long userId = getCurrentUserId();
CarbonStatsSummary summary = carbonService.getCarbonStatsSummary(
userId, period, startDate, endDate);
return JDWAResult.success("获取成功", summary);
}
/**
* 获取环境影响数据
*/
@GetMapping("/impact")
public JDWAResult<Object> getEnvironmentalImpact() {
Long userId = getCurrentUserId();
EnvImpactData impact = carbonService.calculateEnvironmentalImpact(userId);
return JDWAResult.success("获取成功", impact);
}
/**
* 生成碳减排报告
*/
@PostMapping("/report/generate")
public JDWAResult<Object> generateCarbonReport(
@RequestBody Map<String, Object> params) {
Long userId = getCurrentUserId();
String reportType = (String) params.get("reportType");
String reportDate = (String) params.get("reportDate");
JDWACarbonReport report = carbonService.generateCarbonReport(
userId, reportType, reportDate);
return JDWAResult.success("报告生成成功", report);
}
/**
* 获取碳减排报告列表
*/
@GetMapping("/reports")
public JDWAResult<Object> getCarbonReports(
@RequestParam(required = false) String reportType,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
Long userId = getCurrentUserId();
Page<JDWACarbonReport> page = carbonService.getCarbonReports(
userId, reportType, pageNum, pageSize);
return JDWAResult.success("获取成功", page);
}
/**
* 获取碳减排报告详情
*/
@GetMapping("/report/{id}")
public JDWAResult<Object> getCarbonReportDetail(@PathVariable Long id) {
Long userId = getCurrentUserId();
JDWACarbonReport report = carbonService.getCarbonReportDetail(userId, id);
if (report == null) {
return JDWAResult.notFound("报告不存在");
}
return JDWAResult.success("获取成功", report);
}
}
服务接口 (JDWACarbonService)
public interface JDWACarbonService {
/**
* 记录碳减排
*
* @param userId 用户ID
* @param activityId 活动ID
* @param carbonAmount 碳减排量
* @param recordType 记录类型
* @param description 描述
* @return 碳减排记录
*/
JDWACarbonRecord recordCarbonReduction(Long userId, Long activityId,
BigDecimal carbonAmount, String recordType, String description);
/**
* 获取用户碳减排记录列表
*
* @param userId 用户ID
* @param recordType 记录类型
* @param startDate 开始日期
* @param endDate 结束日期
* @param pageNum 页码
* @param pageSize 每页大小
* @return 分页碳减排记录列表
*/
Page<JDWACarbonRecord> getCarbonRecords(Long userId, String recordType,
String startDate, String endDate, Integer pageNum, Integer pageSize);
/**
* 获取碳减排统计摘要
*
* @param userId 用户ID
* @param period 统计周期
* @param startDate 开始日期
* @param endDate 结束日期
* @return 统计摘要
*/
CarbonStatsSummary getCarbonStatsSummary(Long userId, String period,
String startDate, String endDate);
/**
* 计算环境影响数据
*
* @param userId 用户ID
* @return 环境影响数据
*/
EnvImpactData calculateEnvironmentalImpact(Long userId);
/**
* 生成碳减排报告
*
* @param userId 用户ID
* @param reportType 报告类型
* @param reportDate 报告日期
* @return 碳减排报告
*/
JDWACarbonReport generateCarbonReport(Long userId, String reportType, String reportDate);
/**
* 获取碳减排报告列表
*
* @param userId 用户ID
* @param reportType 报告类型
* @param pageNum 页码
* @param pageSize 每页大小
* @return 分页碳减排报告列表
*/
Page<JDWACarbonReport> getCarbonReports(Long userId, String reportType,
Integer pageNum, Integer pageSize);
/**
* 获取碳减排报告详情
*
* @param userId 用户ID
* @param reportId 报告ID
* @return 碳减排报告详情
*/
JDWACarbonReport getCarbonReportDetail(Long userId, Long reportId);
/**
* 更新碳减排统计数据
*
* @param executeDate 执行日期
*/
void updateCarbonStats(LocalDate executeDate);
}
服务实现 (JDWACarbonServiceImpl)
由于代码较长,这里只展示核心方法实现:
@Slf4j
@Service
public class JDWACarbonServiceImpl implements JDWACarbonService {
@Autowired
private JDWACarbonRecordMapper carbonRecordMapper;
@Autowired
private JDWACarbonStatsMapper carbonStatsMapper;
@Autowired
private JDWACarbonReportMapper carbonReportMapper;
@Autowired
private JDWAActivityMapper activityMapper;
@Autowired
private JDWAUserService userService;
@Override
@Transactional(rollbackFor = Exception.class)
public JDWACarbonRecord recordCarbonReduction(Long userId, Long activityId,
BigDecimal carbonAmount, String recordType, String description) {
// 创建碳减排记录
JDWACarbonRecord record = new JDWACarbonRecord();
record.setUserId(userId);
record.setActivityId(activityId);
record.setCarbonAmount(carbonAmount);
record.setRecordType(recordType);
record.setDescription(description);
carbonRecordMapper.insert(record);
// 异步更新统计数据
// 实际实现可以使用Spring的@Async或消息队列
updateDailyCarbonStats(userId, LocalDate.now(), recordType, carbonAmount);
return record;
}
// ... 其他接口实现 ...
@Override
public CarbonStatsSummary getCarbonStatsSummary(Long userId, String period,
String startDate, String endDate) {
CarbonStatsSummary summary = new CarbonStatsSummary();
summary.setUserId(userId);
// 处理日期范围
LocalDate start = null;
LocalDate end = LocalDate.now();
if (StringUtils.hasText(period)) {
// 根据周期设置日期范围
switch (period.toLowerCase()) {
case "week":
start = end.minusWeeks(1);
break;
case "month":
start = end.minusMonths(1);
break;
case "year":
start = end.minusYears(1);
break;
default:
start = end.minusDays(30); // 默认30天
break;
}
} else {
// 自定义日期范围
if (StringUtils.hasText(startDate)) {
start = LocalDate.parse(startDate);
} else {
start = end.minusDays(30); // 默认30天
}
if (StringUtils.hasText(endDate)) {
end = LocalDate.parse(endDate);
}
}
// 查询总碳减排量
BigDecimal totalCarbon = carbonRecordMapper.sumCarbonAmountByUserIdAndDateRange(
userId, start, end.plusDays(1));
summary.setTotalCarbon(totalCarbon != null ? totalCarbon : BigDecimal.ZERO);
// 查询总活动次数
Integer totalActivities = carbonRecordMapper.countRecordsByUserIdAndDateRange(
userId, start, end.plusDays(1));
summary.setTotalActivities(totalActivities != null ? totalActivities : 0);
// 按活动类型统计碳减排量
Map<String, BigDecimal> carbonByType = carbonRecordMapper.sumCarbonAmountByUserIdGroupByType(
userId, start, end.plusDays(1));
summary.setCarbonByActivityType(carbonByType != null ? carbonByType : new HashMap<>());
// 日均碳减排量
long days = ChronoUnit.DAYS.between(start, end) + 1;
if (totalCarbon != null && days > 0) {
BigDecimal dailyAvg = totalCarbon.divide(
BigDecimal.valueOf(days), 2, RoundingMode.HALF_UP);
summary.setDailyAvgCarbon(dailyAvg);
} else {
summary.setDailyAvgCarbon(BigDecimal.ZERO);
}
// 获取日统计数据
List<DateCarbonStats> dailyStats = carbonStatsMapper.getDailyCarbonStatsByUserIdAndDateRange(
userId, start, end.plusDays(1));
summary.setDailyStats(dailyStats != null ? dailyStats : new ArrayList<>());
// 获取周统计数据
List<DateCarbonStats> weeklyStats = carbonStatsMapper.getWeeklyCarbonStatsByUserIdAndDateRange(
userId, start, end.plusDays(1));
summary.setWeeklyStats(weeklyStats != null ? weeklyStats : new ArrayList<>());
// 获取月统计数据
List<DateCarbonStats> monthlyStats = carbonStatsMapper.getMonthlyCarbonStatsByUserIdAndDateRange(
userId, start, end.plusDays(1));
summary.setMonthlyStats(monthlyStats != null ? monthlyStats : new ArrayList<>());
// 计算周均值
BigDecimal weeklyTotal = weeklyStats.stream()
.map(DateCarbonStats::getCarbonAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
int weekCount = weeklyStats.size();
summary.setWeeklyAvgCarbon(weekCount > 0 ?
weeklyTotal.divide(BigDecimal.valueOf(weekCount), 2, RoundingMode.HALF_UP) :
BigDecimal.ZERO);
// 计算月均值
BigDecimal monthlyTotal = monthlyStats.stream()
.map(DateCarbonStats::getCarbonAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
int monthCount = monthlyStats.size();
summary.setMonthlyAvgCarbon(monthCount > 0 ?
monthlyTotal.divide(BigDecimal.valueOf(monthCount), 2, RoundingMode.HALF_UP) :
BigDecimal.ZERO);
return summary;
}
@Override
public EnvImpactData calculateEnvironmentalImpact(Long userId) {
// 获取用户总碳减排量
BigDecimal totalCarbon = carbonRecordMapper.sumCarbonAmountByUserId(userId);
if (totalCarbon == null) {
totalCarbon = BigDecimal.ZERO;
}
EnvImpactData impact = new EnvImpactData();
impact.setCarbonAmount(totalCarbon);
// 计算环境影响指标
// 1棵树每年约吸收25kg CO2
BigDecimal treeEquivalent = totalCarbon.divide(
new BigDecimal("25"), 2, RoundingMode.HALF_UP);
impact.setTreeEquivalent(treeEquivalent);
// 1升汽油燃烧产生约2.3kg CO2
BigDecimal fuelSaved = totalCarbon.divide(
new BigDecimal("2.3"), 2, RoundingMode.HALF_UP);
impact.setFuelSaved(fuelSaved);
// 1度电平均产生约0.5kg CO2(因地区而异)
BigDecimal electricitySaved = totalCarbon.divide(
new BigDecimal("0.5"), 2, RoundingMode.HALF_UP);
impact.setElectricitySaved(electricitySaved);
return impact;
}
@Override
@Transactional(rollbackFor = Exception.class)
public JDWACarbonReport generateCarbonReport(Long userId, String reportType, String reportDate) {
// 检查是否已存在相同报告
LocalDate date = StringUtils.hasText(reportDate) ?
LocalDate.parse(reportDate) : LocalDate.now();
LambdaQueryWrapper<JDWACarbonReport> checkWrapper = new LambdaQueryWrapper<>();
checkWrapper.eq(JDWACarbonReport::getUserId, userId)
.eq(JDWACarbonReport::getReportType, reportType)
.eq(JDWACarbonReport::getReportDate, date);
JDWACarbonReport existReport = carbonReportMapper.selectOne(checkWrapper);
if (existReport != null) {
return existReport;
}
// 计算报告日期范围
LocalDate startDate;
LocalDate endDate = date;
switch (reportType.toUpperCase()) {
case "MONTHLY":
startDate = date.withDayOfMonth(1);
break;
case "QUARTERLY":
int quarter = (date.getMonthValue() - 1) / 3;
startDate = date.withMonth(quarter * 3 + 1).withDayOfMonth(1);
break;
case "YEARLY":
startDate = date.withDayOfYear(1);
break;
default:
throw new JDWAServiceException("不支持的报告类型: " + reportType);
}
// 创建报告记录
JDWACarbonReport report = new JDWACarbonReport();
report.setUserId(userId);
report.setReportType(reportType);
report.setReportDate(date);
report.setStartDate(startDate);
report.setEndDate(endDate);
report.setStatus(0); // 生成中
// 统计数据
BigDecimal totalCarbon = carbonRecordMapper.sumCarbonAmountByUserIdAndDateRange(
userId, startDate, endDate.plusDays(1));
report.setTotalCarbon(totalCarbon != null ? totalCarbon : BigDecimal.ZERO);
Integer totalActivities = carbonRecordMapper.countRecordsByUserIdAndDateRange(
userId, startDate, endDate.plusDays(1));
report.setTotalActivities(totalActivities != null ? totalActivities : 0);
// 活动统计数据
Map<String, Object> activityStats = new HashMap<>();
// 按活动类型统计
Map<String, BigDecimal> carbonByType = carbonRecordMapper.sumCarbonAmountByUserIdGroupByType(
userId, startDate, endDate.plusDays(1));
activityStats.put("carbonByType", carbonByType);
// 按日期统计
List<DateCarbonStats> dailyStats = carbonStatsMapper.getDailyCarbonStatsByUserIdAndDateRange(
userId, startDate, endDate.plusDays(1));
activityStats.put("dailyStats", dailyStats);
report.setActivityStats(JSON.toJSONString(activityStats));
// 环境影响数据
EnvImpactData impact = calculateEnvironmentalImpact(userId);
report.setEnvImpact(JSON.toJSONString(impact));
// 生成证书编码
String certCode = generateCertificationCode(userId, reportType, date);
report.setCertificationCode(certCode);
// 更新状态为已生成
report.setStatus(1);
// 保存报告
carbonReportMapper.insert(report);
return report;
}
/**
* 生成证书编码
*/
private String generateCertificationCode(Long userId, String reportType, LocalDate date) {
String dateStr = date.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String typeCode = reportType.substring(0, 1); // M/Q/Y
String randomCode = UUID.randomUUID().toString().substring(0, 6).toUpperCase();
return String.format("CR-%s-%s-%s-%s", typeCode, userId, dateStr, randomCode);
}
/**
* 更新日统计数据
*/
private void updateDailyCarbonStats(Long userId, LocalDate date,
String recordType, BigDecimal carbonAmount) {
// 获取当日统计记录
LambdaQueryWrapper<JDWACarbonStats> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(JDWACarbonStats::getUserId, userId)
.eq(JDWACarbonStats::getStatsType, "DAY")
.eq(JDWACarbonStats::getStatsDate, date)
.eq(recordType != null, JDWACarbonStats::getActivityType, recordType);
JDWACarbonStats stats = carbonStatsMapper.selectOne(queryWrapper);
if (stats == null) {
// 新建统计记录
stats = new JDWACarbonStats();
stats.setUserId(userId);
stats.setStatsType("DAY");
stats.setStatsDate(date);
stats.setRecordCount(1);
stats.setTotalDistance(carbonAmount);
stats.setTotalCarbon(carbonAmount);
stats.setAvgCarbon(carbonAmount);
stats.setCreateTime(LocalDateTime.now());
stats.setUpdateTime(LocalDateTime.now());
} else {
// 更新统计记录
stats.setRecordCount(stats.getRecordCount() + 1);
stats.setTotalDistance(stats.getTotalDistance().add(carbonAmount));
stats.setTotalCarbon(stats.getTotalCarbon().add(carbonAmount));
stats.setAvgCarbon((stats.getTotalCarbon().add(carbonAmount)).divide(
BigDecimal.valueOf(stats.getRecordCount() + 1), 2, RoundingMode.HALF_UP));
stats.setUpdateTime(LocalDateTime.now());
}
carbonStatsMapper.updateById(stats);
}
/**
* 更新所有用户的碳减排统计数据(定时任务)
*/
@Scheduled(cron = "0 5 0 * * ?") // 每天凌晨0:05执行
public void updateAllUserCarbonStats() {
LocalDate yesterday = LocalDate.now().minusDays(1);
log.info("开始更新{}的碳减排统计数据", yesterday);
try {
// 更新日统计数据
updateDailyCarbonStats(yesterday);
// 当天是周一,更新上周统计
if (yesterday.getDayOfWeek() == DayOfWeek.SUNDAY) {
LocalDate weekStart = yesterday.minusDays(6); // 上周一
updateWeeklyCarbonStats(weekStart, yesterday);
}
// 当天是月末,更新当月统计
if (yesterday.getDayOfMonth() == yesterday.lengthOfMonth()) {
LocalDate monthStart = yesterday.withDayOfMonth(1);
updateMonthlyCarbonStats(monthStart, yesterday);
}
// 当天是年末,更新当年统计
if (yesterday.getDayOfYear() == yesterday.lengthOfYear()) {
LocalDate yearStart = yesterday.withDayOfYear(1);
updateYearlyCarbonStats(yearStart, yesterday);
}
log.info("{}的碳减排统计数据更新完成", yesterday);
} catch (Exception e) {
log.error("更新碳减排统计数据失败: {}", e.getMessage(), e);
}
}
}
API接口规范
获取碳减排记录列表
接口URL:
/api/carbon/records
请求方式: GET
接口说明: 获取用户碳减排记录列表
请求参数:
- recordType: 记录类型(可选)
- startDate: 开始日期(可选,格式:YYYY-MM-DD)
- endDate: 结束日期(可选,格式:YYYY-MM-DD)
- pageNum: 页码(默认1)
- pageSize: 每页大小(默认10)
响应结果:
{
"code": 200,
"message": "获取成功",
"data": {
"records": [
{
"id": 10001,
"userId": 1001,
"activityId": 2001,
"carbonAmount": 0.44,
"recordType": "WALK",
"description": "步行活动碳减排",
"createTime": "2025-05-18T09:01:05"
},
// 更多记录...
],
"total": 42,
"size": 10,
"current": 1,
"pages": 5
}
}
获取碳减排统计摘要
接口URL:
/api/carbon/stats/summary
请求方式: GET
接口说明: 获取用户碳减排统计摘要数据
请求参数:
- period: 统计周期(week/month/year,可选)
- startDate: 开始日期(可选,格式:YYYY-MM-DD)
- endDate: 结束日期(可选,格式:YYYY-MM-DD)
响应结果:
{
"code": 200,
"message": "获取成功",
"data": {
"userId": 1001,
"totalCarbon": 18.7,
"dailyAvgCarbon": 0.62,
"weeklyAvgCarbon": 4.35,
"monthlyAvgCarbon": 18.7,
"totalActivities": 42,
"carbonByActivityType": {
"WALK": 10.2,
"BIKE": 5.6,
"BUS": 1.8,
"SUBWAY": 1.1
},
"dailyStats": [
{
"date": "2025-05-15",
"carbonAmount": 1.2,
"activityCount": 3
},
// 更多日期...
],
"weeklyStats": [
{
"date": "2025-05-12",
"carbonAmount": 4.5,
"activityCount": 12
},
// 更多周数据...
],
"monthlyStats": [
{
"date": "2025-05-01",
"carbonAmount": 18.7,
"activityCount": 42
},
// 更多月数据...
]
}
}
获取环境影响数据
接口URL:
/api/carbon/impact
请求方式: GET
接口说明: 获取用户碳减排的环境影响数据
请求参数: 无
响应结果:
{
"code": 200,
"message": "获取成功",
"data": {
"carbonAmount": 18.7,
"treeEquivalent": 0.75,
"fuelSaved": 8.13,
"electricitySaved": 37.4
}
}
生成碳减排报告
- 接口URL:
/api/carbon/report/generate
- 请求方式: POST
- 接口说明: 生成用户碳减排报告
- 请求参数:
{
"reportType": "MONTHLY",
"reportDate": "2025-05-18"
}
- 响应结果:
{
"code": 200,
"message": "报告生成成功",
"data": {
"id": 5001,
"userId": 1001,
"reportType": "MONTHLY",
"reportDate": "2025-05-18",
"startDate": "2025-05-01",
"endDate": "2025-05-18",
"totalCarbon": 18.7,
"totalActivities": 42,
"certificationCode": "CR-M-1001-20250518-A1B2C3",
"status": 1,
"createTime": "2025-05-18T10:30:45"
}
}
获取碳减排报告列表
接口URL:
/api/carbon/reports
请求方式: GET
接口说明: 获取用户碳减排报告列表
请求参数:
- reportType: 报告类型(可选)
- pageNum: 页码(默认1)
- pageSize: 每页大小(默认10)
响应结果:
{
"code": 200,
"message": "获取成功",
"data": {
"records": [
{
"id": 5001,
"userId": 1001,
"reportType": "MONTHLY",
"reportDate": "2025-05-18",
"totalCarbon": 18.7,
"totalActivities": 42,
"certificationCode": "CR-M-1001-20250518-A1B2C3",
"status": 1,
"createTime": "2025-05-18T10:30:45"
},
// 更多记录...
],
"total": 6,
"size": 10,
"current": 1,
"pages": 1
}
}
获取碳减排报告详情
接口URL:
/api/carbon/report/{id}
请求方式: GET
接口说明: 获取特定碳减排报告的详细信息
请求参数:
- id: 报告ID
响应结果:
{
"code": 200,
"message": "获取成功",
"data": {
"id": 5001,
"userId": 1001,
"reportType": "MONTHLY",
"reportDate": "2025-05-18",
"startDate": "2025-05-01",
"endDate": "2025-05-18",
"totalCarbon": 18.7,
"totalActivities": 42,
"activityStats": "{\"carbonByType\":{\"WALK\":10.2,\"BIKE\":5.6,\"BUS\":1.8,\"SUBWAY\":1.1},\"dailyStats\":[{\"date\":\"2025-05-15\",\"carbonAmount\":1.2,\"activityCount\":3}]}",
"envImpact": "{\"carbonAmount\":18.7,\"treeEquivalent\":0.75,\"fuelSaved\":8.13,\"electricitySaved\":37.4}",
"certificationCode": "CR-M-1001-20250518-A1B2C3",
"status": 1,
"createTime": "2025-05-18T10:30:45",
"updateTime": "2025-05-18T10:30:45"
}
}
统计计算方法详解
碳减排量计算基础
碳减排量的计算是基于用户在活动模块记录的绿色出行活动数据。系统根据不同活动类型的碳减排系数,计算每次活动的碳减排量:
- 步行: 0.22kg CO₂/km
- 骑行: 0.15kg CO₂/km
- 公交: 0.05kg CO₂/km
- 地铁: 0.03kg CO₂/km
这些系数基于平均汽车碳排放量减去相应绿色出行方式的碳排放量计算得出。
统计周期划分
系统支持多个统计周期维度的数据汇总:
- 日统计: 每日零点自动汇总前一日数据
- 周统计: 以自然周为单位(周一至周日)
- 月统计: 以自然月为单位(1号至月末)
- 年统计: 以自然年为单位(1月1日至12月31日)
多维度统计方法
系统提供多个维度的统计分析:
- 时间维度: 日/周/月/年,可视化活动趋势
- 活动类型维度: 不同绿色出行方式的对比
- 用户维度: 个人历史数据对比,成长曲线
- 群体维度: 与平均水平、同龄群体对比
数据聚合策略
- 实时统计: 活动提交后实时更新当日碳减排记录
- 定时聚合: 每日凌晨执行定时任务,更新前一日汇总数据
- 周期聚合: 每周一、每月1日、每年1月1日执行周期性聚合任务
- 按需计算: 用户查询特定时间范围的统计数据时按需计算
数据统计实现示例
/**
* 更新所有用户的碳减排统计数据(定时任务)
*/
@Scheduled(cron = "0 5 0 * * ?") // 每天凌晨0:05执行
public void updateAllUserCarbonStats() {
LocalDate yesterday = LocalDate.now().minusDays(1);
log.info("开始更新{}的碳减排统计数据", yesterday);
try {
// 更新日统计数据
updateDailyCarbonStats(yesterday);
// 当天是周一,更新上周统计
if (yesterday.getDayOfWeek() == DayOfWeek.SUNDAY) {
LocalDate weekStart = yesterday.minusDays(6); // 上周一
updateWeeklyCarbonStats(weekStart, yesterday);
}
// 当天是月末,更新当月统计
if (yesterday.getDayOfMonth() == yesterday.lengthOfMonth()) {
LocalDate monthStart = yesterday.withDayOfMonth(1);
updateMonthlyCarbonStats(monthStart, yesterday);
}
// 当天是年末,更新当年统计
if (yesterday.getDayOfYear() == yesterday.lengthOfYear()) {
LocalDate yearStart = yesterday.withDayOfYear(1);
updateYearlyCarbonStats(yearStart, yesterday);
}
log.info("{}的碳减排统计数据更新完成", yesterday);
} catch (Exception e) {
log.error("更新碳减排统计数据失败: {}", e.getMessage(), e);
}
}
报告生成规则
报告类型
系统支持以下几种类型的碳减排报告:
- 月度报告 (MONTHLY): 每月自动生成,包含当月的碳减排统计
- 季度报告 (QUARTERLY): 每季度自动生成,包含季度碳减排汇总
- 年度报告 (YEARLY): 每年自动生成,全年碳减排总结
报告内容
每份碳减排报告包含以下内容:
- 基本信息: 用户信息、报告期间、生成时间
- 统计摘要: 总碳减排量、活动次数、活动类型分布
- 时间趋势: 时间序列图表,展示碳减排变化趋势
- 环境影响: 碳减排量转化为环保效益(树木、燃油、电力等)
- 成就展示: 报告期内获得的环保成就
- 碳减排证书: 带有认证编码的碳减排证明
证书生成规则
系统为每份报告生成唯一的碳减排证书编码,编码规则如下:
CR-{类型代码}-{用户ID}-{日期}-{随机码}
- 类型代码: M=月度, Q=季度, Y=年度
- 用户ID: 用户唯一标识
- 日期: 格式为YYYYMMDD
- 随机码: 6位随机字母数字组合
例如:CR-M-1001-20250518-A1B2C3
报告生成流程
触发条件:
- 定时触发: 每月1日生成上月报告
- 用户请求: 用户手动请求生成特定时间段报告
数据汇总:
- 查询指定时间段的碳减排记录
- 计算各类统计指标和环境影响
报告生成:
- 创建报告记录
- 生成证书编码
- 保存统计数据
通知用户:
- 系统消息通知
- 可选邮件推送
环境影响评估方法
评估指标
系统将碳减排量转化为以下具体环保指标,使用户更直观地理解自己的环保贡献:
树木当量:
- 计算公式: 碳减排量(kg) ÷ 25
- 说明: 1棵成年树每年约吸收25kg CO₂
燃油节省:
- 计算公式: 碳减排量(kg) ÷ 2.3
- 说明: 1升汽油燃烧约产生2.3kg CO₂
电力节省:
- 计算公式: 碳减排量(kg) ÷ 0.5
- 说明: 1度电平均产生约0.5kg CO₂(因地区而异)
汽车行驶:
- 计算公式: 碳减排量(kg) ÷ 0.12
- 说明: 普通汽车每公里排放约0.12kg CO₂
社会影响评估
除了个人环保贡献,系统也展示用户参与的集体环保影响:
- 平台总减排: 所有用户的碳减排总量
- 排名百分比: 用户在平台内的碳减排排名
- 集体成就: 所有用户共同达成的环保里程碑
数据可视化展示
可视化图表类型
系统提供多种图表类型展示碳减排数据:
- 趋势线图: 展示碳减排量随时间的变化趋势
- 柱状图: 展示不同活动类型的碳减排对比
- 饼图: 展示不同活动类型的碳减排占比
- 仪表盘: 展示目标完成度和排名情况
- 热力图: 展示用户活动的时间分布密度
前端实现技术
- 图表库: 使用ECharts实现各类图表展示
- 数据处理: 前端使用Lodash进行数据预处理
- 动态刷新: 支持图表数据定时刷新和交互式查询
- 响应式设计: 适配不同设备屏幕大小的图表展示
可视化展示示例代码
// 碳减排趋势图表配置
function createCarbonTrendChart(data) {
const dates = data.dailyStats.map(item => item.date);
const values = data.dailyStats.map(item => item.carbonAmount);
const option = {
title: {
text: '碳减排趋势'
},
tooltip: {
trigger: 'axis',
formatter: function(params) {
const date = params[0].name;
const value = params[0].value;
return `${date}: ${value}kg CO₂`;
}
},
xAxis: {
type: 'category',
data: dates,
axisLabel: {
rotate: 45
}
},
yAxis: {
type: 'value',
name: '碳减排量(kg)',
axisLabel: {
formatter: '{value} kg'
}
},
series: [{
name: '碳减排量',
type: 'line',
data: values,
itemStyle: {
color: '#91cc75'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(145, 204, 117, 0.3)' },
{ offset: 1, color: 'rgba(145, 204, 117, 0.1)' }
])
}
}]
};
return option;
}
// 活动类型碳减排对比图
function createActivityTypeChart(data) {
const types = Object.keys(data.carbonByActivityType);
const values = types.map(type => data.carbonByActivityType[type]);
const option = {
title: {
text: '活动类型碳减排对比'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '碳减排量',
type: 'pie',
radius: '50%',
data: types.map((type, index) => {
return {
name: getActivityTypeName(type),
value: values[index]
};
}),
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
return option;
}
// 活动类型名称转换
function getActivityTypeName(type) {
const typeMap = {
'WALK': '步行',
'BIKE': '骑行',
'BUS': '公交',
'SUBWAY': '地铁'
};
return typeMap[type] || type;
}
系统集成
与其他模块的集成
碳减排统计模块与系统其他模块紧密集成:
用户活动模块:
- 活动提交后触发碳减排记录
- 共享活动数据和统计结果
成就系统:
- 提供碳减排数据用于成就检测
- 在报告中展示用户获得的成就
积分系统:
- 碳减排活动产生积分奖励
- 展示碳减排积分获取统计
社交分享模块:
- 支持分享碳减排成果和证书
- 提供好友间碳减排排行榜
外部系统集成
绿色出行平台:
- 对接第三方绿色出行平台数据
- 导入外部活动记录
碳交易平台:
- 将用户碳减排量转化为碳积分
- 对接碳交易市场
环保组织认证:
- 与环保组织合作,提供碳减排认证
- 颁发权威碳减排证书
社交媒体平台:
- 支持一键分享到微信、微博等平台
- 提供社交媒体分享模板
性能优化
数据存储优化
表分区策略:
- 按时间范围对碳减排记录表进行分区
- 按用户ID进行分表,提高查询效率
索引优化:
- 为高频查询字段创建索引
- 针对统计查询场景优化索引结构
-- 为碳减排记录表创建复合索引
CREATE INDEX idx_carbon_record_user_date ON jdwa_carbon_record(user_id, create_time);
CREATE INDEX idx_carbon_record_activity ON jdwa_carbon_record(activity_id, record_type);
-- 为碳减排统计表创建索引
CREATE INDEX idx_carbon_stats_query ON jdwa_carbon_stats(user_id, stats_type, stats_date);
查询性能优化
预聚合策略:
- 定时计算并存储各种维度的聚合数据
- 用户查询时直接获取聚合结果,避免实时计算
缓存机制:
- 使用Redis缓存热门查询结果
- 为用户个人统计数据设置缓存
@Cacheable(value = "carbonStats", key = "#userId + '-' + #period", unless = "#result == null")
public CarbonStatsSummary getCarbonStatsSummary(Long userId, String period,
String startDate, String endDate) {
// 实现逻辑...
}
- 并行计算:
- 利用多线程并行处理大量数据统计
- 按用户分组进行并行计算
大数据量处理
批处理策略:
- 定时任务分批次处理大量数据
- 使用分页查询避免一次加载过多数据
数据归档:
- 定期归档历史数据到冷存储
- 保持活跃数据量在高效范围
异步处理:
- 报告生成等耗时操作异步处理
- 使用消息队列削峰填谷
常见问题解决
统计数据不准确
问题描述:用户反馈碳减排统计数据与预期不符。
可能原因:
- 活动数据重复计算
- 统计任务执行失败
- 缓存数据未更新
- 系数配置不准确
解决方案:
- 检查活动记录是否有重复
- 验证统计任务执行日志
- 清除并重建缓存数据
- 检查碳减排计算系数配置
- 必要时手动触发统计任务重新计算
报告生成失败
问题描述:用户请求生成报告但系统返回失败。
可能原因:
- 指定时间段无活动数据
- 报告生成任务超时
- 报告模板配置错误
- 存储空间不足
解决方案:
- 检查用户在该时间段是否有活动数据
- 增加报告生成任务超时时间
- 验证报告模板配置
- 检查存储空间状态
- 查看详细错误日志定位具体问题
数据查询响应慢
问题描述:查询碳减排统计数据时响应时间过长。
可能原因:
- 查询范围过大
- 索引未充分利用
- 缓存未生效
- 数据库负载高
解决方案:
- 限制查询时间范围
- 优化SQL语句,确保利用索引
- 检查缓存配置是否正确
- 引入预聚合数据减轻实时计算
- 考虑数据库读写分离
未来优化计划
短期优化
算法优化:
- 引入更精确的碳减排计算模型
- 根据地理位置调整计算系数
展示增强:
- 增加更多可视化图表类型
- 提供个性化数据展示选项
报告丰富:
- 增加更多环境影响指标
- 提供报告导出为PDF功能
长期规划
AI预测分析:
- 引入机器学习模型预测碳减排趋势
- 提供个性化碳减排建议
区块链认证:
- 利用区块链技术为碳减排证书提供不可篡改的认证
- 实现碳积分的可信交易
生态系统扩展:
- 接入更多绿色生活场景(如垃圾分类、节电节水)
- 打造完整的个人碳账户体系
国际标准对接:
- 对接国际碳减排标准
- 提供符合国际认证的碳减排报告
参考资源
总结
碳减排统计模块是JDWA Green-U系统的重要组成部分,通过精确的碳减排计算、多维度的统计分析和直观的数据可视化,为用户提供了量化环保成果的平台。该模块不仅记录用户的绿色行为,还通过各种环境影响指标增强用户的环保意识,鼓励持续的环保行动。
通过与其他模块的紧密集成,碳减排统计数据成为了成就系统、积分奖励和社交分享的重要基础。未来,该模块将继续优化计算模型、丰富展示方式,并引入更多创新功能,为绿色低碳生活方式提供更有力的数据支持。