Merge remote-tracking branch 'origin/develop' into zhanghongwei_branch

pull/14/head
ZHW 7 months ago
commit d44080486b

@ -0,0 +1,33 @@
# 小组周总结-第4周
## 团队名称和起止时间
**团队名称:** 1班-汪汪队
**开始时间:** 2023-10-13
**结束时间:** 2023-10-19
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
|------|----------|-----|----------|
| 1 | 确定分工 | 完成 | 2023-10-13 开会细分确定团队分工,统一开发工具 |
| 2 | 需求获取 | 完成 | 根据老师给出需求完成需求获取 |
| 3 | 学习 | 完成 | 根据分工学习MQTT或前端相关知识 |
| 4 | 实现基本的数据通信功能 | 基本完成 | 实现通过MQTT协议获取饮水机数据的基本功能 |
| 5 |尝试设计构建简易的ui草案| 进行中 |通过学习的前端知识尝试构建ui页面|
## 小结
1. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。
2. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。
3. **项目管理:** 项目进度管理杨安然同学及时推进项目流程,确保项目有条不紊。
---
## 【注】
1. 在小结一栏中写出希望得到如何的帮助,如讲座等;
2. 请将个人计划和总结提前发给负责人;
3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交;
4. PM综合本小组成员工作情况提交小组周计划、周总结报告按时上传至代码托管平台。

@ -4,7 +4,7 @@
**姓  名:** 曹峻茂
**团队名称:** 软1-汪汪队
**开始时间:** 2023-10-013
**开始时间:** 2023-10-13
**结束时间:** 2023-10-19

@ -0,0 +1,37 @@
# 个人周总结-第4周
## 姓名和起止时间
**姓  名:** 曹峻茂
**团队名称:** 1班-汪汪队
**开始时间:** 2023-10-13
**结束时间:** 2023-10-19
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
| ---- | ------------------------------------------------ |----|--------------------------------------------------------------|
| 3 | 学习 | 完成 | 学习MQTT协议知识 |
| 4 | 实现基本的数据通信功能 | 完成 | 实现通过MQTT协议获取饮水机数据的基本功能 |
| 4 | 召开例会,讨论原型的完成情况,并交换报告各自进度 | 完成 | 2023-10-19召开每周例会并确定下周进度计划需求 |
## 对团队工作的建议
1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平;
2. **进度统一:** 团队成员尽量统一项目进度;
## 小结
1. **原实现数据传输功能:** 完成了数据传输模块的基本实现;
2. **技能学习:** 小组成员各自开展自己所负责部分的个人技能的学习;
3. **项目管理:** PM及时推进项目进度确保工作有条不紊
4. **计划制定:** 根据本周任务完成情况与下一阶段文档提交要求,制定团队任务计划。
---
## 【注】
1. 在小结一栏中写出希望得到如何的帮助,如讲座等;
2. 请将个人计划和总结提前发给负责人;
3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交;
4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台;

@ -0,0 +1,23 @@
# 个人周计划-第1周
## 姓名和起止时间
**姓  名:** 罗月航
**团队名称:** 1班-汪汪队
**开始时间:** 2025-10-13
**结束时间:** 2025-10-19
## 本周任务计划安排
| 序号 | 计划内容 | 协作人 | 情况说明 |
| ---- | -------- | ------ | -------- |
| 1 | 需求分析与理解 | 组员 | 仔细阅读项目需求文档,理解管理员、维修人员、学生三类用户的功能需求 |
| 2 | 设计规范制定 | 组员 | 与协作者共同确定Web端与移动端的设计风格、色彩体系和组件规范 |
| 3 | 维修人员APP原型设计 | 个人 | 完成维修人员APP核心页面设备监控、工单处理的线框图和高保真设计 |
| 4 | 管理员平台协作设计 | 组员 | 协助完成管理员平台中"工单跟踪"与"人员管理"模块的UI设计 |
| 5 | 设计评审与优化 | 组员 | 参与内部设计评审根据反馈意见优化完善UI设计方案 |
## 小结
**协作沟通:** 希望建立定期的设计评审机制,确保设计方向的一致性。

