项目介绍
随着环保意识的提升,垃圾分类已成为现代校园管理的重要议题。本项目旨在通过Java全栈技术,构建一个智能化的校园垃圾分类管理系统,助力校园环保事业发展。
技术栈
后端技术
- Spring Boot 2.7.0:快速构建基于Spring的应用程序
- Spring Security:实现身份认证和权限控制
- MyBatis Plus:简化数据库操作
- MySQL 8.0:数据持久化存储
- Redis:缓存处理和会话管理
- JWT:实现无状态的用户认证
前端技术
- Vue 3:构建用户界面
- Element Plus:UI组件库
- Axios:处理HTTP请求
- Echarts:数据可视化展示
- Vite:前端构建工具
核心功能模块
1. 用户管理模块
- 学生、教师、管理员多角色管理
- 基于JWT的登录认证
- 细粒度的权限控制
- 个人信息管理
2. 垃圾分类指导模块
- 垃圾分类知识库
- 智能识别算法支持
- 分类指导交互
- 常见问题解答
3. 垃圾投放管理模块
- 智能垃圾箱实时监控
- 垃圾箱容量预警
- 投放记录追踪
- 分类准确度统计
4. 积分奖励模块
- 正确投放积分奖励
- 积分兑换商城
- 排行榜系统
- 成就体系
5. 数据分析模块
- 垃圾分类数据统计
- 投放行为分析
- 效果评估报告
- 趋势预测
系统架构
整体架构
采用前后端分离的微服务架构,主要分为以下几层:
- 表现层:处理用户交互和数据展示
- 业务层:实现核心业务逻辑
- 持久层:负责数据存储和访问
- 基础设施层:提供技术支持和服务
数据库设计
基于业务需求,设计了如下核心表结构:
- 用户表(user)
- 垃圾分类表(waste_category)
- 投放记录表(disposal_record)
- 积分记录表(point_record)
- 知识库表(knowledge_base)
项目亮点
-
智能识别:集成深度学习算法,实现垃圾图像的自动分类识别
-
实时监控:通过IoT技术实现垃圾箱状态的实时监控和预警
-
数据可视化:运用Echarts实现直观的数据展示和分析
-
游戏化设计:引入积分、排行榜等游戏化元素,提高用户参与度
-
微服务架构:采用微服务设计,提高系统可扩展性和维护性
项目收获
通过本项目的开发,不仅提升了全栈开发能力,也深入理解了以下几点:
- 前后端分离架构的设计与实现
- 微服务的划分与治理
- 分布式系统的技术难点
- 大数据分析与可视化
- 项目开发完整流程
未来展望
- 引入更多AI技术,提升识别准确率
- 扩展到更多场景,如社区、企业等
- 优化系统性能,提升用户体验
- 增加更多互动功能,提高参与度
- 完善数据分析,为决策提供支持
本项目不仅是一个技术实践,更是对环保理念的实际应用。通过技术手段促进垃圾分类习惯的养成,为建设绿色校园贡献力量。
源代码
Directory Content Summary
Directory Structure
src/
backend/
pom.xml
src/
main/
java/
com/
campus/
waste/
WasteApplication.java
common/
R.java
config/
SecurityConfig.java
controller/
AchievementController.java
AuthController.java
DataAnalysisController.java
DisposalManagementController.java
KnowledgeController.java
PointsController.java
UserController.java
WasteClassificationController.java
entity/
Achievement.java
BinAlert.java
ClassificationStats.java
DisposalBehavior.java
DisposalRecord.java
EffectivenessReport.java
FAQ.java
KnowledgeArticle.java
PointsExchange.java
PointsProduct.java
PointsRecord.java
SmartBin.java
TrendPrediction.java
User.java
UserAchievement.java
UserPoints.java
WasteCategory.java
WasteItem.java
WasteStatistics.java
model/
dto/
ImageRecognitionDTO.java
LoginDTO.java
UserDTO.java
vo/
BinAlertVO.java
BinMonitoringVO.java
BinVolumeStatVO.java
ClassificationStatsVO.java
DisposalRecordVO.java
FAQVO.java
KnowledgeArticleVO.java
LoginVO.java
SmartBinDetailVO.java
SmartBinVO.java
UserVO.java
WasteCategoryVO.java
WasteItemVO.java
security/
JwtAuthenticationFilter.java
JwtTokenProvider.java
service/
AchievementService.java
DataAnalysisService.java
DisposalManagementService.java
KnowledgeService.java
PointsService.java
UserService.java
WasteClassificationService.java
impl/
AchievementServiceImpl.java
DataAnalysisServiceImpl.java
DisposalManagementServiceImpl.java
KnowledgeServiceImpl.java
PointsServiceImpl.java
UserServiceImpl.java
WasteClassificationServiceImpl.java
resources/
application.yml
db/
data_analysis.sql
points_reward.sql
schema.sql
waste_classification.sql
waste_disposal.sql
target/
classes/
application.yml
com/
campus/
waste/
common/
config/
controller/
entity/
model/
dto/
vo/
security/
service/
impl/
db/
data_analysis.sql
points_reward.sql
schema.sql
waste_classification.sql
waste_disposal.sql
generated-sources/
annotations/
generated-test-sources/
test-annotations/
test-classes/
frontend/
src/
api/
achievement.js
analysis.js
disposal.js
points.js
views/
analysis/
Dashboard.vue
disposal/
BinMonitoring.vue
ClassificationStats.vue
DisposalRecords.vue
points/
Achievement.vue
PointsMall.vue
waste/
WasteClassification.vue
File Contents
backend\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.campus</groupId>
<artifactId>waste-classification</artifactId>
<version>1.0.0</version>
<name>campus-waste-classification</name>
<description>Campus Waste Classification System</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<properties>
<java.version>1.8</java.version>
<mybatis-plus.version>3.5.2</mybatis-plus.version>
<jjwt.version>0.9.1</jjwt.version>
<druid.version>1.2.8</druid.version>
<hutool.version>5.8.0</hutool.version>
</properties>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!-- Hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
backend\src\main\java\com\campus\waste\WasteApplication.java
package com.campus.waste;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement
public class WasteApplication {
public static void main(String[] args) {
SpringApplication.run(WasteApplication.class, args);
}
}
backend\src\main\java\com\campus\waste\common\R.java
package com.campus.waste.common;
import lombok.Data;
@Data
public class R<T> {
private Integer code;
private String message;
private T data;
private R(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> R<T> ok() {
return new R<>(200, "success", null);
}
public static <T> R<T> ok(T data) {
return new R<>(200, "success", data);
}
public static <T> R<T> error(String message) {
return new R<>(500, message, null);
}
public static <T> R<T> error(Integer code, String message) {
return new R<>(code, message, null);
}
}
backend\src\main\java\com\campus\waste\config\SecurityConfig.java
package com.campus.waste.config;
import com.campus.waste.security.JwtAuthenticationFilter;
import com.campus.waste.security.JwtAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfig(JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.antMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
backend\src\main\java\com\campus\waste\controller\AchievementController.java
package com.campus.waste.controller;
import com.campus.waste.common.R;
import com.campus.waste.model.vo.*;
import com.campus.waste.service.AchievementService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/achievement")
public class AchievementController {
private final AchievementService achievementService;
public AchievementController(AchievementService achievementService) {
this.achievementService = achievementService;
}
@GetMapping("/list")
public R<List<AchievementVO>> getAllAchievements() {
return R.ok(achievementService.getAllAchievements());
}
@GetMapping("/user")
public R<List<UserAchievementVO>> getUserAchievements() {
Long userId = getCurrentUserId();
return R.ok(achievementService.getUserAchievements(userId));
}
@GetMapping("/recent")
public R<List<UserAchievementVO>> getRecentCompletedAchievements(
@RequestParam(defaultValue = "5") Integer limit) {
Long userId = getCurrentUserId();
return R.ok(achievementService.getRecentCompletedAchievements(userId, limit));
}
@GetMapping("/stats")
public R<AchievementStatsVO> getUserAchievementStats() {
Long userId = getCurrentUserId();
return R.ok(achievementService.getUserAchievementStats(userId));
}
// TODO: 从SecurityContext中获取当前用户ID
private Long getCurrentUserId() {
return 1L; // 临时返回测试用户ID
}
}
backend\src\main\java\com\campus\waste\controller\AuthController.java
package com.campus.waste.controller;
import com.campus.waste.common.R;
import com.campus.waste.model.dto.LoginDTO;
import com.campus.waste.model.vo.LoginVO;
import com.campus.waste.security.JwtTokenProvider;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider tokenProvider;
public AuthController(AuthenticationManager authenticationManager, JwtTokenProvider tokenProvider) {
this.authenticationManager = authenticationManager;
this.tokenProvider = tokenProvider;
}
@PostMapping("/login")
public R<LoginVO> login(@Valid @RequestBody LoginDTO loginDTO) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginDTO.getUsername(),
loginDTO.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.generateToken(authentication);
LoginVO loginVO = new LoginVO();
loginVO.setToken(jwt);
loginVO.setTokenType("Bearer");
return R.ok(loginVO);
}
}
backend\src\main\java\com\campus\waste\controller\DataAnalysisController.java
package com.campus.waste.controller;
import com.campus.waste.common.R;
import com.campus.waste.model.vo.*;
import com.campus.waste.service.DataAnalysisService;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/analysis")
@PreAuthorize("hasRole('ADMIN')")
public class DataAnalysisController {
private final DataAnalysisService dataAnalysisService;
public DataAnalysisController(DataAnalysisService dataAnalysisService) {
this.dataAnalysisService = dataAnalysisService;
}
@GetMapping("/statistics")
public R<List<WasteStatisticsVO>> getWasteStatistics(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate,
@RequestParam(required = false) String wasteType) {
return R.ok(dataAnalysisService.getWasteStatistics(startDate, endDate, wasteType));
}
@GetMapping("/behavior")
public R<DisposalBehaviorAnalysisVO> getDisposalBehaviorAnalysis(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
return R.ok(dataAnalysisService.getDisposalBehaviorAnalysis(startDate, endDate));
}
@GetMapping("/behavior/user/{userId}")
public R<List<DisposalBehaviorVO>> getUserDisposalBehaviors(
@PathVariable Long userId,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
return R.ok(dataAnalysisService.getUserDisposalBehaviors(userId, startDate, endDate));
}
@GetMapping("/report")
public R<EffectivenessReportVO> getEffectivenessReport(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate reportDate) {
return R.ok(dataAnalysisService.getEffectivenessReport(reportDate));
}
@PostMapping("/report/generate")
public R<Void> generateEffectivenessReport(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate reportDate) {
dataAnalysisService.generateEffectivenessReport(reportDate);
return R.ok();
}
@GetMapping("/prediction")
public R<List<TrendPredictionVO>> getTrendPrediction(
@RequestParam String wasteType,
@RequestParam(defaultValue = "3") Integer predictionMonths) {
return R.ok(dataAnalysisService.getTrendPrediction(wasteType, predictionMonths));
}
@PostMapping("/prediction/update")
public R<Void> updateTrendPrediction(@RequestParam String wasteType) {
dataAnalysisService.updateTrendPrediction(wasteType);
return R.ok();
}
@GetMapping("/overview")
public R<Map<String, Object>> getDataAnalysisOverview() {
return R.ok(dataAnalysisService.getDataAnalysisOverview());
}
@GetMapping("/time")
public R<List<TimeAnalysisVO>> getTimeAnalysis(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
return R.ok(dataAnalysisService.getTimeAnalysis(startDate, endDate));
}
@GetMapping("/region")
public R<List<RegionAnalysisVO>> getRegionAnalysis(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
return R.ok(dataAnalysisService.getRegionAnalysis(startDate, endDate));
}
@GetMapping("/export")
public ResponseEntity<byte[]> exportAnalysisReport(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
byte[] report = dataAnalysisService.exportAnalysisReport(startDate, endDate);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment",
String.format("analysis_report_%s_%s.xlsx", startDate, endDate));
return ResponseEntity.ok()
.headers(headers)
.body(report);
}
}
backend\src\main\java\com\campus\waste\controller\DisposalManagementController.java
package com.campus.waste.controller;
import com.campus.waste.common.R;
import com.campus.waste.entity.DisposalRecord;
import com.campus.waste.model.vo.*;
import com.campus.waste.service.DisposalManagementService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/disposal")
public class DisposalManagementController {
private final DisposalManagementService disposalManagementService;
public DisposalManagementController(DisposalManagementService disposalManagementService) {
this.disposalManagementService = disposalManagementService;
}
@GetMapping("/bins")
public R<List<SmartBinVO>> getAllBins() {
return R.ok(disposalManagementService.getAllBins());
}
@GetMapping("/bin/{binId}")
public R<SmartBinDetailVO> getBinDetail(@PathVariable Long binId) {
return R.ok(disposalManagementService.getBinDetail(binId));
}
@PutMapping("/bin/{binId}/status/{status}")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> updateBinStatus(@PathVariable Long binId, @PathVariable Integer status) {
disposalManagementService.updateBinStatus(binId, status);
return R.ok();
}
@PostMapping("/record")
public R<Void> recordDisposal(@RequestBody DisposalRecord record) {
disposalManagementService.recordDisposal(record);
return R.ok();
}
@GetMapping("/records")
public R<IPage<DisposalRecordVO>> getDisposalRecords(
@RequestParam(required = false) Long binId,
@RequestParam(required = false) Long userId,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
return R.ok(disposalManagementService.getDisposalRecords(binId, userId, pageNum, pageSize));
}
@GetMapping("/alerts")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<List<BinAlertVO>> getUnprocessedAlerts() {
return R.ok(disposalManagementService.getUnprocessedAlerts());
}
@PostMapping("/alert/{alertId}/process")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<Void> processAlert(
@PathVariable Long alertId,
@RequestParam String processor,
@RequestParam String processResult) {
disposalManagementService.processAlert(alertId, processor, processResult);
return R.ok();
}
@GetMapping("/stats")
public R<List<ClassificationStatsVO>> getClassificationStats(
@RequestParam(required = false) Long userId,
@RequestParam(defaultValue = "week") String timeRange) {
return R.ok(disposalManagementService.getClassificationStats(userId, timeRange));
}
@GetMapping("/monitoring")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<List<BinMonitoringVO>> getBinMonitoringData() {
return R.ok(disposalManagementService.getBinMonitoringData());
}
@PostMapping("/bin/{binId}/clean")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<Void> cleanBin(@PathVariable Long binId) {
disposalManagementService.cleanBin(binId);
return R.ok();
}
@GetMapping("/bin/{binId}/volume-trend")
public R<List<BinVolumeStatVO>> getBinVolumeTrend(
@PathVariable Long binId,
@RequestParam(defaultValue = "day") String timeRange) {
return R.ok(disposalManagementService.getBinVolumeTrend(binId, timeRange));
}
}
backend\src\main\java\com\campus\waste\controller\KnowledgeController.java
package com.campus.waste.controller;
import com.campus.waste.common.R;
import com.campus.waste.entity.KnowledgeArticle;
import com.campus.waste.entity.FAQ;
import com.campus.waste.model.vo.KnowledgeArticleVO;
import com.campus.waste.model.vo.FAQVO;
import com.campus.waste.service.KnowledgeService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/knowledge")
public class KnowledgeController {
private final KnowledgeService knowledgeService;
public KnowledgeController(KnowledgeService knowledgeService) {
this.knowledgeService = knowledgeService;
}
@GetMapping("/articles")
public R<IPage<KnowledgeArticleVO>> getArticleList(
@RequestParam(required = false) Long categoryId,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
return R.ok(knowledgeService.getArticleList(categoryId, pageNum, pageSize));
}
@GetMapping("/article/{articleId}")
public R<KnowledgeArticleVO> getArticleDetail(@PathVariable Long articleId) {
// 增加浏览次数
knowledgeService.incrementArticleViewCount(articleId);
return R.ok(knowledgeService.getArticleDetail(articleId));
}
@PostMapping("/article")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<Void> addArticle(@RequestBody KnowledgeArticle article) {
knowledgeService.addArticle(article);
return R.ok();
}
@PutMapping("/article")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<Void> updateArticle(@RequestBody KnowledgeArticle article) {
knowledgeService.updateArticle(article);
return R.ok();
}
@DeleteMapping("/article/{articleId}")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<Void> deleteArticle(@PathVariable Long articleId) {
knowledgeService.deleteArticle(articleId);
return R.ok();
}
@GetMapping("/faqs")
public R<List<FAQVO>> getFAQList(@RequestParam(required = false) Long categoryId) {
return R.ok(knowledgeService.getFAQList(categoryId));
}
@PostMapping("/faq")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<Void> addFAQ(@RequestBody FAQ faq) {
knowledgeService.addFAQ(faq);
return R.ok();
}
@PutMapping("/faq")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<Void> updateFAQ(@RequestBody FAQ faq) {
knowledgeService.updateFAQ(faq);
return R.ok();
}
@DeleteMapping("/faq/{faqId}")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER')")
public R<Void> deleteFAQ(@PathVariable Long faqId) {
knowledgeService.deleteFAQ(faqId);
return R.ok();
}
@PostMapping("/faq/{faqId}/view")
public R<Void> incrementFAQViewCount(@PathVariable Long faqId) {
knowledgeService.incrementFAQViewCount(faqId);
return R.ok();
}
}
backend\src\main\java\com\campus\waste\controller\PointsController.java
package com.campus.waste.controller;
import com.campus.waste.common.R;
import com.campus.waste.model.vo.*;
import com.campus.waste.service.PointsService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/points")
public class PointsController {
private final PointsService pointsService;
public PointsController(PointsService pointsService) {
this.pointsService = pointsService;
}
@GetMapping("/info")
public R<UserPointsVO> getUserPoints() {
Long userId = getCurrentUserId();
return R.ok(pointsService.getUserPoints(userId));
}
@GetMapping("/records")
public R<IPage<PointsRecordVO>> getPointsRecords(
@RequestParam(required = false) Integer type,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
Long userId = getCurrentUserId();
return R.ok(pointsService.getPointsRecords(userId, type, pageNum, pageSize));
}
@GetMapping("/products")
public R<List<PointsProductVO>> getProductList(
@RequestParam(defaultValue = "false") Boolean includeOffShelf) {
return R.ok(pointsService.getProductList(includeOffShelf));
}
@PostMapping("/exchange")
public R<Void> exchangeProduct(@RequestBody PointsExchangeDTO dto) {
Long userId = getCurrentUserId();
pointsService.exchangeProduct(
userId,
dto.getProductId(),
dto.getQuantity(),
dto.getReceiverName(),
dto.getReceiverPhone(),
dto.getReceiverAddress()
);
return R.ok();
}
@GetMapping("/exchanges")
public R<IPage<PointsExchangeVO>> getExchangeRecords(
@RequestParam(required = false) Integer status,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
Long userId = getCurrentUserId();
return R.ok(pointsService.getExchangeRecords(userId, status, pageNum, pageSize));
}
@GetMapping("/ranking")
public R<List<PointsRankingVO>> getPointsRanking(
@RequestParam(defaultValue = "week") String timeRange) {
return R.ok(pointsService.getPointsRanking(timeRange));
}
@PutMapping("/exchange/{exchangeId}/status/{status}")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> updateExchangeStatus(
@PathVariable Long exchangeId,
@PathVariable Integer status) {
pointsService.updateExchangeStatus(exchangeId, status);
return R.ok();
}
// TODO: 从SecurityContext中获取当前用户ID
private Long getCurrentUserId() {
return 1L; // 临时返回测试用户ID
}
}
backend\src\main\java\com\campus\waste\controller\UserController.java
package com.campus.waste.controller;
import com.campus.waste.common.R;
import com.campus.waste.model.dto.UserDTO;
import com.campus.waste.model.vo.UserVO;
import com.campus.waste.service.UserService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/register")
public R<Void> register(@Valid @RequestBody UserDTO userDTO) {
userService.register(userDTO);
return R.ok();
}
@PutMapping
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER', 'STUDENT')")
public R<Void> updateUserInfo(@Valid @RequestBody UserDTO userDTO) {
userService.updateUserInfo(userDTO);
return R.ok();
}
@GetMapping("/{userId}")
@PreAuthorize("hasAnyRole('ADMIN', 'TEACHER', 'STUDENT')")
public R<UserVO> getUserInfo(@PathVariable Long userId) {
return R.ok(userService.getUserInfo(userId));
}
@GetMapping("/list")
@PreAuthorize("hasRole('ADMIN')")
public R<List<UserVO>> getUserList(@RequestParam(required = false) Integer userType) {
return R.ok(userService.getUserList(userType));
}
@PutMapping("/{userId}/status/{status}")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> updateUserStatus(@PathVariable Long userId, @PathVariable Integer status) {
userService.updateUserStatus(userId, status);
return R.ok();
}
@DeleteMapping("/{userId}")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> deleteUser(@PathVariable Long userId) {
userService.deleteUser(userId);
return R.ok();
}
}
backend\src\main\java\com\campus\waste\controller\WasteClassificationController.java
package com.campus.waste.controller;
import com.campus.waste.common.R;
import com.campus.waste.entity.WasteCategory;
import com.campus.waste.entity.WasteItem;
import com.campus.waste.model.dto.ImageRecognitionDTO;
import com.campus.waste.model.vo.WasteCategoryVO;
import com.campus.waste.model.vo.WasteItemVO;
import com.campus.waste.service.WasteClassificationService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@RestController
@RequestMapping("/waste")
public class WasteClassificationController {
private final WasteClassificationService wasteClassificationService;
public WasteClassificationController(WasteClassificationService wasteClassificationService) {
this.wasteClassificationService = wasteClassificationService;
}
@GetMapping("/categories")
public R<List<WasteCategoryVO>> getAllCategories() {
return R.ok(wasteClassificationService.getAllCategories());
}
@GetMapping("/items/{categoryId}")
public R<List<WasteItemVO>> getItemsByCategory(@PathVariable Long categoryId) {
return R.ok(wasteClassificationService.getItemsByCategory(categoryId));
}
@GetMapping("/items/search")
public R<List<WasteItemVO>> searchItems(@RequestParam String keyword) {
return R.ok(wasteClassificationService.searchItems(keyword));
}
@PostMapping("/recognize")
public R<ImageRecognitionDTO> recognizeImage(@RequestParam("file") MultipartFile image,
@RequestParam Long userId) {
return R.ok(wasteClassificationService.recognizeImage(image, userId));
}
@PostMapping("/category")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> addCategory(@RequestBody WasteCategory category) {
wasteClassificationService.addCategory(category);
return R.ok();
}
@PostMapping("/item")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> addItem(@RequestBody WasteItem item) {
wasteClassificationService.addItem(item);
return R.ok();
}
@PutMapping("/category")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> updateCategory(@RequestBody WasteCategory category) {
wasteClassificationService.updateCategory(category);
return R.ok();
}
@PutMapping("/item")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> updateItem(@RequestBody WasteItem item) {
wasteClassificationService.updateItem(item);
return R.ok();
}
@DeleteMapping("/category/{categoryId}")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> deleteCategory(@PathVariable Long categoryId) {
wasteClassificationService.deleteCategory(categoryId);
return R.ok();
}
@DeleteMapping("/item/{itemId}")
@PreAuthorize("hasRole('ADMIN')")
public R<Void> deleteItem(@PathVariable Long itemId) {
wasteClassificationService.deleteItem(itemId);
return R.ok();
}
}
backend\src\main\java\com\campus\waste\entity\Achievement.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("achievement")
public class Achievement {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String description;
private String iconUrl;
private Integer type;
private Integer targetValue;
private Integer pointsReward;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
}
backend\src\main\java\com\campus\waste\entity\BinAlert.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("bin_alert")
public class BinAlert {
@TableId(type = IdType.AUTO)
private Long id;
private Long binId;
private Integer alertType;
private String alertValue;
private LocalDateTime alertTime;
private LocalDateTime processedTime;
private String processor;
private String processResult;
private Integer status;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\ClassificationStats.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("classification_stats")
public class ClassificationStats {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Long categoryId;
private Integer totalCount;
private Integer correctCount;
private LocalDate statsDate;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\DisposalBehavior.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("disposal_behavior")
public class DisposalBehavior {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private String wasteType;
private LocalDateTime disposalTime;
private String location;
private BigDecimal weight;
private Boolean isCorrect;
private String errorType;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
}
backend\src\main\java\com\campus\waste\entity\DisposalRecord.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
import java.math.BigDecimal;
@Data
@TableName("disposal_record")
public class DisposalRecord {
@TableId(type = IdType.AUTO)
private Long id;
private Long binId;
private Long userId;
private Long wasteTypeId;
private BigDecimal weight;
private BigDecimal volume;
private Integer isCorrect;
private String correctionNote;
private LocalDateTime disposalTime;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
}
backend\src\main\java\com\campus\waste\entity\EffectivenessReport.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("effectiveness_report")
public class EffectivenessReport {
@TableId(type = IdType.AUTO)
private Long id;
private LocalDate reportDate;
private Integer totalUsers;
private Integer activeUsers;
private BigDecimal totalWeight;
private BigDecimal correctRate;
private BigDecimal reductionRate;
private BigDecimal satisfactionRate;
private String mainProblems;
private String suggestions;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\FAQ.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("faq")
public class FAQ {
@TableId(type = IdType.AUTO)
private Long id;
private String question;
private String answer;
private Long categoryId;
private Integer viewCount;
private Integer sortOrder;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\KnowledgeArticle.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("knowledge_article")
public class KnowledgeArticle {
@TableId(type = IdType.AUTO)
private Long id;
private String title;
private String content;
private Long categoryId;
private String author;
private Integer viewCount;
private Integer status;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\PointsExchange.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("points_exchange")
public class PointsExchange {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Long productId;
private Integer pointsCost;
private Integer quantity;
private Integer status;
private String receiverName;
private String receiverPhone;
private String receiverAddress;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\PointsProduct.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("points_product")
public class PointsProduct {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String description;
private String imageUrl;
private Integer points;
private Integer stock;
private Integer totalStock;
private Integer exchangeLimit;
private Integer status;
private LocalDateTime startTime;
private LocalDateTime endTime;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\PointsRecord.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("points_record")
public class PointsRecord {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Integer points;
private Integer type;
private Long sourceId;
private String sourceType;
private String description;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
}
backend\src\main\java\com\campus\waste\entity\SmartBin.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
import java.math.BigDecimal;
@Data
@TableName("smart_bin")
public class SmartBin {
@TableId(type = IdType.AUTO)
private Long id;
private String binCode;
private String location;
private Long categoryId;
private Integer capacity;
private Integer currentVolume;
private Integer batteryLevel;
private BigDecimal temperature;
private BigDecimal humidity;
private Integer status;
private Integer alertThreshold;
private LocalDateTime lastCleanedTime;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\TrendPrediction.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("trend_prediction")
public class TrendPrediction {
@TableId(type = IdType.AUTO)
private Long id;
private LocalDate predictionDate;
private String wasteType;
private BigDecimal predictedWeight;
private Integer predictedCount;
private BigDecimal confidenceLevel;
private String influencingFactors;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\User.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String realName;
private String email;
private String phone;
private String avatar;
/**
* 用户类型(0管理员,1教师,2学生)
*/
private Integer userType;
/**
* 帐号状态(0正常 1停用)
*/
private Integer status;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer delFlag;
}
backend\src\main\java\com\campus\waste\entity\UserAchievement.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("user_achievement")
public class UserAchievement {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Long achievementId;
private Integer currentValue;
private Integer isCompleted;
private LocalDateTime completeTime;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\UserPoints.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("user_points")
public class UserPoints {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Integer totalPoints;
private Integer availablePoints;
private Integer level;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\WasteCategory.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("waste_category")
public class WasteCategory {
@TableId(type = IdType.AUTO)
private Long id;
private String categoryName;
private String categoryCode;
private String description;
private String disposalGuide;
private String categoryIcon;
private Integer sortOrder;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\WasteItem.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("waste_item")
public class WasteItem {
@TableId(type = IdType.AUTO)
private Long id;
private String itemName;
private Long categoryId;
private String keywords;
private String description;
private String disposalMethod;
private String imageUrl;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\entity\WasteStatistics.java
package com.campus.waste.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("waste_statistics")
public class WasteStatistics {
@TableId(type = IdType.AUTO)
private Long id;
private String wasteType;
private BigDecimal weight;
private Integer count;
private BigDecimal correctRate;
private LocalDate collectionTime;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\model\dto\ImageRecognitionDTO.java
package com.campus.waste.model.dto;
import lombok.Data;
@Data
public class ImageRecognitionDTO {
private Long wasteItemId;
private String itemName;
private String categoryName;
private String disposalMethod;
private Double confidence;
private String imageUrl;
}
backend\src\main\java\com\campus\waste\model\dto\LoginDTO.java
package com.campus.waste.model.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class LoginDTO {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
}
backend\src\main\java\com\campus\waste\model\dto\UserDTO.java
package com.campus.waste.model.dto;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@Data
public class UserDTO {
private Long id;
@NotBlank(message = "用户名不能为空")
private String username;
private String password;
@NotBlank(message = "真实姓名不能为空")
private String realName;
@Email(message = "邮箱格式不正确")
private String email;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
private String avatar;
private Integer userType;
}
backend\src\main\java\com\campus\waste\model\vo\BinAlertVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class BinAlertVO {
private Long id;
private Long binId;
private String binCode;
private String location;
private Integer alertType;
private String alertValue;
private LocalDateTime alertTime;
private LocalDateTime processedTime;
private String processor;
private String processResult;
private Integer status;
}
backend\src\main\java\com\campus\waste\model\vo\BinMonitoringVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class BinMonitoringVO {
private Long id;
private String binCode;
private String location;
private String categoryName;
private Integer capacity;
private Integer currentVolume;
private Double usageRate;
private Integer batteryLevel;
private BigDecimal temperature;
private BigDecimal humidity;
private Integer status;
}
backend\src\main\java\com\campus\waste\model\vo\BinVolumeStatVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class BinVolumeStatVO {
private LocalDateTime time;
private Integer volume;
private Double usageRate;
}
backend\src\main\java\com\campus\waste\model\vo\ClassificationStatsVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.time.LocalDate;
@Data
public class ClassificationStatsVO {
private Long id;
private Long userId;
private String username;
private Long categoryId;
private String categoryName;
private Integer totalCount;
private Integer correctCount;
private Double accuracy;
private LocalDate statsDate;
}
backend\src\main\java\com\campus\waste\model\vo\DisposalRecordVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class DisposalRecordVO {
private Long id;
private Long binId;
private String binCode;
private Long userId;
private String username;
private Long wasteTypeId;
private String wasteTypeName;
private BigDecimal weight;
private BigDecimal volume;
private Integer isCorrect;
private String correctionNote;
private LocalDateTime disposalTime;
}
backend\src\main\java\com\campus\waste\model\vo\FAQVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class FAQVO {
private Long id;
private String question;
private String answer;
private Long categoryId;
private Integer viewCount;
private Integer sortOrder;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\model\vo\KnowledgeArticleVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class KnowledgeArticleVO {
private Long id;
private String title;
private String content;
private Long categoryId;
private String author;
private Integer viewCount;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\model\vo\LoginVO.java
package com.campus.waste.model.vo;
import lombok.Data;
@Data
public class LoginVO {
private String token;
private String tokenType;
}
backend\src\main\java\com\campus\waste\model\vo\SmartBinDetailVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class SmartBinDetailVO {
private Long id;
private String binCode;
private String location;
private Long categoryId;
private String categoryName;
private Integer capacity;
private Integer currentVolume;
private Double usageRate;
private Integer batteryLevel;
private BigDecimal temperature;
private BigDecimal humidity;
private Integer status;
private Integer alertThreshold;
private LocalDateTime lastCleanedTime;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\model\vo\SmartBinVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class SmartBinVO {
private Long id;
private String binCode;
private String location;
private Long categoryId;
private Integer capacity;
private Integer currentVolume;
private Integer batteryLevel;
private BigDecimal temperature;
private BigDecimal humidity;
private Integer status;
private LocalDateTime lastCleanedTime;
}
backend\src\main\java\com\campus\waste\model\vo\UserVO.java
package com.campus.waste.model.vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UserVO {
private Long id;
private String username;
private String password;
private String realName;
private String email;
private String phone;
private String avatar;
private Integer userType;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
backend\src\main\java\com\campus\waste\model\vo\WasteCategoryVO.java
package com.campus.waste.model.vo;
import lombok.Data;
@Data
public class WasteCategoryVO {
private Long id;
private String categoryName;
private String categoryCode;
private String description;
private String disposalGuide;
private String categoryIcon;
private Integer sortOrder;
}
backend\src\main\java\com\campus\waste\model\vo\WasteItemVO.java
package com.campus.waste.model.vo;
import lombok.Data;
@Data
public class WasteItemVO {
private Long id;
private String itemName;
private Long categoryId;
private String categoryName;
private String keywords;
private String description;
private String disposalMethod;
private String imageUrl;
}
backend\src\main\java\com\campus\waste\security\JwtAuthenticationFilter.java
package com.campus.waste.security;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Value("${jwt.token-start-with}")
private String tokenStartWith;
private final JwtTokenProvider tokenProvider;
private final CustomUserDetailsService customUserDetailsService;
public JwtAuthenticationFilter(JwtTokenProvider tokenProvider, CustomUserDetailsService customUserDetailsService) {
this.tokenProvider = tokenProvider;
this.customUserDetailsService = customUserDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Long userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(tokenStartWith)) {
return bearerToken.substring(tokenStartWith.length());
}
return null;
}
}
backend\src\main\java\com\campus\waste\security\JwtTokenProvider.java
package com.campus.waste.security;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private int jwtExpirationInMs;
public String generateToken(Authentication authentication) {
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs * 1000);
return Jwts.builder()
.setSubject(Long.toString(userPrincipal.getId()))
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public Long getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
// 无效的JWT签名
return false;
} catch (MalformedJwtException ex) {
// 无效的JWT令牌
return false;
} catch (ExpiredJwtException ex) {
// 过期的JWT令牌
return false;
} catch (UnsupportedJwtException ex) {
// 不支持的JWT令牌
return false;
} catch (IllegalArgumentException ex) {
// JWT声明字符串为空
return false;
}
}
}
backend\src\main\java\com\campus\waste\service\AchievementService.java
package com.campus.waste.service;
import com.campus.waste.entity.*;
import com.campus.waste.model.vo.*;
import java.util.List;
public interface AchievementService {
/**
* 获取所有成就列表
*/
List<AchievementVO> getAllAchievements();
/**
* 获取用户成就列表
*/
List<UserAchievementVO> getUserAchievements(Long userId);
/**
* 检查并更新用户成就
*/
void checkAndUpdateAchievements(Long userId, Integer type, Integer value);
/**
* 获取用户最近完成的成就
*/
List<UserAchievementVO> getRecentCompletedAchievements(Long userId, Integer limit);
/**
* 获取用户成就统计
*/
AchievementStatsVO getUserAchievementStats(Long userId);
}
backend\src\main\java\com\campus\waste\service\DataAnalysisService.java
package com.campus.waste.service;
import com.campus.waste.entity.*;
import com.campus.waste.model.vo.*;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
public interface DataAnalysisService {
/**
* 获取垃圾分类统计数据
*/
List<WasteStatisticsVO> getWasteStatistics(LocalDate startDate, LocalDate endDate, String wasteType);
/**
* 获取投放行为分析数据
*/
DisposalBehaviorAnalysisVO getDisposalBehaviorAnalysis(LocalDate startDate, LocalDate endDate);
/**
* 获取用户投放行为详情
*/
List<DisposalBehaviorVO> getUserDisposalBehaviors(Long userId, LocalDate startDate, LocalDate endDate);
/**
* 获取效果评估报告
*/
EffectivenessReportVO getEffectivenessReport(LocalDate reportDate);
/**
* 生成效果评估报告
*/
void generateEffectivenessReport(LocalDate reportDate);
/**
* 获取趋势预测数据
*/
List<TrendPredictionVO> getTrendPrediction(String wasteType, Integer predictionMonths);
/**
* 更新趋势预测
*/
void updateTrendPrediction(String wasteType);
/**
* 获取数据分析概览
*/
Map<String, Object> getDataAnalysisOverview();
/**
* 获取时段分析数据
*/
List<TimeAnalysisVO> getTimeAnalysis(LocalDate startDate, LocalDate endDate);
/**
* 获取区域分析数据
*/
List<RegionAnalysisVO> getRegionAnalysis(LocalDate startDate, LocalDate endDate);
/**
* 导出分析报告
*/
byte[] exportAnalysisReport(LocalDate startDate, LocalDate endDate);
}
backend\src\main\java\com\campus\waste\service\DisposalManagementService.java
package com.campus.waste.service;
import com.campus.waste.entity.SmartBin;
import com.campus.waste.entity.DisposalRecord;
import com.campus.waste.entity.BinAlert;
import com.campus.waste.model.vo.*;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.util.List;
public interface DisposalManagementService {
/**
* 获取所有垃圾箱列表
*/
List<SmartBinVO> getAllBins();
/**
* 获取垃圾箱详情
*/
SmartBinDetailVO getBinDetail(Long binId);
/**
* 更新垃圾箱状态
*/
void updateBinStatus(Long binId, Integer status);
/**
* 记录垃圾投放
*/
void recordDisposal(DisposalRecord record);
/**
* 获取投放记录
*/
IPage<DisposalRecordVO> getDisposalRecords(Long binId, Long userId, Integer pageNum, Integer pageSize);
/**
* 获取未处理的预警
*/
List<BinAlertVO> getUnprocessedAlerts();
/**
* 处理预警
*/
void processAlert(Long alertId, String processor, String processResult);
/**
* 获取分类准确率统计
*/
List<ClassificationStatsVO> getClassificationStats(Long userId, String timeRange);
/**
* 检查并生成容量预警
*/
void checkAndGenerateVolumeAlerts();
/**
* 获取实时监控数据
*/
List<BinMonitoringVO> getBinMonitoringData();
/**
* 清空垃圾箱
*/
void cleanBin(Long binId);
/**
* 获取垃圾箱容量趋势
*/
List<BinVolumeStatVO> getBinVolumeTrend(Long binId, String timeRange);
}
backend\src\main\java\com\campus\waste\service\KnowledgeService.java
package com.campus.waste.service;
import com.campus.waste.entity.KnowledgeArticle;
import com.campus.waste.entity.FAQ;
import com.campus.waste.model.vo.KnowledgeArticleVO;
import com.campus.waste.model.vo.FAQVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.util.List;
public interface KnowledgeService {
/**
* 获取文章列表
*/
IPage<KnowledgeArticleVO> getArticleList(Long categoryId, Integer pageNum, Integer pageSize);
/**
* 获取文章详情
*/
KnowledgeArticleVO getArticleDetail(Long articleId);
/**
* 添加文章
*/
void addArticle(KnowledgeArticle article);
/**
* 更新文章
*/
void updateArticle(KnowledgeArticle article);
/**
* 删除文章
*/
void deleteArticle(Long articleId);
/**
* 获取FAQ列表
*/
List<FAQVO> getFAQList(Long categoryId);
/**
* 添加FAQ
*/
void addFAQ(FAQ faq);
/**
* 更新FAQ
*/
void updateFAQ(FAQ faq);
/**
* 删除FAQ
*/
void deleteFAQ(Long faqId);
/**
* 增加文章浏览次数
*/
void incrementArticleViewCount(Long articleId);
/**
* 增加FAQ浏览次数
*/
void incrementFAQViewCount(Long faqId);
}
backend\src\main\java\com\campus\waste\service\PointsService.java
package com.campus.waste.service;
import com.campus.waste.entity.*;
import com.campus.waste.model.vo.*;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.util.List;
public interface PointsService {
/**
* 获取用户积分信息
*/
UserPointsVO getUserPoints(Long userId);
/**
* 增加用户积分
*/
void addPoints(Long userId, Integer points, Integer type, Long sourceId, String sourceType, String description);
/**
* 扣减用户积分
*/
boolean deductPoints(Long userId, Integer points, Integer type, Long sourceId, String sourceType, String description);
/**
* 获取用户积分记录
*/
IPage<PointsRecordVO> getPointsRecords(Long userId, Integer type, Integer pageNum, Integer pageSize);
/**
* 获取积分商城商品列表
*/
List<PointsProductVO> getProductList(Boolean includeOffShelf);
/**
* 兑换商品
*/
void exchangeProduct(Long userId, Long productId, Integer quantity, String receiverName, String receiverPhone, String receiverAddress);
/**
* 获取用户兑换记录
*/
IPage<PointsExchangeVO> getExchangeRecords(Long userId, Integer status, Integer pageNum, Integer pageSize);
/**
* 获取积分排行榜
*/
List<PointsRankingVO> getPointsRanking(String timeRange);
/**
* 更新兑换记录状态
*/
void updateExchangeStatus(Long exchangeId, Integer status);
/**
* 检查并更新用户等级
*/
void checkAndUpdateLevel(Long userId);
}
backend\src\main\java\com\campus\waste\service\UserService.java
package com.campus.waste.service;
import com.campus.waste.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.campus.waste.model.dto.UserDTO;
import com.campus.waste.model.vo.UserVO;
import java.util.List;
public interface UserService extends IService<User> {
/**
* 用户注册
*/
void register(UserDTO userDTO);
/**
* 更新用户信息
*/
void updateUserInfo(UserDTO userDTO);
/**
* 获取用户信息
*/
UserVO getUserInfo(Long userId);
/**
* 获取用户列表
*/
List<UserVO> getUserList(Integer userType);
/**
* 修改用户状态
*/
void updateUserStatus(Long userId, Integer status);
/**
* 删除用户
*/
void deleteUser(Long userId);
}
backend\src\main\java\com\campus\waste\service\WasteClassificationService.java
package com.campus.waste.service;
import com.campus.waste.entity.WasteCategory;
import com.campus.waste.entity.WasteItem;
import com.campus.waste.model.dto.ImageRecognitionDTO;
import com.campus.waste.model.vo.WasteCategoryVO;
import com.campus.waste.model.vo.WasteItemVO;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
public interface WasteClassificationService {
/**
* 获取所有垃圾分类
*/
List<WasteCategoryVO> getAllCategories();
/**
* 根据分类ID获取物品列表
*/
List<WasteItemVO> getItemsByCategory(Long categoryId);
/**
* 搜索垃圾物品
*/
List<WasteItemVO> searchItems(String keyword);
/**
* 图像识别
*/
ImageRecognitionDTO recognizeImage(MultipartFile image, Long userId);
/**
* 添加垃圾分类
*/
void addCategory(WasteCategory category);
/**
* 添加垃圾物品
*/
void addItem(WasteItem item);
/**
* 更新垃圾分类
*/
void updateCategory(WasteCategory category);
/**
* 更新垃圾物品
*/
void updateItem(WasteItem item);
/**
* 删除垃圾分类
*/
void deleteCategory(Long categoryId);
/**
* 删除垃圾物品
*/
void deleteItem(Long itemId);
}
backend\src\main\java\com\campus\waste\service\impl\AchievementServiceImpl.java
package com.campus.waste.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.campus.waste.entity.*;
import com.campus.waste.mapper.*;
import com.campus.waste.model.vo.*;
import com.campus.waste.service.AchievementService;
import com.campus.waste.service.PointsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.BeanUtils;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class AchievementServiceImpl implements AchievementService {
private final AchievementMapper achievementMapper;
private final UserAchievementMapper userAchievementMapper;
private final PointsService pointsService;
public AchievementServiceImpl(AchievementMapper achievementMapper,
UserAchievementMapper userAchievementMapper,
PointsService pointsService) {
this.achievementMapper = achievementMapper;
this.userAchievementMapper = userAchievementMapper;
this.pointsService = pointsService;
}
@Override
public List<AchievementVO> getAllAchievements() {
List<Achievement> achievements = achievementMapper.selectList(null);
return achievements.stream()
.map(this::convertToAchievementVO)
.collect(Collectors.toList());
}
@Override
public List<UserAchievementVO> getUserAchievements(Long userId) {
// 获取所有成就
List<Achievement> achievements = achievementMapper.selectList(null);
// 获取用户已完成的成就
List<UserAchievement> userAchievements = userAchievementMapper.selectList(
new LambdaQueryWrapper<UserAchievement>()
.eq(UserAchievement::getUserId, userId)
);
Map<Long, UserAchievement> userAchievementMap = userAchievements.stream()
.collect(Collectors.toMap(UserAchievement::getAchievementId, ua -> ua));
// 合并成就信息
return achievements.stream()
.map(achievement -> {
UserAchievementVO vo = new UserAchievementVO();
BeanUtils.copyProperties(achievement, vo);
UserAchievement userAchievement = userAchievementMap.get(achievement.getId());
if (userAchievement != null) {
vo.setCurrentValue(userAchievement.getCurrentValue());
vo.setIsCompleted(userAchievement.getIsCompleted());
vo.setCompleteTime(userAchievement.getCompleteTime());
} else {
vo.setCurrentValue(0);
vo.setIsCompleted(0);
}
return vo;
})
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void checkAndUpdateAchievements(Long userId, Integer type, Integer value) {
// 获取指定类型的未完成成就
List<Achievement> achievements = achievementMapper.selectList(
new LambdaQueryWrapper<Achievement>()
.eq(Achievement::getType, type)
);
for (Achievement achievement : achievements) {
UserAchievement userAchievement = userAchievementMapper.selectOne(
new LambdaQueryWrapper<UserAchievement>()
.eq(UserAchievement::getUserId, userId)
.eq(UserAchievement::getAchievementId, achievement.getId())
);
if (userAchievement == null) {
// 创建新的用户成就记录
userAchievement = new UserAchievement();
userAchievement.setUserId(userId);
userAchievement.setAchievementId(achievement.getId());
userAchievement.setCurrentValue(value);
userAchievement.setIsCompleted(value >= achievement.getTargetValue() ? 1 : 0);
if (userAchievement.getIsCompleted() == 1) {
userAchievement.setCompleteTime(LocalDateTime.now());
// 发放成就奖励
awardAchievementPoints(userId, achievement);
}
userAchievementMapper.insert(userAchievement);
} else if (userAchievement.getIsCompleted() == 0) {
// 更新进度
userAchievement.setCurrentValue(value);
if (value >= achievement.getTargetValue()) {
userAchievement.setIsCompleted(1);
userAchievement.setCompleteTime(LocalDateTime.now());
// 发放成就奖励
awardAchievementPoints(userId, achievement);
}
userAchievementMapper.updateById(userAchievement);
}
}
}
@Override
public List<UserAchievementVO> getRecentCompletedAchievements(Long userId, Integer limit) {
List<UserAchievement> userAchievements = userAchievementMapper.selectList(
new LambdaQueryWrapper<UserAchievement>()
.eq(UserAchievement::getUserId, userId)
.eq(UserAchievement::getIsCompleted, 1)
.orderByDesc(UserAchievement::getCompleteTime)
.last("LIMIT " + limit)
);
return userAchievements.stream()
.map(ua -> {
Achievement achievement = achievementMapper.selectById(ua.getAchievementId());
UserAchievementVO vo = new UserAchievementVO();
BeanUtils.copyProperties(achievement, vo);
vo.setCurrentValue(ua.getCurrentValue());
vo.setIsCompleted(ua.getIsCompleted());
vo.setCompleteTime(ua.getCompleteTime());
return vo;
})
.collect(Collectors.toList());
}
@Override
public AchievementStatsVO getUserAchievementStats(Long userId) {
AchievementStatsVO stats = new AchievementStatsVO();
// 获取用户成就完成情况
List<UserAchievement> userAchievements = userAchievementMapper.selectList(
new LambdaQueryWrapper<UserAchievement>()
.eq(UserAchievement::getUserId, userId)
);
// 获取所有成就总数
int totalAchievements = achievementMapper.selectCount(null);
// 计算完成数量
long completedCount = userAchievements.stream()
.filter(ua -> ua.getIsCompleted() == 1)
.count();
stats.setTotalCount(totalAchievements);
stats.setCompletedCount((int) completedCount);
stats.setCompletionRate(totalAchievements == 0 ? 0 :
(double) completedCount / totalAchievements * 100);
return stats;
}
private void awardAchievementPoints(Long userId, Achievement achievement) {
if (achievement.getPointsReward() > 0) {
pointsService.addPoints(
userId,
achievement.getPointsReward(),
3,
achievement.getId(),
"achievement_reward",
"完成成就:" + achievement.getName()
);
}
}
private AchievementVO convertToAchievementVO(Achievement achievement) {
AchievementVO vo = new AchievementVO();
BeanUtils.copyProperties(achievement, vo);
return vo;
}
}
backend\src\main\java\com\campus\waste\service\impl\DataAnalysisServiceImpl.java
package com.campus.waste.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.campus.waste.entity.*;
import com.campus.waste.mapper.*;
import com.campus.waste.model.vo.*;
import com.campus.waste.service.DataAnalysisService;
import com.campus.waste.utils.ExcelUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.springframework.beans.BeanUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import java.math.BigDecimal;
import java.math.RoundingMode;
@Service
public class DataAnalysisServiceImpl implements DataAnalysisService {
private final WasteStatisticsMapper wasteStatisticsMapper;
private final DisposalBehaviorMapper disposalBehaviorMapper;
private final EffectivenessReportMapper effectivenessReportMapper;
private final TrendPredictionMapper trendPredictionMapper;
public DataAnalysisServiceImpl(WasteStatisticsMapper wasteStatisticsMapper,
DisposalBehaviorMapper disposalBehaviorMapper,
EffectivenessReportMapper effectivenessReportMapper,
TrendPredictionMapper trendPredictionMapper) {
this.wasteStatisticsMapper = wasteStatisticsMapper;
this.disposalBehaviorMapper = disposalBehaviorMapper;
this.effectivenessReportMapper = effectivenessReportMapper;
this.trendPredictionMapper = trendPredictionMapper;
}
@Override
public List<WasteStatisticsVO> getWasteStatistics(LocalDate startDate, LocalDate endDate, String wasteType) {
LambdaQueryWrapper<WasteStatistics> wrapper = new LambdaQueryWrapper<>();
wrapper.ge(WasteStatistics::getCollectionTime, startDate)
.le(WasteStatistics::getCollectionTime, endDate)
.eq(wasteType != null, WasteStatistics::getWasteType, wasteType)
.orderByAsc(WasteStatistics::getCollectionTime);
List<WasteStatistics> statistics = wasteStatisticsMapper.selectList(wrapper);
return statistics.stream()
.map(this::convertToWasteStatisticsVO)
.collect(Collectors.toList());
}
@Override
public DisposalBehaviorAnalysisVO getDisposalBehaviorAnalysis(LocalDate startDate, LocalDate endDate) {
List<DisposalBehavior> behaviors = disposalBehaviorMapper.selectList(
new LambdaQueryWrapper<DisposalBehavior>()
.ge(DisposalBehavior::getDisposalTime, startDate.atStartOfDay())
.le(DisposalBehavior::getDisposalTime, endDate.atTime(23, 59, 59))
);
DisposalBehaviorAnalysisVO analysis = new DisposalBehaviorAnalysisVO();
// 计算总体正确率
long correctCount = behaviors.stream().filter(DisposalBehavior::getIsCorrect).count();
analysis.setOverallCorrectRate(calculatePercentage(correctCount, behaviors.size()));
// 按垃圾类型分组统计
Map<String, List<DisposalBehavior>> typeGroups = behaviors.stream()
.collect(Collectors.groupingBy(DisposalBehavior::getWasteType));
List<TypeAnalysisVO> typeAnalysis = typeGroups.entrySet().stream()
.map(entry -> {
TypeAnalysisVO vo = new TypeAnalysisVO();
vo.setWasteType(entry.getKey());
vo.setCount(entry.getValue().size());
long typeCorrectCount = entry.getValue().stream()
.filter(DisposalBehavior::getIsCorrect)
.count();
vo.setCorrectRate(calculatePercentage(typeCorrectCount, entry.getValue().size()));
return vo;
})
.collect(Collectors.toList());
analysis.setTypeAnalysis(typeAnalysis);
// 错误类型分析
Map<String, Long> errorTypes = behaviors.stream()
.filter(b -> !b.getIsCorrect())
.collect(Collectors.groupingBy(
DisposalBehavior::getErrorType,
Collectors.counting()
));
analysis.setErrorTypeAnalysis(errorTypes);
return analysis;
}
@Override
public List<DisposalBehaviorVO> getUserDisposalBehaviors(Long userId, LocalDate startDate, LocalDate endDate) {
List<DisposalBehavior> behaviors = disposalBehaviorMapper.selectList(
new LambdaQueryWrapper<DisposalBehavior>()
.eq(DisposalBehavior::getUserId, userId)
.ge(DisposalBehavior::getDisposalTime, startDate.atStartOfDay())
.le(DisposalBehavior::getDisposalTime, endDate.atTime(23, 59, 59))
.orderByDesc(DisposalBehavior::getDisposalTime)
);
return behaviors.stream()
.map(this::convertToDisposalBehaviorVO)
.collect(Collectors.toList());
}
@Override
public EffectivenessReportVO getEffectivenessReport(LocalDate reportDate) {
EffectivenessReport report = effectivenessReportMapper.selectOne(
new LambdaQueryWrapper<EffectivenessReport>()
.eq(EffectivenessReport::getReportDate, reportDate)
);
if (report == null) {
generateEffectivenessReport(reportDate);
report = effectivenessReportMapper.selectOne(
new LambdaQueryWrapper<EffectivenessReport>()
.eq(EffectivenessReport::getReportDate, reportDate)
);
}
return convertToEffectivenessReportVO(report);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void generateEffectivenessReport(LocalDate reportDate) {
// 获取统计周期(前一天)
LocalDateTime startTime = reportDate.atStartOfDay();
LocalDateTime endTime = reportDate.atTime(23, 59, 59);
// 查询当天的投放行为数据
List<DisposalBehavior> behaviors = disposalBehaviorMapper.selectList(
new LambdaQueryWrapper<DisposalBehavior>()
.ge(DisposalBehavior::getDisposalTime, startTime)
.le(DisposalBehavior::getDisposalTime, endTime)
);
// 统计数据
Set<Long> activeUsers = behaviors.stream()
.map(DisposalBehavior::getUserId)
.collect(Collectors.toSet());
BigDecimal totalWeight = behaviors.stream()
.map(DisposalBehavior::getWeight)
.reduce(BigDecimal.ZERO, BigDecimal::add);
long correctCount = behaviors.stream()
.filter(DisposalBehavior::getIsCorrect)
.count();
// 创建报告
EffectivenessReport report = new EffectivenessReport();
report.setReportDate(reportDate);
report.setTotalUsers(1000); // TODO: 从用户表获取
report.setActiveUsers(activeUsers.size());
report.setTotalWeight(totalWeight);
report.setCorrectRate(calculatePercentage(correctCount, behaviors.size()));
report.setReductionRate(calculateReductionRate(reportDate));
report.setSatisfactionRate(new BigDecimal("95.00")); // TODO: 从满意度调查获取
report.setMainProblems(analyzeMainProblems(behaviors));
report.setSuggestions(generateSuggestions(behaviors));
// 保存报告
effectivenessReportMapper.insert(report);
}
@Override
public List<TrendPredictionVO> getTrendPrediction(String wasteType, Integer predictionMonths) {
// 获取历史数据
LocalDate endDate = LocalDate.now();
LocalDate startDate = endDate.minusMonths(12); // 使用过去12个月的数据进行预测
List<WasteStatistics> historicalData = wasteStatisticsMapper.selectList(
new LambdaQueryWrapper<WasteStatistics>()
.eq(WasteStatistics::getWasteType, wasteType)
.ge(WasteStatistics::getCollectionTime, startDate)
.le(WasteStatistics::getCollectionTime, endDate)
.orderByAsc(WasteStatistics::getCollectionTime)
);
// 使用简单线性回归进行预测
SimpleRegression regression = new SimpleRegression();
for (int i = 0; i < historicalData.size(); i++) {
regression.addData(i, historicalData.get(i).getWeight().doubleValue());
}
// 生成预测数据
List<TrendPredictionVO> predictions = new ArrayList<>();
LocalDate currentDate = endDate.plusDays(1);
for (int i = 0; i < predictionMonths * 30; i++) {
TrendPredictionVO prediction = new TrendPredictionVO();
prediction.setPredictionDate(currentDate);
prediction.setWasteType(wasteType);
double predictedValue = regression.predict(historicalData.size() + i);
prediction.setPredictedWeight(new BigDecimal(predictedValue).setScale(2, RoundingMode.HALF_UP));
// 计算预测区间
double[] interval = regression.getConfidenceInterval(historicalData.size() + i, 0.95);
prediction.setConfidenceLevel(new BigDecimal("95.00"));
prediction.setLowerBound(new BigDecimal(interval[0]).setScale(2, RoundingMode.HALF_UP));
prediction.setUpperBound(new BigDecimal(interval[1]).setScale(2, RoundingMode.HALF_UP));
predictions.add(prediction);
currentDate = currentDate.plusDays(1);
}
return predictions;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateTrendPrediction(String wasteType) {
// 删除旧的预测数据
trendPredictionMapper.delete(
new LambdaQueryWrapper<TrendPrediction>()
.eq(TrendPrediction::getWasteType, wasteType)
.ge(TrendPrediction::getPredictionDate, LocalDate.now())
);
// 生成新的预测数据
List<TrendPredictionVO> predictions = getTrendPrediction(wasteType, 3); // 预测未来3个月
// 保存预测数据
predictions.forEach(prediction -> {
TrendPrediction entity = new TrendPrediction();
BeanUtils.copyProperties(prediction, entity);
trendPredictionMapper.insert(entity);
});
}
@Override
public Map<String, Object> getDataAnalysisOverview() {
Map<String, Object> overview = new HashMap<>();
LocalDate today = LocalDate.now();
LocalDate startDate = today.minusDays(7);
// 获取最近7天的数据
List<WasteStatistics> weeklyStats = wasteStatisticsMapper.selectList(
new LambdaQueryWrapper<WasteStatistics>()
.ge(WasteStatistics::getCollectionTime, startDate)
.le(WasteStatistics::getCollectionTime, today)
);
// 计算总量和增长率
BigDecimal totalWeight = weeklyStats.stream()
.map(WasteStatistics::getWeight)
.reduce(BigDecimal.ZERO, BigDecimal::add);
double averageCorrectRate = weeklyStats.stream()
.mapToDouble(ws -> ws.getCorrectRate().doubleValue())
.average()
.orElse(0.0);
// 获取最新的效果评估报告
EffectivenessReport latestReport = effectivenessReportMapper.selectOne(
new LambdaQueryWrapper<EffectivenessReport>()
.orderByDesc(EffectivenessReport::getReportDate)
.last("LIMIT 1")
);
overview.put("totalWeight", totalWeight);
overview.put("averageCorrectRate", averageCorrectRate);
overview.put("activeUsers", latestReport != null ? latestReport.getActiveUsers() : 0);
overview.put("reductionRate", latestReport != null ? latestReport.getReductionRate() : 0);
return overview;
}
@Override
public List<TimeAnalysisVO> getTimeAnalysis(LocalDate startDate, LocalDate endDate) {
List<DisposalBehavior> behaviors = disposalBehaviorMapper.selectList(
new LambdaQueryWrapper<DisposalBehavior>()
.ge(DisposalBehavior::getDisposalTime, startDate.atStartOfDay())
.le(DisposalBehavior::getDisposalTime, endDate.atTime(23, 59, 59))
);
// 按小时统计
Map<Integer, List<DisposalBehavior>> hourlyGroups = behaviors.stream()
.collect(Collectors.groupingBy(b -> b.getDisposalTime().getHour()));
return hourlyGroups.entrySet().stream()
.map(entry -> {
TimeAnalysisVO vo = new TimeAnalysisVO();
vo.setHour(entry.getKey());
vo.setCount(entry.getValue().size());
long correctCount = entry.getValue().stream()
.filter(DisposalBehavior::getIsCorrect)
.count();
vo.setCorrectRate(calculatePercentage(correctCount, entry.getValue().size()));
return vo;
})
.sorted(Comparator.comparing(TimeAnalysisVO::getHour))
.collect(Collectors.toList());
}
@Override
public List<RegionAnalysisVO> getRegionAnalysis(LocalDate startDate, LocalDate endDate) {
List<DisposalBehavior> behaviors = disposalBehaviorMapper.selectList(
new LambdaQueryWrapper<DisposalBehavior>()
.ge(DisposalBehavior::getDisposalTime, startDate.atStartOfDay())
.le(DisposalBehavior::getDisposalTime, endDate.atTime(23, 59, 59))
);
// 按区域统计
Map<String, List<DisposalBehavior>> regionGroups = behaviors.stream()
.collect(Collectors.groupingBy(DisposalBehavior::getLocation));
return regionGroups.entrySet().stream()
.map(entry -> {
RegionAnalysisVO vo = new RegionAnalysisVO();
vo.setRegion(entry.getKey());
vo.setCount(entry.getValue().size());
long correctCount = entry.getValue().stream()
.filter(DisposalBehavior::getIsCorrect)
.count();
vo.setCorrectRate(calculatePercentage(correctCount, entry.getValue().size()));
// 计算各类型垃圾占比
Map<String, Long> typeDistribution = entry.getValue().stream()
.collect(Collectors.groupingBy(
DisposalBehavior::getWasteType,
Collectors.counting()
));
vo.setTypeDistribution(typeDistribution);
return vo;
})
.collect(Collectors.toList());
}
@Override
public byte[] exportAnalysisReport(LocalDate startDate, LocalDate endDate) {
// 获取各项分析数据
List<WasteStatisticsVO> statistics = getWasteStatistics(startDate, endDate, null);
DisposalBehaviorAnalysisVO behaviorAnalysis = getDisposalBehaviorAnalysis(startDate, endDate);
List<TimeAnalysisVO> timeAnalysis = getTimeAnalysis(startDate, endDate);
List<RegionAnalysisVO> regionAnalysis = getRegionAnalysis(startDate, endDate);
// 生成Excel报告
return ExcelUtils.generateAnalysisReport(
statistics,
behaviorAnalysis,
timeAnalysis,
regionAnalysis,
startDate,
endDate
);
}
private BigDecimal calculatePercentage(long part, long total) {
if (total == 0) return BigDecimal.ZERO;
return new BigDecimal(part)
.multiply(new BigDecimal("100"))
.divide(new BigDecimal(total), 2, RoundingMode.HALF_UP);
}
private BigDecimal calculateReductionRate(LocalDate currentDate) {
// 获取上周同期数据
LocalDate lastWeek = currentDate.minusWeeks(1);
WasteStatistics lastWeekStats = wasteStatisticsMapper.selectOne(
new LambdaQueryWrapper<WasteStatistics>()
.eq(WasteStatistics::getCollectionTime, lastWeek)
);
WasteStatistics currentStats = wasteStatisticsMapper.selectOne(
new LambdaQueryWrapper<WasteStatistics>()
.eq(WasteStatistics::getCollectionTime, currentDate)
);
if (lastWeekStats == null || currentStats == null) {
return BigDecimal.ZERO;
}
BigDecimal reduction = lastWeekStats.getWeight().subtract(currentStats.getWeight());
return reduction.multiply(new BigDecimal("100"))
.divide(lastWeekStats.getWeight(), 2, RoundingMode.HALF_UP);
}
private String analyzeMainProblems(List<DisposalBehavior> behaviors) {
// 分析错误行为
Map<String, Long> errorTypes = behaviors.stream()
.filter(b -> !b.getIsCorrect())
.collect(Collectors.groupingBy(
DisposalBehavior::getErrorType,
Collectors.counting()
));
// 找出主要问题
return errorTypes.entrySet().stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
.limit(3)
.map(e -> e.getKey() + "(" + e.getValue() + "次)")
.collect(Collectors.joining("、"));
}
private String generateSuggestions(List<DisposalBehavior> behaviors) {
List<String> suggestions = new ArrayList<>();
// 分析错误率
long totalErrors = behaviors.stream()
.filter(b -> !b.getIsCorrect())
.count();
double errorRate = (double) totalErrors / behaviors.size();
if (errorRate > 0.3) {
suggestions.add("建议加强垃圾分类知识的宣传教育");
}
// 分析时段分布
Map<Integer, Long> hourlyDistribution = behaviors.stream()
.collect(Collectors.groupingBy(
b -> b.getDisposalTime().getHour(),
Collectors.counting()
));
// 检查是否存在投放高峰期
hourlyDistribution.entrySet().stream()
.filter(e -> e.getValue() > behaviors.size() * 0.2)
.findFirst()
.ifPresent(e -> suggestions.add(
String.format("建议在%d时段增加垃圾收集频次", e.getKey())
));
return String.join(";", suggestions);
}
private WasteStatisticsVO convertToWasteStatisticsVO(WasteStatistics statistics) {
WasteStatisticsVO vo = new WasteStatisticsVO();
BeanUtils.copyProperties(statistics, vo);
return vo;
}
private DisposalBehaviorVO convertToDisposalBehaviorVO(DisposalBehavior behavior) {
DisposalBehaviorVO vo = new DisposalBehaviorVO();
BeanUtils.copyProperties(behavior, vo);
return vo;
}
private EffectivenessReportVO convertToEffectivenessReportVO(EffectivenessReport report) {
EffectivenessReportVO vo = new EffectivenessReportVO();
BeanUtils.copyProperties(report, vo);
return vo;
}
}
backend\src\main\java\com\campus\waste\service\impl\DisposalManagementServiceImpl.java
package com.campus.waste.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.campus.waste.entity.*;
import com.campus.waste.mapper.*;
import com.campus.waste.model.vo.*;
import com.campus.waste.service.DisposalManagementService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.BeanUtils;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class DisposalManagementServiceImpl implements DisposalManagementService {
private final SmartBinMapper smartBinMapper;
private final DisposalRecordMapper disposalRecordMapper;
private final BinAlertMapper binAlertMapper;
private final ClassificationStatsMapper classificationStatsMapper;
private final WasteCategoryMapper wasteCategoryMapper;
public DisposalManagementServiceImpl(SmartBinMapper smartBinMapper,
DisposalRecordMapper disposalRecordMapper,
BinAlertMapper binAlertMapper,
ClassificationStatsMapper classificationStatsMapper,
WasteCategoryMapper wasteCategoryMapper) {
this.smartBinMapper = smartBinMapper;
this.disposalRecordMapper = disposalRecordMapper;
this.binAlertMapper = binAlertMapper;
this.classificationStatsMapper = classificationStatsMapper;
this.wasteCategoryMapper = wasteCategoryMapper;
}
@Override
public List<SmartBinVO> getAllBins() {
List<SmartBin> bins = smartBinMapper.selectList(null);
return bins.stream()
.map(this::convertToSmartBinVO)
.collect(Collectors.toList());
}
@Override
public SmartBinDetailVO getBinDetail(Long binId) {
SmartBin bin = smartBinMapper.selectById(binId);
if (bin == null) {
throw new RuntimeException("垃圾箱不存在");
}
SmartBinDetailVO vo = new SmartBinDetailVO();
BeanUtils.copyProperties(bin, vo);
// 获取分类信息
WasteCategory category = wasteCategoryMapper.selectById(bin.getCategoryId());
if (category != null) {
vo.setCategoryName(category.getCategoryName());
}
// 计算容量使用率
vo.setUsageRate((double) bin.getCurrentVolume() / bin.getCapacity() * 100);
return vo;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateBinStatus(Long binId, Integer status) {
SmartBin bin = new SmartBin();
bin.setId(binId);
bin.setStatus(status);
smartBinMapper.updateById(bin);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void recordDisposal(DisposalRecord record) {
// 记录投放
record.setDisposalTime(LocalDateTime.now());
disposalRecordMapper.insert(record);
// 更新垃圾箱容量
SmartBin bin = smartBinMapper.selectById(record.getBinId());
bin.setCurrentVolume(bin.getCurrentVolume() + record.getVolume().intValue());
smartBinMapper.updateById(bin);
// 更新统计数据
updateClassificationStats(record);
// 检查是否需要生成预警
checkAndGenerateAlert(bin);
}
@Override
public IPage<DisposalRecordVO> getDisposalRecords(Long binId, Long userId, Integer pageNum, Integer pageSize) {
Page<DisposalRecord> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<DisposalRecord> wrapper = new LambdaQueryWrapper<>();
if (binId != null) {
wrapper.eq(DisposalRecord::getBinId, binId);
}
if (userId != null) {
wrapper.eq(DisposalRecord::getUserId, userId);
}
wrapper.orderByDesc(DisposalRecord::getDisposalTime);
IPage<DisposalRecord> recordPage = disposalRecordMapper.selectPage(page, wrapper);
return recordPage.convert(this::convertToDisposalRecordVO);
}
@Override
public List<BinAlertVO> getUnprocessedAlerts() {
List<BinAlert> alerts = binAlertMapper.selectList(
new LambdaQueryWrapper<BinAlert>()
.eq(BinAlert::getStatus, 0)
.orderByDesc(BinAlert::getAlertTime)
);
return alerts.stream()
.map(this::convertToBinAlertVO)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void processAlert(Long alertId, String processor, String processResult) {
BinAlert alert = new BinAlert();
alert.setId(alertId);
alert.setProcessor(processor);
alert.setProcessResult(processResult);
alert.setProcessedTime(LocalDateTime.now());
alert.setStatus(1);
binAlertMapper.updateById(alert);
}
@Override
public List<ClassificationStatsVO> getClassificationStats(Long userId, String timeRange) {
LocalDate startDate = getStartDateFromTimeRange(timeRange);
List<ClassificationStats> statsList = classificationStatsMapper.selectList(
new LambdaQueryWrapper<ClassificationStats>()
.eq(userId != null, ClassificationStats::getUserId, userId)
.ge(ClassificationStats::getStatsDate, startDate)
.orderByAsc(ClassificationStats::getStatsDate)
);
return statsList.stream()
.map(this::convertToClassificationStatsVO)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void checkAndGenerateVolumeAlerts() {
List<SmartBin> bins = smartBinMapper.selectList(null);
for (SmartBin bin : bins) {
checkAndGenerateAlert(bin);
}
}
@Override
public List<BinMonitoringVO> getBinMonitoringData() {
List<SmartBin> bins = smartBinMapper.selectList(null);
return bins.stream()
.map(this::convertToBinMonitoringVO)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void cleanBin(Long binId) {
SmartBin bin = new SmartBin();
bin.setId(binId);
bin.setCurrentVolume(0);
bin.setLastCleanedTime(LocalDateTime.now());
smartBinMapper.updateById(bin);
}
@Override
public List<BinVolumeStatVO> getBinVolumeTrend(Long binId, String timeRange) {
LocalDateTime startTime = getStartTimeFromTimeRange(timeRange);
return disposalRecordMapper.getBinVolumeTrend(binId, startTime);
}
private void checkAndGenerateAlert(SmartBin bin) {
// 检查容量预警
double usageRate = (double) bin.getCurrentVolume() / bin.getCapacity() * 100;
if (usageRate >= bin.getAlertThreshold()) {
generateAlert(bin.getId(), 1, String.format("%.2f%%", usageRate));
}
// 检查电量预警
if (bin.getBatteryLevel() != null && bin.getBatteryLevel() < 20) {
generateAlert(bin.getId(), 2, bin.getBatteryLevel().toString());
}
}
private void generateAlert(Long binId, Integer alertType, String alertValue) {
// 检查是否已存在未处理的同类型预警
Long count = binAlertMapper.selectCount(
new LambdaQueryWrapper<BinAlert>()
.eq(BinAlert::getBinId, binId)
.eq(BinAlert::getAlertType, alertType)
.eq(BinAlert::getStatus, 0)
);
if (count == 0) {
BinAlert alert = new BinAlert();
alert.setBinId(binId);
alert.setAlertType(alertType);
alert.setAlertValue(alertValue);
alert.setAlertTime(LocalDateTime.now());
alert.setStatus(0);
binAlertMapper.insert(alert);
}
}
private void updateClassificationStats(DisposalRecord record) {
LocalDate statsDate = record.getDisposalTime().toLocalDate();
ClassificationStats stats = classificationStatsMapper.selectOne(
new LambdaQueryWrapper<ClassificationStats>()
.eq(ClassificationStats::getUserId, record.getUserId())
.eq(ClassificationStats::getCategoryId, record.getWasteTypeId())
.eq(ClassificationStats::getStatsDate, statsDate)
);
if (stats == null) {
stats = new ClassificationStats();
stats.setUserId(record.getUserId());
stats.setCategoryId(record.getWasteTypeId());
stats.setStatsDate(statsDate);
stats.setTotalCount(1);
stats.setCorrectCount(record.getIsCorrect());
classificationStatsMapper.insert(stats);
} else {
stats.setTotalCount(stats.getTotalCount() + 1);
stats.setCorrectCount(stats.getCorrectCount() + record.getIsCorrect());
classificationStatsMapper.updateById(stats);
}
}
private SmartBinVO convertToSmartBinVO(SmartBin bin) {
SmartBinVO vo = new SmartBinVO();
BeanUtils.copyProperties(bin, vo);
return vo;
}
private DisposalRecordVO convertToDisposalRecordVO(DisposalRecord record) {
DisposalRecordVO vo = new DisposalRecordVO();
BeanUtils.copyProperties(record, vo);
return vo;
}
private BinAlertVO convertToBinAlertVO(BinAlert alert) {
BinAlertVO vo = new BinAlertVO();
BeanUtils.copyProperties(alert, vo);
return vo;
}
private ClassificationStatsVO convertToClassificationStatsVO(ClassificationStats stats) {
ClassificationStatsVO vo = new ClassificationStatsVO();
BeanUtils.copyProperties(stats, vo);
return vo;
}
private BinMonitoringVO convertToBinMonitoringVO(SmartBin bin) {
BinMonitoringVO vo = new BinMonitoringVO();
BeanUtils.copyProperties(bin, vo);
vo.setUsageRate((double) bin.getCurrentVolume() / bin.getCapacity() * 100);
return vo;
}
private LocalDate getStartDateFromTimeRange(String timeRange) {
LocalDate now = LocalDate.now();
switch (timeRange) {
case "week":
return now.minusWeeks(1);
case "month":
return now.minusMonths(1);
case "year":
return now.minusYears(1);
default:
return now.minusDays(7);
}
}
private LocalDateTime getStartTimeFromTimeRange(String timeRange) {
LocalDateTime now = LocalDateTime.now();
switch (timeRange) {
case "day":
return now.minusDays(1);
case "week":
return now.minusWeeks(1);
case "month":
return now.minusMonths(1);
default:
return now.minusDays(1);
}
}
}
backend\src\main\java\com\campus\waste\service\impl\KnowledgeServiceImpl.java
package com.campus.waste.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.campus.waste.entity.KnowledgeArticle;
import com.campus.waste.entity.FAQ;
import com.campus.waste.mapper.KnowledgeArticleMapper;
import com.campus.waste.mapper.FAQMapper;
import com.campus.waste.model.vo.KnowledgeArticleVO;
import com.campus.waste.model.vo.FAQVO;
import com.campus.waste.service.KnowledgeService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class KnowledgeServiceImpl implements KnowledgeService {
private final KnowledgeArticleMapper articleMapper;
private final FAQMapper faqMapper;
public KnowledgeServiceImpl(KnowledgeArticleMapper articleMapper, FAQMapper faqMapper) {
this.articleMapper = articleMapper;
this.faqMapper = faqMapper;
}
@Override
public IPage<KnowledgeArticleVO> getArticleList(Long categoryId, Integer pageNum, Integer pageSize) {
Page<KnowledgeArticle> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<KnowledgeArticle> wrapper = new LambdaQueryWrapper<KnowledgeArticle>()
.eq(categoryId != null, KnowledgeArticle::getCategoryId, categoryId)
.eq(KnowledgeArticle::getStatus, 1)
.orderByDesc(KnowledgeArticle::getCreateTime);
IPage<KnowledgeArticle> articlePage = articleMapper.selectPage(page, wrapper);
return articlePage.convert(this::convertToArticleVO);
}
@Override
public KnowledgeArticleVO getArticleDetail(Long articleId) {
KnowledgeArticle article = articleMapper.selectById(articleId);
if (article == null) {
throw new RuntimeException("文章不存在");
}
return convertToArticleVO(article);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addArticle(KnowledgeArticle article) {
articleMapper.insert(article);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateArticle(KnowledgeArticle article) {
articleMapper.updateById(article);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteArticle(Long articleId) {
articleMapper.deleteById(articleId);
}
@Override
public List<FAQVO> getFAQList(Long categoryId) {
LambdaQueryWrapper<FAQ> wrapper = new LambdaQueryWrapper<FAQ>()
.eq(categoryId != null, FAQ::getCategoryId, categoryId)
.orderByAsc(FAQ::getSortOrder);
List<FAQ> faqs = faqMapper.selectList(wrapper);
return faqs.stream()
.map(this::convertToFAQVO)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addFAQ(FAQ faq) {
faqMapper.insert(faq);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateFAQ(FAQ faq) {
faqMapper.updateById(faq);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteFAQ(Long faqId) {
faqMapper.deleteById(faqId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void incrementArticleViewCount(Long articleId) {
articleMapper.incrementViewCount(articleId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void incrementFAQViewCount(Long faqId) {
faqMapper.incrementViewCount(faqId);
}
private KnowledgeArticleVO convertToArticleVO(KnowledgeArticle article) {
KnowledgeArticleVO vo = new KnowledgeArticleVO();
BeanUtils.copyProperties(article, vo);
return vo;
}
private FAQVO convertToFAQVO(FAQ faq) {
FAQVO vo = new FAQVO();
BeanUtils.copyProperties(faq, vo);
return vo;
}
}
backend\src\main\java\com\campus\waste\service\impl\PointsServiceImpl.java
package com.campus.waste.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.campus.waste.entity.*;
import com.campus.waste.mapper.*;
import com.campus.waste.model.vo.*;
import com.campus.waste.service.PointsService;
import com.campus.waste.service.AchievementService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.BeanUtils;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class PointsServiceImpl implements PointsService {
private final UserPointsMapper userPointsMapper;
private final PointsRecordMapper pointsRecordMapper;
private final PointsProductMapper pointsProductMapper;
private final PointsExchangeMapper pointsExchangeMapper;
private final AchievementService achievementService;
public PointsServiceImpl(UserPointsMapper userPointsMapper,
PointsRecordMapper pointsRecordMapper,
PointsProductMapper pointsProductMapper,
PointsExchangeMapper pointsExchangeMapper,
AchievementService achievementService) {
this.userPointsMapper = userPointsMapper;
this.pointsRecordMapper = pointsRecordMapper;
this.pointsProductMapper = pointsProductMapper;
this.pointsExchangeMapper = pointsExchangeMapper;
this.achievementService = achievementService;
}
@Override
public UserPointsVO getUserPoints(Long userId) {
UserPoints userPoints = getUserPointsEntity(userId);
UserPointsVO vo = new UserPointsVO();
BeanUtils.copyProperties(userPoints, vo);
return vo;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addPoints(Long userId, Integer points, Integer type, Long sourceId, String sourceType, String description) {
// 更新用户积分
UserPoints userPoints = getUserPointsEntity(userId);
userPoints.setTotalPoints(userPoints.getTotalPoints() + points);
userPoints.setAvailablePoints(userPoints.getAvailablePoints() + points);
userPointsMapper.updateById(userPoints);
// 记录积分变动
PointsRecord record = new PointsRecord();
record.setUserId(userId);
record.setPoints(points);
record.setType(type);
record.setSourceId(sourceId);
record.setSourceType(sourceType);
record.setDescription(description);
pointsRecordMapper.insert(record);
// 检查并更新等级
checkAndUpdateLevel(userId);
// 检查并更新成就
achievementService.checkAndUpdateAchievements(userId, 2, userPoints.getTotalPoints());
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deductPoints(Long userId, Integer points, Integer type, Long sourceId, String sourceType, String description) {
UserPoints userPoints = getUserPointsEntity(userId);
if (userPoints.getAvailablePoints() < points) {
return false;
}
userPoints.setAvailablePoints(userPoints.getAvailablePoints() - points);
userPointsMapper.updateById(userPoints);
PointsRecord record = new PointsRecord();
record.setUserId(userId);
record.setPoints(-points);
record.setType(type);
record.setSourceId(sourceId);
record.setSourceType(sourceType);
record.setDescription(description);
pointsRecordMapper.insert(record);
return true;
}
@Override
public IPage<PointsRecordVO> getPointsRecords(Long userId, Integer type, Integer pageNum, Integer pageSize) {
Page<PointsRecord> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<PointsRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PointsRecord::getUserId, userId)
.eq(type != null, PointsRecord::getType, type)
.orderByDesc(PointsRecord::getCreateTime);
IPage<PointsRecord> recordPage = pointsRecordMapper.selectPage(page, wrapper);
return recordPage.convert(this::convertToPointsRecordVO);
}
@Override
public List<PointsProductVO> getProductList(Boolean includeOffShelf) {
LambdaQueryWrapper<PointsProduct> wrapper = new LambdaQueryWrapper<>();
if (!includeOffShelf) {
wrapper.eq(PointsProduct::getStatus, 1)
.ge(PointsProduct::getEndTime, LocalDateTime.now())
.le(PointsProduct::getStartTime, LocalDateTime.now());
}
wrapper.orderByAsc(PointsProduct::getPoints);
List<PointsProduct> products = pointsProductMapper.selectList(wrapper);
return products.stream()
.map(this::convertToPointsProductVO)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void exchangeProduct(Long userId, Long productId, Integer quantity, String receiverName, String receiverPhone, String receiverAddress) {
// 检查商品
PointsProduct product = pointsProductMapper.selectById(productId);
if (product == null || product.getStatus() != 1 || product.getStock() < quantity) {
throw new RuntimeException("商品不可兑换");
}
// 检查兑换限制
Integer exchangedCount = pointsExchangeMapper.selectCount(
new LambdaQueryWrapper<PointsExchange>()
.eq(PointsExchange::getUserId, userId)
.eq(PointsExchange::getProductId, productId)
);
if (exchangedCount + quantity > product.getExchangeLimit()) {
throw new RuntimeException("超出兑换限制");
}
// 扣减积分
int totalPoints = product.getPoints() * quantity;
boolean success = deductPoints(userId, totalPoints, 2, productId, "product_exchange", "兑换商品: " + product.getName());
if (!success) {
throw new RuntimeException("积分不足");
}
// 扣减库存
product.setStock(product.getStock() - quantity);
pointsProductMapper.updateById(product);
// 创建兑换记录
PointsExchange exchange = new PointsExchange();
exchange.setUserId(userId);
exchange.setProductId(productId);
exchange.setPointsCost(totalPoints);
exchange.setQuantity(quantity);
exchange.setStatus(0);
exchange.setReceiverName(receiverName);
exchange.setReceiverPhone(receiverPhone);
exchange.setReceiverAddress(receiverAddress);
pointsExchangeMapper.insert(exchange);
}
@Override
public IPage<PointsExchangeVO> getExchangeRecords(Long userId, Integer status, Integer pageNum, Integer pageSize) {
Page<PointsExchange> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<PointsExchange> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PointsExchange::getUserId, userId)
.eq(status != null, PointsExchange::getStatus, status)
.orderByDesc(PointsExchange::getCreateTime);
IPage<PointsExchange> exchangePage = pointsExchangeMapper.selectPage(page, wrapper);
return exchangePage.convert(this::convertToPointsExchangeVO);
}
@Override
public List<PointsRankingVO> getPointsRanking(String timeRange) {
return userPointsMapper.getPointsRanking(timeRange);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateExchangeStatus(Long exchangeId, Integer status) {
PointsExchange exchange = new PointsExchange();
exchange.setId(exchangeId);
exchange.setStatus(status);
pointsExchangeMapper.updateById(exchange);
}
@Override
public void checkAndUpdateLevel(Long userId) {
UserPoints userPoints = getUserPointsEntity(userId);
int newLevel = calculateLevel(userPoints.getTotalPoints());
if (newLevel != userPoints.getLevel()) {
userPoints.setLevel(newLevel);
userPointsMapper.updateById(userPoints);
}
}
private UserPoints getUserPointsEntity(Long userId) {
UserPoints userPoints = userPointsMapper.selectOne(
new LambdaQueryWrapper<UserPoints>()
.eq(UserPoints::getUserId, userId)
);
if (userPoints == null) {
userPoints = new UserPoints();
userPoints.setUserId(userId);
userPoints.setTotalPoints(0);
userPoints.setAvailablePoints(0);
userPoints.setLevel(1);
userPointsMapper.insert(userPoints);
}
return userPoints;
}
private int calculateLevel(int totalPoints) {
if (totalPoints < 100) return 1;
if (totalPoints < 500) return 2;
if (totalPoints < 2000) return 3;
if (totalPoints < 5000) return 4;
if (totalPoints < 10000) return 5;
return 6;
}
private PointsRecordVO convertToPointsRecordVO(PointsRecord record) {
PointsRecordVO vo = new PointsRecordVO();
BeanUtils.copyProperties(record, vo);
return vo;
}
private PointsProductVO convertToPointsProductVO(PointsProduct product) {
PointsProductVO vo = new PointsProductVO();
BeanUtils.copyProperties(product, vo);
return vo;
}
private PointsExchangeVO convertToPointsExchangeVO(PointsExchange exchange) {
PointsExchangeVO vo = new PointsExchangeVO();
BeanUtils.copyProperties(exchange, vo);
// 获取商品信息
PointsProduct product = pointsProductMapper.selectById(exchange.getProductId());
if (product != null) {
vo.setProductName(product.getName());
vo.setProductImage(product.getImageUrl());
}
return vo;
}
}
backend\src\main\java\com\campus\waste\service\impl\UserServiceImpl.java
package com.campus.waste.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.campus.waste.entity.User;
import com.campus.waste.mapper.UserMapper;
import com.campus.waste.model.dto.UserDTO;
import com.campus.waste.model.vo.UserVO;
import com.campus.waste.service.UserService;
import com.campus.waste.exception.BusinessException;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
private final PasswordEncoder passwordEncoder;
public UserServiceImpl(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void register(UserDTO userDTO) {
// 验证用户名是否已存在
if (isUsernameExists(userDTO.getUsername())) {
throw new BusinessException("用户名已存在");
}
User user = new User();
BeanUtils.copyProperties(userDTO, user);
// 加密密码
user.setPassword(passwordEncoder.encode(userDTO.getPassword()));
// 设置默认状态
user.setStatus(0);
save(user);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateUserInfo(UserDTO userDTO) {
User user = getById(userDTO.getId());
if (user == null) {
throw new BusinessException("用户不存在");
}
// 如果修改了用户名,需要验证新用户名是否存在
if (!user.getUsername().equals(userDTO.getUsername()) && isUsernameExists(userDTO.getUsername())) {
throw new BusinessException("用户名已存在");
}
BeanUtils.copyProperties(userDTO, user);
// 如果密码不为空,则更新密码
if (userDTO.getPassword() != null && !userDTO.getPassword().isEmpty()) {
user.setPassword(passwordEncoder.encode(userDTO.getPassword()));
}
updateById(user);
}
@Override
public UserVO getUserInfo(Long userId) {
User user = getById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
return convertToVO(user);
}
@Override
public List<UserVO> getUserList(Integer userType) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
if (userType != null) {
wrapper.eq(User::getUserType, userType);
}
return list(wrapper).stream()
.map(this::convertToVO)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateUserStatus(Long userId, Integer status) {
User user = getById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
User updateUser = new User();
updateUser.setId(userId);
updateUser.setStatus(status);
updateById(updateUser);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteUser(Long userId) {
User user = getById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
// 逻辑删除
removeById(userId);
}
private boolean isUsernameExists(String username) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername, username);
return count(wrapper) > 0;
}
private UserVO convertToVO(User user) {
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
// 清除敏感信息
vo.setPassword(null);
return vo;
}
}
backend\src\main\java\com\campus\waste\service\impl\WasteClassificationServiceImpl.java
package com.campus.waste.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.campus.waste.entity.WasteCategory;
import com.campus.waste.entity.WasteItem;
import com.campus.waste.mapper.WasteCategoryMapper;
import com.campus.waste.mapper.WasteItemMapper;
import com.campus.waste.model.dto.ImageRecognitionDTO;
import com.campus.waste.model.vo.WasteCategoryVO;
import com.campus.waste.model.vo.WasteItemVO;
import com.campus.waste.service.WasteClassificationService;
import com.campus.waste.service.ImageRecognitionService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class WasteClassificationServiceImpl implements WasteClassificationService {
private final WasteCategoryMapper categoryMapper;
private final WasteItemMapper itemMapper;
private final ImageRecognitionService imageRecognitionService;
public WasteClassificationServiceImpl(WasteCategoryMapper categoryMapper,
WasteItemMapper itemMapper,
ImageRecognitionService imageRecognitionService) {
this.categoryMapper = categoryMapper;
this.itemMapper = itemMapper;
this.imageRecognitionService = imageRecognitionService;
}
@Override
public List<WasteCategoryVO> getAllCategories() {
List<WasteCategory> categories = categoryMapper.selectList(
new LambdaQueryWrapper<WasteCategory>()
.orderByAsc(WasteCategory::getSortOrder)
);
return categories.stream()
.map(this::convertToCategoryVO)
.collect(Collectors.toList());
}
@Override
public List<WasteItemVO> getItemsByCategory(Long categoryId) {
List<WasteItem> items = itemMapper.selectList(
new LambdaQueryWrapper<WasteItem>()
.eq(WasteItem::getCategoryId, categoryId)
);
return items.stream()
.map(this::convertToItemVO)
.collect(Collectors.toList());
}
@Override
public List<WasteItemVO> searchItems(String keyword) {
List<WasteItem> items = itemMapper.selectList(
new LambdaQueryWrapper<WasteItem>()
.like(WasteItem::getItemName, keyword)
.or()
.like(WasteItem::getKeywords, keyword)
);
return items.stream()
.map(this::convertToItemVO)
.collect(Collectors.toList());
}
@Override
public ImageRecognitionDTO recognizeImage(MultipartFile image, Long userId) {
return imageRecognitionService.recognizeWaste(image, userId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addCategory(WasteCategory category) {
categoryMapper.insert(category);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addItem(WasteItem item) {
itemMapper.insert(item);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateCategory(WasteCategory category) {
categoryMapper.updateById(category);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateItem(WasteItem item) {
itemMapper.updateById(item);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteCategory(Long categoryId) {
// 检查是否存在关联的物品
long count = itemMapper.selectCount(
new LambdaQueryWrapper<WasteItem>()
.eq(WasteItem::getCategoryId, categoryId)
);
if (count > 0) {
throw new RuntimeException("该分类下存在物品,无法删除");
}
categoryMapper.deleteById(categoryId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteItem(Long itemId) {
itemMapper.deleteById(itemId);
}
private WasteCategoryVO convertToCategoryVO(WasteCategory category) {
WasteCategoryVO vo = new WasteCategoryVO();
BeanUtils.copyProperties(category, vo);
return vo;
}
private WasteItemVO convertToItemVO(WasteItem item) {
WasteItemVO vo = new WasteItemVO();
BeanUtils.copyProperties(item, vo);
// 获取分类信息
WasteCategory category = categoryMapper.selectById(item.getCategoryId());
if (category != null) {
vo.setCategoryName(category.getCategoryName());
}
return vo;
}
}
backend\src\main\resources\application.yml
server:
port: 8080
servlet:
context-path: /api
spring:
application:
name: campus-waste-classification
# 数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/campus_waste?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
druid:
initial-size: 5
min-idle: 10
max-active: 20
# Redis配置
redis:
host: localhost
port: 6379
database: 0
timeout: 10s
lettuce:
pool:
min-idle: 0
max-idle: 8
max-active: 8
max-wait: -1ms
# MyBatis Plus配置
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.campus.waste.entity
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: delFlag
logic-delete-value: 1
logic-not-delete-value: 0
id-type: auto
# JWT配置
jwt:
secret: campus-waste-classification-secret
expiration: 604800 # 7天,单位秒
token-start-with: "Bearer "
backend\src\main\resources\db\data_analysis.sql
-- 垃圾分类数据统计表
CREATE TABLE waste_statistics (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
waste_type VARCHAR(50) NOT NULL COMMENT '垃圾类型',
weight DECIMAL(10,2) NOT NULL COMMENT '重量(kg)',
count INT NOT NULL COMMENT '数量',
correct_rate DECIMAL(5,2) NOT NULL COMMENT '正确率(%)',
collection_time DATE NOT NULL COMMENT '统计日期',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) COMMENT '垃圾分类数据统计';
-- 投放行为分析表
CREATE TABLE disposal_behavior (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL COMMENT '用户ID',
waste_type VARCHAR(50) NOT NULL COMMENT '垃圾类型',
disposal_time DATETIME NOT NULL COMMENT '投放时间',
location VARCHAR(100) NOT NULL COMMENT '投放地点',
weight DECIMAL(10,2) NOT NULL COMMENT '重量(kg)',
is_correct TINYINT(1) NOT NULL COMMENT '是否正确分类',
error_type VARCHAR(50) COMMENT '错误类型',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) COMMENT '投放行为分析';
-- 效果评估报告表
CREATE TABLE effectiveness_report (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
report_date DATE NOT NULL COMMENT '报告日期',
total_users INT NOT NULL COMMENT '总用户数',
active_users INT NOT NULL COMMENT '活跃用户数',
total_weight DECIMAL(10,2) NOT NULL COMMENT '总重量(kg)',
correct_rate DECIMAL(5,2) NOT NULL COMMENT '整体正确率(%)',
reduction_rate DECIMAL(5,2) NOT NULL COMMENT '减少率(%)',
satisfaction_rate DECIMAL(5,2) NOT NULL COMMENT '满意度(%)',
main_problems TEXT COMMENT '主要问题',
suggestions TEXT COMMENT '改进建议',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) COMMENT '效果评估报告';
-- 趋势预测表
CREATE TABLE trend_prediction (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
prediction_date DATE NOT NULL COMMENT '预测日期',
waste_type VARCHAR(50) NOT NULL COMMENT '垃圾类型',
predicted_weight DECIMAL(10,2) NOT NULL COMMENT '预测重量(kg)',
predicted_count INT NOT NULL COMMENT '预测数量',
confidence_level DECIMAL(5,2) NOT NULL COMMENT '置信度(%)',
influencing_factors TEXT COMMENT '影响因素',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) COMMENT '趋势预测';
-- 添加索引
CREATE INDEX idx_waste_statistics_date ON waste_statistics(collection_time);
CREATE INDEX idx_disposal_behavior_user ON disposal_behavior(user_id);
CREATE INDEX idx_disposal_behavior_time ON disposal_behavior(disposal_time);
CREATE INDEX idx_effectiveness_report_date ON effectiveness_report(report_date);
CREATE INDEX idx_trend_prediction_date ON trend_prediction(prediction_date);
backend\src\main\resources\db\points_reward.sql
-- 用户积分表
CREATE TABLE IF NOT EXISTS user_points (
id BIGINT AUTO_INCREMENT COMMENT '主键ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
total_points INT NOT NULL DEFAULT 0 COMMENT '总积分',
available_points INT NOT NULL DEFAULT 0 COMMENT '可用积分',
level INT NOT NULL DEFAULT 1 COMMENT '用户等级',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户积分表';
-- 积分记录表
CREATE TABLE IF NOT EXISTS points_record (
id BIGINT AUTO_INCREMENT COMMENT '主键ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
points INT NOT NULL COMMENT '积分变动值',
type TINYINT NOT NULL COMMENT '类型(1:投放奖励 2:兑换消费 3:活动奖励)',
source_id BIGINT COMMENT '来源ID',
source_type VARCHAR(50) COMMENT '来源类型',
description VARCHAR(255) COMMENT '描述',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id),
KEY idx_user_id (user_id),
KEY idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分记录表';
-- 积分商城商品表
CREATE TABLE IF NOT EXISTS points_product (
id BIGINT AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(100) NOT NULL COMMENT '商品名称',
description TEXT COMMENT '商品描述',
image_url VARCHAR(255) COMMENT '商品图片',
points INT NOT NULL COMMENT '所需积分',
stock INT NOT NULL DEFAULT 0 COMMENT '库存数量',
total_stock INT NOT NULL DEFAULT 0 COMMENT '总库存',
exchange_limit INT DEFAULT 1 COMMENT '每人限兑数量',
status TINYINT DEFAULT 1 COMMENT '状态(0:下架 1:上架)',
start_time DATETIME COMMENT '开始时间',
end_time DATETIME COMMENT '结束时间',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城商品表';
源码下载地址:源码下载