@ -0,0 +1,33 @@
# 个人周总结-第4周
## 姓名和起止时间
**姓  名:** [罗月航]
**团队名称:** 1班-汪汪队
**开始时间:** 2025-10-13
**结束时间:** 2025-10-19
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
| ---- | -------- | -------- | -------- |
| 1 | 需求分析与理解 | 完成 | 仔细研读了项目需求文档,明确了管理员、维修人员、学生三类用户的功能需求和业务流程 |
| 2 | 设计规范制定 | 完成 | 与协作者共同确定了Web端与移动端统一的设计风格、色彩体系和基础组件规范 |
| 3 | 维修人员APP原型设计 | 基本完成 | 完成了维修人员APP核心页面设备监控、工单列表、抢单处理流程的线框图设计 |
| 4 | 管理员平台协作设计 | 部分完成 | 协助完成了管理员平台"工单跟踪"模块的初步界面设计 |
| 5 | 设计评审与优化 | 完成 | 参与了内部设计评审会议根据反馈意见对维修人员APP原型进行了初步优化 |
## 对团队工作的建议
1. **设计协作:** 建议建立团队共享的设计资源库,统一图标、组件等设计元素;
2. **沟通机制:** 建议固定每日站会时间,及时同步设计进展和遇到的问题;
3. **技术预研:** 建议前端开发提前了解接口数据结构,确保设计与开发衔接顺畅。
## 小结
1. **设计产出:** 完成了维修人员APP的主体原型设计确立了项目整体的视觉风格
2. **协作成效:** 与协作者配合良好保持了Web端与移动端设计风格的一致性
3. **技能提升:** 通过实际项目实践,提升了移动端界面设计的专业能力;
4. **后续计划:** 根据评审反馈完善设计细节,并开始准备前端开发环境搭建。

@ -0,0 +1,33 @@
# 个人周计划-第4周
## 姓名和起止时间
**姓  名:** 王磊
**团队名称:** 1班-汪汪队
**开始时间:** 2023-10-13
**结束时间:** 2023-10-19
## 本周任务计划安排
|序号| 计划内容| 协作人 | 情况说明 |
| ---- | ------ | ------| ------- |
| 1 | 需求获取 | 组员 | 2025-10-10 与肖雄仁老师面对面沟通确定需求 |
| 2 | 学习 | 个人 | 周内持续学习搭建数据库和处理数据的相关知识 |
| 3 | 确定分工 | 组员 | 2025-10-10 开会细分确定团队分工, |
| 4 | 学习项目的相关知识 | 个人 | 周内持续学习数据库的相关知识 |
## 小结
1. **学习需求:** 学习数据库相关知识和处理数据;
2. **知识储备:** 提前学习后续需要使用的知识,为后续的前端开发做准备;
3. **文档撰写:** 结合课内知识学习需求文档的撰写。
---
## 【注】
1. 在小结一栏中写出希望得到如何的帮助,如讲座等;
1. 请将个人计划和总结提前发给负责人;
1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交;
1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台;

@ -0,0 +1,39 @@
# 个人周总结-第4周
## 姓名和起止时间
**姓  名:** 王磊
**团队名称:** 1班-汪汪队
**开始时间:** 2025-10-13
**结束时间:** 2025-10-19
## 本周任务完成情况
序号 总结内容 是否完成 情况说明
1 MySQL数据库环境搭建与配置 完成 成功安装MySQL 8.0.43创建water_management数据库配置数据库连接环境变量解决数据库连接权限问题
2 MQTT数据处理器开发 完成 实现基于Java的MQTT数据接收、解析和处理模块支持制水机和供水机两种设备类型的数据处理
3 数据库表结构设计与创建 完成 设计并创建device_data设备数据表和device_alert告警表建立完整的数据存储结构
4 数据解析与业务逻辑实现 完成 实现JSON数据解析、设备状态判断、异常检测和自动告警生成功能
5 项目集成与测试 完成 成功将数据处理模块集成到智慧饮水系统项目中,完成多组测试数据的验证
## 对团队工作的建议
1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平;
2. **进度统一:** 团队成员尽量统一项目进度;
## 小结
1. **技能学习:** 小组成员各自开展自己所负责部分的个人技能的学习;
2. **项目管理:** PM及时推进项目进度确保工作有条不紊
3. **计划制定:** 根据本周任务完成情况与下一阶段文档提交要求,制定团队任务计划。
---
## 【注】
1. 在小结一栏中写出希望得到如何的帮助,如讲座等;
2. 请将个人计划和总结提前发给负责人;
3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交;
4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台;

@ -0,0 +1,24 @@
# 个人周计划-第4周
## 姓名和起止时间
**姓  名:** 周竞由
**团队名称:** 软1-汪汪队
**开始时间:** 2025-10-13
**结束时间:** 2025-10-19
## 本周任务计划安排
| 序号 | 计划内容 | 协作人 | 情况说明 |
|----|--------------------------------|-----|-------------------------------|
| 1 | 分析需求文档并与团队沟通,明确前端设计的核心功能模块 | 组员 | 2025-10-13 阅读需求文档并与设计团队确认需求细节 |
| 2 | 制定UI设计方案规划学生端、维修人员端和Web端的界面风格 | 个人 | 设计草图和布局框架,确保与需求一致 |
| 3 | 完成首页和功能模块的初步UI设计关注色彩、字体和布局 | 个人 | 完成首页UI设计并与团队进行初步评审 |
| 4 | 继续完善学生端APP的UI设计加入动画效果和交互细节 | 个人 | 根据反馈继续调整设计,优化交互效果 |
| 5 | 开始前端页面布局开发使用HTML & CSS编写基本结构 | 个人 | 搭建学生端的基础页面布局,并与后端接口对接 |
## 小结
1. **学习需求:** 希望能有更多关于UI设计与前端开发工具的支持和学习资源
2. **知识储备:** 提前学习前端开发的相关技能包括HTML、CSS、JavaScript的基础与进阶知识
3. **文档撰写:** 完成需求分析与前端设计方案,并与团队保持密切沟通,确保所有开发内容一致。

@ -0,0 +1,31 @@
# 个人周总结-第4周
## 姓名和起止时间
**姓  名:** 周竞由
**团队名称:** 软1-汪汪队
**开始时间:** 2025-10-13
**结束时间:** 2025-10-19
## 本周任务完成情况
| 序号 | 总结内容 | 是否完成 | 情况说明 |
|----|--------------------------------|------|------------------------------------------------|
| 1 | 分析需求文档并与团队沟通,明确前端设计的核心功能模块 | 完成 | 已深入阅读项目需求文档明确了学生端APP的主要功能与设计方向并与团队讨论确认前端开发重点 |
| 2 | 制定UI设计方案规划学生端、维修人员端和Web端的界面风格 | 完成 | 明确了整体设计风格为简洁清新风,确定主色调为蓝白系,规划了主要功能模块的布局与交互方式 |
| 3 | 完成首页和功能模块的初步UI设计关注色彩、字体和布局 | 基本完成 | 已完成首页与部分核心功能页面的UI设计初稿关注了可读性与层次感并进行初步评审 |
| 4 | 继续完善学生端APP的UI设计加入动画效果和交互细节 | 部分完成 | 已设计部分交互细节与动画效果,收集团队反馈后计划下周进一步优化体验 |
| 5 | 开始前端页面布局开发使用HTML & CSS编写基本结构 | 进行中 | 已搭建学生端基础页面框架,正在完善响应式布局与后端接口对接测试 |
## 对团队工作的建议
1. **接口文档提前共享:** 建议后端开发尽早提供学生端接口数据结构说明,以便前端更高效地完成数据对接;
2. **设计风格统一:** 建议团队建立统一的UI组件库或样式规范确保多端界面一致性
3. **阶段评审机制:** 每周可定期进行一次UI评审或开发同步会促进协作效率。
## 小结
1. **设计成果:** 本周完成了学生端整体UI设计方案初步确定了APP界面布局与色彩风格
2. **开发进展:** 完成了首页与部分页面的前端结构搭建,为后续功能开发奠定基础;
3. **学习提升:** 在本周过程中加强了对HTML、CSS布局及UI交互设计的掌握
4. **后续计划:** 下周将重点完善交互动画与页面细节,实现接口数据对接与动态展示功能。

@ -0,0 +1,34 @@
package com.campus.water.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/admin/monitor")
public class MonitorController {
@Autowired
private RealTimeService realTimeService;
@Autowired
private HistoryDataService historyDataService;
@GetMapping("/realTime/{deviceId}")
public Object getRealTimeData(@PathVariable String deviceId) {
return realTimeService.getRealTimeData(deviceId);
}
@GetMapping("/realTime/all")
public List<Object> getAllRealTimeData() {
return realTimeService.getAllRealTimeData();
}
@GetMapping("/history/{deviceId}")
public List<Object> getHistoryData(
@PathVariable String deviceId,
@RequestParam String startTime,
@RequestParam String endTime
) {
return historyDataService.getHistoryData(deviceId, startTime, endTime);
}
}

@ -0,0 +1,30 @@
package com.campus.water.web;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class RealTimeService {
private ConcurrentHashMap<String, Object> realTimeData = new ConcurrentHashMap<>();
private List<Object> alerts = new ArrayList<>();
public void updateRealTimeData(Object sensorData) {
System.out.println("更新实时数据");
}
public void addAlert(Object alertData) {
alerts.add(alertData);
System.out.println("添加告警");
}
public Object getRealTimeData(String deviceId) {
return realTimeData.get(deviceId);
}
public List<Object> getAllRealTimeData() {
return new ArrayList<>(realTimeData.values());
}
}

@ -0,0 +1,38 @@
package com.campus.water.web.config;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebMqttConfig {
// 修改为本地MQTT
private String mqttBroker = "tcp://localhost:1883";
private String clientId = "campus-water-admin-web";
@Bean
public MqttClient mqttClient() throws Exception {
MqttClient client = new MqttClient(mqttBroker, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
options.setKeepAliveInterval(60);
client.connect(options);
System.out.println("管理Web已连接MQTT服务器" + mqttBroker);
return client;
}
@Bean
public String[] subscribeTopics() {
return new String[]{
"forward/web/device/#",
"forward/web/alert/#"
};
}
@Bean
public int[] qosLevels() {
return new int[]{0, 1};
}
}

@ -0,0 +1,48 @@
package com.campus.water.web.mqtt;
import com.alibaba.fastjson2.JSONObject;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class WebMqttSubscriber {
@Autowired
private MqttClient mqttClient;
@Autowired
private String[] subscribeTopics;
@Autowired
private int[] qosLevels;
@Autowired
private RealTimeService realTimeService;
@PostConstruct
public void startSubscribe() throws Exception {
for (int i = 0; i < subscribeTopics.length; i++) {
String topic = subscribeTopics[i];
int qos = qosLevels[i];
mqttClient.subscribe(topic, qos, messageListener());
System.out.println("管理Web已订阅主题" + topic);
}
}
private IMqttMessageListener messageListener() {
return (topic, message) -> {
String payload = new String(message.getPayload());
System.out.println("管理Web接收数据主题=" + topic + ",内容=" + payload);
// 直接使用JSONObject处理数据
JSONObject data = JSONObject.parseObject(payload);
if (topic.startsWith("forward/web/device/")) {
realTimeService.updateRealTimeData(data);
} else if (topic.startsWith("forward/web/alert/")) {
realTimeService.addAlert(data);
}
};
}
}

@ -0,0 +1,86 @@
package com.campus.water.mqtt.core;
import com.alibaba.fastjson2.JSONObject;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class DataForwarder {
@Autowired
private MqttClient mqttClient;
public void forwardToWeb(Map<String, Object> sensorData) throws Exception {
String deviceId = (String) sensorData.get("deviceId");
String topic = "forward/web/device/" + deviceId;
String payload = JSONObject.toJSONString(sensorData);
publish(topic, payload, 0);
System.out.println("转发到管理Web: " + deviceId);
}
public void forwardToWeb(Map<String, Object> alertData, boolean isAlert) throws Exception {
String deviceId = (String) alertData.get("deviceId");
String topic = "forward/web/alert/" + deviceId;
String payload = JSONObject.toJSONString(alertData);
publish(topic, payload, 1);
System.out.println("转发告警到管理Web: " + deviceId);
}
public void forwardToStudentApp(Map<String, Object> sensorData) throws Exception {
Integer deviceType = (Integer) sensorData.get("deviceType");
if (deviceType != 1) return;
String deviceId = (String) sensorData.get("deviceId");
String terminalId = getRelatedTerminalId(deviceId);
String topic = "forward/student/terminal/" + terminalId;
Double tdsValue = (Double) sensorData.get("tdsValue");
String payload = JSONObject.toJSONString(new JSONObject() {{
put("terminalId", terminalId);
put("tdsValue", tdsValue);
put("waterQuality", getWaterQuality(tdsValue));
put("timestamp", sensorData.get("timestamp"));
}});
publish(topic, payload, 0);
System.out.println("转发到学生APP: " + terminalId);
}
public void forwardToRepairApp(Map<String, Object> sensorData) throws Exception {
String areaId = (String) sensorData.get("areaId");
String deviceId = (String) sensorData.get("deviceId");
String topic = "forward/repair/area/" + areaId + "/device/" + deviceId;
String payload = JSONObject.toJSONString(sensorData);
publish(topic, payload, 0);
System.out.println("转发到维修APP: " + deviceId);
}
public void forwardToRepairApp(Map<String, Object> alertData, boolean isAlert) throws Exception {
String areaId = (String) alertData.get("areaId");
String deviceId = (String) alertData.get("deviceId");
String topic = "forward/repair/area/" + areaId + "/alert/" + deviceId;
String payload = JSONObject.toJSONString(alertData);
publish(topic, payload, 1);
System.out.println("转发告警到维修APP: " + deviceId);
}
private void publish(String topic, String payload, int qos) throws Exception {
MqttMessage message = new MqttMessage(payload.getBytes());
message.setQos(qos);
mqttClient.publish(topic, message);
}
private String getRelatedTerminalId(String deviceId) {
return deviceId.equals("WM001") ? "TERM001" : "TERM002";
}
private String getWaterQuality(Double tds) {
if (tds == null) return "未知";
if (tds < 50) return "纯净水(无矿物质)";
else if (tds < 300) return "优质矿化水";
else if (tds < 600) return "合格矿化水";
else return "水质超标(不建议饮用)";
}
}

@ -0,0 +1,43 @@
package com.campus.water.mqtt.config;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MqttConfig {
// 修改为本地MQTT broker
private String brokerUrl = "tcp://localhost:1883";
private String clientId = "campus-water-mqtt-server";
private int keepAlive = 60;
@Bean
public MqttClient mqttClient() throws Exception {
MqttClient client = new MqttClient(brokerUrl, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
options.setKeepAliveInterval(keepAlive);
options.setAutomaticReconnect(true);
client.connect(options);
System.out.println("MQTT服务器已连接Broker" + brokerUrl);
return client;
}
@Bean
public String[] subscribeTopics() {
return new String[]{
"sensor/watermaker/#",
"sensor/watersupply/#",
"alert/#"
};
}
@Bean
public int[] qosLevels() {
return new int[]{0, 0, 1};
}
}

@ -0,0 +1,144 @@
package com.campus.water.mqtt.core;
import com.alibaba.fastjson2.JSONObject;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@Component
public class MqttSubscriber {
@Autowired
private MqttClient mqttClient;
@Autowired
private String[] subscribeTopics;
@Autowired
private int[] qosLevels;
@Autowired
private DataForwarder dataForwarder;
@PostConstruct
public void startSubscribe() throws Exception {
for (int i = 0; i < subscribeTopics.length; i++) {
String topic = subscribeTopics[i];
int qos = qosLevels[i];
mqttClient.subscribe(topic, qos, messageListener());
System.out.println("MQTT服务器已订阅主题" + topic + "QoS=" + qos + "");
}
}
private IMqttMessageListener messageListener() {
return (topic, message) -> {
String payload = new String(message.getPayload());
System.out.println("MQTT服务器接收数据主题=" + topic + ",内容=" + payload);
try {
if (topic.startsWith("sensor/watermaker/")) {
// 解析制水机数据
Map<String, Object> sensorData = parseWaterMakerData(topic, payload);
dataForwarder.forwardToWeb(sensorData);
dataForwarder.forwardToStudentApp(sensorData);
} else if (topic.startsWith("sensor/watersupply/")) {
// 解析供水机数据
Map<String, Object> sensorData = parseWaterSupplyData(topic, payload);
dataForwarder.forwardToWeb(sensorData);
dataForwarder.forwardToRepairApp(sensorData);
} else if (topic.startsWith("alert/")) {
// 解析告警数据
Map<String, Object> alertData = parseAlertMessage(topic, payload);
dataForwarder.forwardToWeb(alertData);
dataForwarder.forwardToRepairApp(alertData);
}
} catch (Exception e) {
System.err.println("处理MQTT消息异常: " + e.getMessage());
e.printStackTrace();
}
};
}
private Map<String, Object> parseWaterMakerData(String topic, String payload) {
Map<String, Object> data = new HashMap<>();
parseCommonData(topic, payload, data);
data.put("deviceType", 1); // 制水机
JSONObject json = JSONObject.parseObject(payload);
data.put("tdsValue", json.getDouble("tds"));
data.put("waterFlow", json.getDouble("flow"));
data.put("waterPressure", json.getDouble("pressure"));
data.put("filterLife", json.getInteger("filter_life"));
data.put("leakage", json.getBoolean("leakage"));
data.put("status", determineStatus(data));
return data;
}
private Map<String, Object> parseWaterSupplyData(String topic, String payload) {
Map<String, Object> data = new HashMap<>();
parseCommonData(topic, payload, data);
data.put("deviceType", 2); // 供水机
JSONObject json = JSONObject.parseObject(payload);
data.put("waterFlow", json.getDouble("flow"));
data.put("waterPressure", json.getDouble("pressure"));
data.put("waterLevel", json.getDouble("water_level"));
data.put("status", determineStatus(data));
return data;
}
private Map<String, Object> parseAlertMessage(String topic, String payload) {
Map<String, Object> alert = new HashMap<>();
String[] parts = topic.split("/");
alert.put("deviceId", parts.length >= 2 ? parts[1] : "unknown");
JSONObject json = JSONObject.parseObject(payload);
alert.put("alertType", json.getString("alert_type"));
alert.put("alertLevel", json.getString("alert_level"));
alert.put("alertMessage", json.getString("alert_message"));
alert.put("areaId", json.getString("area_id"));
alert.put("deviceType", json.getInteger("device_type"));
alert.put("timestamp", LocalDateTime.now().toString());
return alert;
}
private void parseCommonData(String topic, String payload, Map<String, Object> data) {
String[] parts = topic.split("/");
data.put("deviceId", parts.length >= 3 ? parts[2] : "unknown");
JSONObject json = JSONObject.parseObject(payload);
data.put("areaId", json.getString("area_id"));
data.put("temperature", json.getDouble("temperature"));
data.put("humidity", json.getDouble("humidity"));
data.put("timestamp", LocalDateTime.now().toString());
}
private String determineStatus(Map<String, Object> data) {
Integer deviceType = (Integer) data.get("deviceType");
if (deviceType == 1) { // 制水机
Double tdsValue = (Double) data.get("tdsValue");
Integer filterLife = (Integer) data.get("filterLife");
Boolean leakage = (Boolean) data.get("leakage");
if (tdsValue != null && tdsValue > 600) return "error";
if (filterLife != null && filterLife < 10) return "error";
if (leakage != null && leakage) return "error";
if (tdsValue != null && tdsValue > 300) return "warning";
} else if (deviceType == 2) { // 供水机
Double waterLevel = (Double) data.get("waterLevel");
if (waterLevel != null && waterLevel < 10) return "error";
if (waterLevel != null && waterLevel < 20) return "warning";
}
return "normal";
}
}

@ -0,0 +1,62 @@
package com.campus.water.repair.mqtt;
import com.alibaba.fastjson2.JSONObject;
import com.campus.water.repair.config.RepairMqttConfig;
import com.campus.water.repair.entity.AreaDeviceVO;
import com.campus.water.repair.entity.RepairAlertVO;
import com.campus.water.repair.service.AreaDeviceService;
import com.campus.water.repair.service.WorkOrderService;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* APPMQTT//
*/
@Component
public class RepairMqttSubscriber {
@Autowired
private RepairMqttConfig mqttConfig;
@Autowired
private AreaDeviceService areaDeviceService;
@Autowired
private WorkOrderService workOrderService;
// 维修员登录后订阅本辖区数据(先查询维修员所属片区)
public void subscribeAreaData(String repairmanId, String areaId) throws Exception {
MqttClient client = mqttConfig.mqttClient(repairmanId);
// 1. 订阅本辖区设备数据(供水机水位/制水机状态)
String deviceTopic = mqttConfig.getDeviceSubscribeTopic(areaId);
client.subscribe(deviceTopic, 0, deviceMessageListener(areaId));
// 2. 订阅本辖区告警报文(触发抢单)
String alertTopic = mqttConfig.getAlertSubscribeTopic(areaId);
client.subscribe(alertTopic, 1, alertMessageListener(areaId));
System.out.println("维修APPID" + repairmanId + ")已订阅片区" + areaId + "数据");
}
// 设备数据监听器(缓存辖区设备状态,供巡检使用)
private IMqttMessageListener deviceMessageListener(String areaId) {
return (topic, message) -> {
String payload = new String(message.getPayload());
AreaDeviceVO deviceVO = JSONObject.parseObject(payload, AreaDeviceVO.class);
areaDeviceService.updateAreaDeviceStatus(areaId, deviceVO);
System.out.println("维修APP接收片区" + areaId + "设备数据:" + payload);
};
}
// 告警监听器(新告警触发工单,推送抢单提醒)
private IMqttMessageListener alertMessageListener(String areaId) {
return (topic, message) -> {
String payload = new String(message.getPayload());
RepairAlertVO alertVO = JSONObject.parseObject(payload, RepairAlertVO.class);
// 触发工单生成存入MySQL并推送抢单提醒
workOrderService.createWorkOrderFromAlert(alertVO);
System.out.println("维修APP接收片区" + areaId + "告警:" + payload + "(已生成工单)");
};
}
}

@ -0,0 +1,43 @@
package com.campus.water.repair.config;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* APPMQTT/
*/
@Configuration
public class RepairMqttConfig {
private String mqttBroker = "tcp://mqtt-server.campus.com:1883";
private String clientIdPrefix = "campus-water-repair-"; // 客户端ID前缀拼接维修员ID
// 动态生成客户端ID按维修员ID区分
@Bean
public String clientId(String repairmanId) {
return clientIdPrefix + repairmanId;
}
@Bean
public MqttClient mqttClient(String repairmanId) throws Exception {
String clientId = clientId(repairmanId);
MqttClient client = new MqttClient(mqttBroker, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false); // 重连后恢复订阅,避免错过工单
options.setKeepAliveInterval(60);
client.connect(options);
System.out.println("维修APPID" + repairmanId + "已连接MQTT服务器");
return client;
}
// 订阅主题(仅本辖区设备数据+告警,适配“维修员仅查看本辖区”需求)
public String getDeviceSubscribeTopic(String areaId) {
return "forward/repair/area/" + areaId + "/device/#";
}
public String getAlertSubscribeTopic(String areaId) {
return "forward/repair/area/" + areaId + "/alert/#";
}
}

@ -0,0 +1,53 @@
package com.campus.water.repair.controller;
import com.campus.water.repair.entity.WorkOrderVO;
import com.campus.water.repair.service.WorkOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* APP/
*/
@RestController
@RequestMapping("/api/repair/workOrder")
public class WorkOrderController {
@Autowired
private WorkOrderService workOrderService;
// 1. 获取本辖区待抢单工单(需求“抢单”功能)
@GetMapping("/pending/{repairmanId}")
public List<WorkOrderVO> getPendingWorkOrder(@PathVariable String repairmanId) {
String areaId = workOrderService.getRepairmanArea(repairmanId); // 获取维修员所属片区
return workOrderService.getPendingWorkOrderByArea(areaId);
}
// 2. 抢单(需求核心功能,需校验辖区权限)
@PostMapping("/grab")
public String grabWorkOrder(
@RequestParam String repairmanId,
@RequestParam String orderId
) {
return workOrderService.grabWorkOrder(repairmanId, orderId) ?
"抢单成功,请及时处理" : "工单已被抢,或您无权限抢此工单";
}
// 3. 提交工单处理结果(需求“处理工单并提交”功能)
@PostMapping("/complete")
public String completeWorkOrder(
@RequestParam String orderId,
@RequestParam String repairmanId,
@RequestParam String dealNote, // 处理备注(如“已更换滤芯”)
@RequestParam String imgUrl // 现场照片URL可选
) {
workOrderService.completeWorkOrder(orderId, repairmanId, dealNote, imgUrl);
return "工单处理完成,已提交审核";
}
// 4. 查看个人处理中的工单(需求“跟踪工单状态”功能)
@GetMapping("/processing/{repairmanId}")
public List<WorkOrderVO> getProcessingWorkOrder(@PathVariable String repairmanId) {
return workOrderService.getProcessingWorkOrderByRepairman(repairmanId);
}
}

@ -0,0 +1,39 @@
package com.campus.water.student.config;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* APPMQTT
*/
@Configuration
public class StudentMqttConfig {
private String mqttBroker = "tcp://mqtt-server.campus.com:1883";
private String clientIdPrefix = "campus-water-student-"; // 客户端ID前缀拼接学生ID
// 动态生成客户端ID按学生ID区分避免冲突
@Bean
public String clientId(String studentId) {
return clientIdPrefix + studentId;
}
@Bean
public MqttClient mqttClient(String studentId) throws Exception {
String clientId = clientId(studentId);
MqttClient client = new MqttClient(mqttBroker, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true); // 学生APP登录后重新订阅无需保留会话
options.setKeepAliveInterval(120); // 移动端心跳间隔 longer适配电池续航
client.connect(options);
System.out.println("学生APPID" + studentId + "已连接MQTT服务器");
return client;
}
// 订阅主题(仅终端机水质数据,适配学生“扫码查水质”需求)
public String getSubscribeTopic(String terminalId) {
return "forward/student/terminal/" + terminalId;
}
}

@ -0,0 +1,49 @@
package com.campus.water.student.controller;
import com.campus.water.student.entity.DrinkRecordVO;
import com.campus.water.student.service.DrinkRecordService;
import com.campus.water.student.service.WaterQualityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* APP
*/
@RestController
@RequestMapping("/api/student/drink")
public class DrinkController {
@Autowired
private DrinkRecordService drinkRecordService;
@Autowired
private WaterQualityService waterQualityService;
// 1. 扫码用水(生成饮水记录,需求核心功能)
@PostMapping("/scan")
public String scanDrink(
@RequestParam String studentId,
@RequestParam String terminalId,
@RequestParam double volume // 饮水量(终端机流量传感器获取或按时间估算)
) {
// 获取当前终端机水质(用于记录)
String waterQuality = waterQualityService.getWaterQuality(terminalId);
// 生成饮水记录存入MySQL
drinkRecordService.createDrinkRecord(studentId, terminalId, volume, waterQuality);
return "扫码用水成功,饮水量:" + volume + "L水质" + waterQuality;
}
// 2. 查询今日饮水量(需求“查看每日饮水量”功能)
@GetMapping("/today/{studentId}")
public DrinkRecordVO getTodayDrink(@PathVariable String studentId) {
return drinkRecordService.getTodayDrinkRecord(studentId);
}
// 3. 查询历史饮水量(按日/周/月筛选)
@GetMapping("/history/{studentId}")
public List<DrinkRecordVO> getHistoryDrink(
@PathVariable String studentId,
@RequestParam String type, // type: day/week/month
@RequestParam String date // 日期如2024-05-20
) {
return drinkRecordService.getHistoryDrinkRecord(studentId, type, date);
}
}

@ -0,0 +1,45 @@
package com.campus.water.student.mqtt;
import com.alibaba.fastjson2.JSONObject;
import com.campus.water.student.config.StudentMqttConfig;
import com.campus.water.student.entity.WaterQualityVO;
import com.campus.water.student.service.WaterQualityService;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* APPMQTT
*/
@Component
public class StudentMqttSubscriber {
@Autowired
private StudentMqttConfig mqttConfig;
@Autowired
private WaterQualityService waterQualityService;
// 学生扫码后订阅对应终端机的水质数据
public void subscribeTerminalWaterQuality(String studentId, String terminalId) throws Exception {
// 1. 创建MQTT客户端按学生ID区分
MqttClient client = mqttConfig.mqttClient(studentId);
// 2. 订阅该终端机的水质主题
String topic = mqttConfig.getSubscribeTopic(terminalId);
client.subscribe(topic, 0, messageListener(terminalId));
System.out.println("学生APPID" + studentId + ")已订阅终端机" + terminalId + "水质数据");
}
// 消息监听器缓存水质数据供APP前端展示
private IMqttMessageListener messageListener(String terminalId) {
return (topic, message) -> {
String payload = new String(message.getPayload());
System.out.println("学生APP接收终端机" + terminalId + "水质数据:" + payload);
// 解析水质数据并缓存
WaterQualityVO qualityVO = JSONObject.parseObject(payload, WaterQualityVO.class);
waterQualityService.updateWaterQuality(terminalId, qualityVO);
};
}
}
Loading…
Cancel
Save