diff --git a/README.md b/README.md index 05cf810..15d8ad7 100644 --- a/README.md +++ b/README.md @@ -1 +1,66 @@ -# 项目架构 \ No newline at end of file +# 项目架构 + +## 概述 + +本项目是一个基于 Spring Boot 的多模块 Maven 项目,采用分层架构设计,旨在提供一个可扩展和可维护的应用程序结构。项目使用 Java 21 版本,并集成了 Spring Security、MyBatis 等主流框架。 + +## 模块结构 + +项目由以下核心模块组成: + +### ski-dashboard-model +- **作用**: 数据模型层,定义了应用程序的核心数据结构和实体类 +- **依赖**: 无外部依赖,仅依赖父项目 + +### ski-dashboard-common +- **作用**: 公共组件层,包含通用工具类、公共配置和数据访问接口 +- **主要功能**: + - 工具类和辅助函数 + - MyBatis Mapper 接口定义 + - JWT 认证相关工具 +- **依赖**: + - ski-dashboard-model + - MyBatis Spring Boot Starter + +### ski-dashboard-service +- **作用**: 业务逻辑层,实现核心业务逻辑和服务接口 +- **主要功能**: + - 业务逻辑处理 + - 数据访问服务实现 +- **依赖**: + - ski-dashboard-model + - ski-dashboard-common + +### ski-dashboard-admin +- **作用**: 应用入口和管理控制台,提供 REST API 和 Web 管理界面 +- **主要功能**: + - RESTful API 接口 + - Swagger API 文档 + - Web 安全配置 +- **依赖**: + - ski-dashboard-model + - ski-dashboard-common + - ski-dashboard-service + - SpringDoc OpenAPI UI + +## 技术栈 + +- **核心框架**: Spring Boot 3.5.7 +- **编程语言**: Java 21 +- **安全框架**: Spring Security +- **Web 框架**: Spring Web MVC +- **持久层框架**: MyBatis +- **数据库**: PostgreSQL +- **会话管理**: Spring Session JDBC +- **API 文档**: SpringDoc OpenAPI +- **JWT 认证**: java-jwt 4.4.0 +- **构建工具**: Maven +- **代码简化**: Lombok + +## 架构特点 + +1. **分层架构**: 清晰分离数据模型、公共组件、业务逻辑和应用入口 +2. **模块化设计**: 各模块职责明确,便于独立开发和维护 +3. **松耦合**: 模块间通过接口依赖,降低耦合度 +4. **可扩展性**: 易于添加新功能模块或替换现有组件 +5. **安全性**: 集成 Spring Security 和 JWT 实现认证授权 \ No newline at end of file diff --git a/absolute/path/to/file b/absolute/path/to/file new file mode 100644 index 0000000..2e6ef7b --- /dev/null +++ b/absolute/path/to/file @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO trail_position (trail_id, position, name) + VALUES (#{trailId}, #{position}, #{name}) + + + + UPDATE trail_position + SET trail_id = #{trailId}, position = #{position}, name = #{name} + WHERE id = #{id} + + + + UPDATE trail_position + SET snow_machine_status = 0, snow_machine_id = NULL + WHERE id = #{id} + + + + UPDATE trail_position + SET snow_machine_status = #{snowMachineStatus}, snow_machine_id = #{snowMachineId} + WHERE id = #{id} + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index b6a87cd..b72a2fb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot @@ -22,10 +22,11 @@ - ski-dashboard-admin - ski-dashboard-service - ski-dashboard-common + ski-dashboard-model + ski-dashboard-common + ski-dashboard-service + ski-dashboard-admin @@ -40,10 +41,22 @@ org.springframework.boot spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-logging + + org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + org.mybatis.spring.boot @@ -121,4 +134,4 @@ - + \ No newline at end of file diff --git a/ski-dashboard-admin/pom.xml b/ski-dashboard-admin/pom.xml index bf8e31e..55634e3 100644 --- a/ski-dashboard-admin/pom.xml +++ b/ski-dashboard-admin/pom.xml @@ -24,13 +24,13 @@ 2.8.14 - com.ski.lichuan + com.ski ski-dashboard-common 0.0.1-SNAPSHOT compile - com.ski.lichuan + com.ski ski-dashboard-service 0.0.1-SNAPSHOT compile diff --git a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/SkiDashboardAdminApplication.java b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/SkiDashboardAdminApplication.java index 74f3170..8a6ba39 100644 --- a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/SkiDashboardAdminApplication.java +++ b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/SkiDashboardAdminApplication.java @@ -3,9 +3,11 @@ package com.ski.lichuan.admin; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication(scanBasePackages = {"com.ski.lichuan"}) @MapperScan("com.ski.lichuan.mapper") +@EnableScheduling public class SkiDashboardAdminApplication { public static void main(String[] args) { SpringApplication.run(SkiDashboardAdminApplication.class, args); diff --git a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/config/SecurityConfig.java b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/config/SecurityConfig.java index 3e8a76f..1918ce4 100644 --- a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/config/SecurityConfig.java +++ b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/config/SecurityConfig.java @@ -53,6 +53,7 @@ public class SecurityConfig { .authorizeHttpRequests(auth -> auth // 放行 Swagger UI 相关路径 .requestMatchers( + "/**", "/api/auth/login", "/swagger-ui.html", "/swagger-ui/**", diff --git a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/OptionsController.java b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/OptionsController.java new file mode 100644 index 0000000..6002088 --- /dev/null +++ b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/OptionsController.java @@ -0,0 +1,97 @@ +package com.ski.lichuan.admin.controller; + +import com.ski.lichuan.model.common.HttpResponseData; +import com.ski.lichuan.model.dashboard.TrailPosition; +import com.ski.lichuan.model.device.Impl.SnowMachineDevice; +import com.ski.lichuan.services.SnowMachineDeviceService; +import com.ski.lichuan.services.TrailPositionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/api/options") +@CrossOrigin(origins = "*") +@Tag(name = "公共选项", description = "提供公共的选项数据") +public class OptionsController { + + @Autowired + private TrailPositionService trailPositionService; + + @Autowired + private SnowMachineDeviceService snowMachineDeviceService; + + /** + * 获取所有已绑定雪机的雪道位置信息 + * + * @return 已绑定雪机的雪道位置列表 + */ + @GetMapping("/boundTrailPositions") + @Operation(summary = "获取已绑定雪机的雪道位置信息", description = "获取所有已绑定雪机的雪道位置信息,用于下拉框选项") + public HttpResponseData> getBoundTrailPositions() { + HttpResponseData> responseData = new HttpResponseData<>(); + try { + List boundPositions = trailPositionService.getBoundTrailPositions(); + return responseData.success(boundPositions); + } catch (Exception e) { + return responseData.error("获取已绑定雪机的雪道位置信息失败: " + e.getMessage()); + } + } + + /** + * 获取所有未绑定位置的雪机设备信息 + * + * @return 未绑定位置的雪机设备列表 + */ + @GetMapping("/unboundSnowMachines") + @Operation(summary = "获取未绑定位置的雪机设备信息", description = "获取所有未绑定位置的雪机设备信息,用于下拉框选项") + public HttpResponseData> getUnboundSnowMachines() { + HttpResponseData> responseData = new HttpResponseData<>(); + try { + List unboundMachines = snowMachineDeviceService.selectUnboundMachines(); + return responseData.success(unboundMachines); + } catch (Exception e) { + return responseData.error("获取未绑定位置的雪机设备信息失败: " + e.getMessage()); + } + } + + /** + * 获取所有未绑定雪机的雪道位置信息 + * + * @return 未绑定雪机的雪道位置列表 + */ + @GetMapping("/unboundTrailPositions") + @Operation(summary = "获取未绑定雪机的雪道位置信息", description = "获取所有未绑定雪机的雪道位置信息,用于下拉框选项") + public HttpResponseData> getUnboundTrailPositions() { + HttpResponseData> responseData = new HttpResponseData<>(); + try { + List unboundPositions = trailPositionService.getUnboundTrailPositions(); + return responseData.success(unboundPositions); + } catch (Exception e) { + return responseData.error("获取未绑定雪机的雪道位置信息失败: " + e.getMessage()); + } + } + + /** + * 获取所有雪机设备信息 + * + * @return 所有雪机设备列表 + */ + @GetMapping("/snowMachineDevices") + @Operation(summary = "获取所有雪机设备信息", description = "获取所有雪机设备信息,用于下拉框选项") + public HttpResponseData> getAllSnowMachineDevices() { + HttpResponseData> responseData = new HttpResponseData<>(); + try { + List allDevices = snowMachineDeviceService.selectAllDevices(); + return responseData.success(allDevices); + } catch (Exception e) { + return responseData.error("获取所有雪机设备信息失败: " + e.getMessage()); + } + } +} diff --git a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/auth/AuthController.java b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/auth/AuthController.java index b88218a..cb29e85 100644 --- a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/auth/AuthController.java +++ b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/auth/AuthController.java @@ -77,7 +77,7 @@ public class AuthController { * @return 用户信息 */ @GetMapping("/userinfo") - @Operation(summary = "获取当前用户信息", description = "根据JWT Token获取当前登录用户的基本信息",) + @Operation(summary = "获取当前用户信息", description = "根据JWT Token获取当前登录用户的基本信息") public ResponseEntity> getUserInfo() { log.info("获取用户信息请求"); diff --git a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/device/SnowMachineController.java b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/device/SnowMachineController.java index 12defe3..66d889b 100644 --- a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/device/SnowMachineController.java +++ b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/device/SnowMachineController.java @@ -1,7 +1,96 @@ package com.ski.lichuan.admin.controller.device; + +import com.ski.lichuan.model.common.HttpResponseData; +import com.ski.lichuan.model.device.Impl.SnowMachineDevice; +import com.ski.lichuan.services.SnowMachineDeviceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/api/device/snowmachine") +@CrossOrigin(origins = "*") // 根据实际需要调整CORS策略 +@Tag(name = "造雪机设备", description = "造雪机设备的基本信息管理") public class SnowMachineController { + + @Autowired + private SnowMachineDeviceService snowMachineDeviceService; + /** - * 1.造雪机设备的基本信息管理,增删查改 + * 添加造雪机设备 + * + * @param snowMachineDevice 造雪机设备信息 + * @return 添加结果 */ -} + @PostMapping("/") + @Operation(summary = "添加造雪机设备") + public HttpResponseData addSnowMachine(@RequestBody SnowMachineDevice snowMachineDevice) { + // 调用服务层添加设备 + int result = snowMachineDeviceService.addSnowMachineDevice(snowMachineDevice); + return HttpResponseData.ok(result > 0); + } + + /** + * 更新造雪机设备信息 + * + * @param id 设备ID + * @param snowMachineDevice 更新的设备信息 + * @return 更新结果 + */ + @PutMapping("/{id}") + @Operation(summary = "更新造雪机设备信息") + public HttpResponseData updateSnowMachine(@PathVariable Integer id, @RequestBody SnowMachineDevice snowMachineDevice) { + snowMachineDevice.setId(id); + int result = snowMachineDeviceService.updateSnowMachineDevice(snowMachineDevice); + return HttpResponseData.ok(result > 0); + } + + /** + * 删除造雪机设备 + * + * @param id 设备ID + * @return 删除结果 + */ + @DeleteMapping("/{id}") + @Operation(summary = "删除造雪机设备") + public HttpResponseData deleteSnowMachine(@PathVariable Integer id) { + // 调用服务层删除设备 + int result = snowMachineDeviceService.deleteSnowMachineDevice(id); + return HttpResponseData.ok(result > 0); + } + + /** + * 根据ID获取造雪机设备信息 + * + * @param id 设备ID + * @return 设备信息 + */ + @GetMapping("/{id}") + @Operation(summary = "根据ID获取造雪机设备信息") + public HttpResponseData getSnowMachineById(@PathVariable Integer id) { + // 调用服务层获取设备信息 + SnowMachineDevice result = snowMachineDeviceService.getSnowMachineDeviceById(id); + log.info("根据ID获取造雪机设备信息,ID:{}", id); + return HttpResponseData.ok(result); + } + + /** + * 获取所有造雪机设备列表 + * + * @return 设备列表 + */ + @GetMapping("/") + @Operation(summary = "获取所有造雪机设备列表") + public HttpResponseData> getAllSnowMachines() { + // 调用服务层获取所有设备 + List result = snowMachineDeviceService.getAllSnowMachineDevices(); + return HttpResponseData.ok(result); + } +} \ No newline at end of file diff --git a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/monitor/SkiResortController.java b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/monitor/SkiResortController.java index c3cb9f1..ef4f44f 100644 --- a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/monitor/SkiResortController.java +++ b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/controller/monitor/SkiResortController.java @@ -1,10 +1,177 @@ package com.ski.lichuan.admin.controller.monitor; -public class SkiResortController { - /** - * 1.获取滑雪场雪道和造雪机关联信息 - * 2.提供滑雪场雪道和造雪机的关联移除(上下机)接口 - * 3.提供滑雪场造雪机的群控功能(只对滑雪场内的造雪机生效) - */ +import com.ski.lichuan.model.common.DeviceEnum; +import com.ski.lichuan.model.common.HttpResponseData; +import com.ski.lichuan.model.dashboard.*; +import com.ski.lichuan.model.device.Impl.SnowMachineDevice; +import com.ski.lichuan.model.device.config.SnowMachineDeviceParams; +import com.ski.lichuan.model.device.config.SnowMachineDevicePitchHorizontalParams; +import com.ski.lichuan.services.TrailPositionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; +import java.util.List; + + +@Slf4j +@RestController +@RequestMapping("/api/dashboard") +@CrossOrigin(origins = "*") // 根据实际需要调整CORS策略 +@Tag(name = "首页", description = "首页信息展示") +public class SkiResortController { + + @Autowired + private TrailPositionService trailPositionService; + + @GetMapping("/") + @Operation(summary = "首页-获取滑雪场雪道和造雪机关联信息") + public HttpResponseData>> getDashboardInfo() { + HttpResponseData>> responseData = new HttpResponseData<>(); + List> result = trailPositionService.getAllTrailPositions(); + return responseData.success(result); + } + + @GetMapping("/positionInfo") + @Operation(summary = "获取雪道坑位的详细信息(造雪机)") + public HttpResponseData getPositionInfo(Integer trailPositionId) { + HttpResponseData responseData = new HttpResponseData<>(); + TrailPositionInfo result = new TrailPositionInfo(); + + Integer trailId = trailPositionId / 10; + Integer position = trailPositionId % 10; + + // 雪机设备 + SnowMachineDevice snowMachineDevice = new SnowMachineDevice(); + snowMachineDevice.setLora("1234567890123456"); + snowMachineDevice.setId(1000000000 + trailPositionId); + snowMachineDevice.setName("造雪机-" + trailPositionId); + snowMachineDevice.setType(DeviceEnum.SNOW_MAKER.getValue()); + // 雪道位置信息 + TrailPosition trailPosition = new TrailPosition(); + trailPosition.setId(trailPositionId); + trailPosition.setTrailId(trailId); + trailPosition.setPosition(position); + trailPosition.setName(trailId + "-" + position); + trailPosition.setSnowMachineStatus(1); + trailPosition.setSnowMachineId(snowMachineDevice.getId()); + // 雪道位置状态信息 + TrailPositionMonitorInfo trailPositionMonitorInfo = new TrailPositionMonitorInfo(); + trailPositionMonitorInfo.setTrailPosition(trailPosition); + trailPositionMonitorInfo.setSnowMakerName(snowMachineDevice.getName()); + + trailPositionMonitorInfo.setPriority(1); + trailPositionMonitorInfo.setEnvironmentTemperature(-3.5); + trailPositionMonitorInfo.setEnvironmentHumidity(50.0); + trailPositionMonitorInfo.setWetBulbTemperature(0.5); + trailPositionMonitorInfo.setCommunicationStatus(true); + trailPositionMonitorInfo.setIsNormal(true); + trailPositionMonitorInfo.setIsRunning(true); + trailPositionMonitorInfo.setTemperatureConditionSatisfied(true); + trailPositionMonitorInfo.setWindSpeed(5.0); + trailPositionMonitorInfo.setWindDirection(180.0); + // 造雪机设备状态 + SnowMachineDeviceInfo snowMachineDeviceInfo = new SnowMachineDeviceInfo(); + snowMachineDeviceInfo.setSnowMachineDevice(snowMachineDevice); + snowMachineDeviceInfo.setFrontWaterPressure(0.5 + Math.random() * 0.3); // 前端水压 0.5-0.8 MPa + snowMachineDeviceInfo.setBackWaterPressure(0.4 + Math.random() * 0.2); // 后端水压 0.4-0.6 MPa + snowMachineDeviceInfo.setPressure(0.8 + Math.random() * 0.2); // 气压 0.8-1.0 MPa + snowMachineDeviceInfo.setTemperature(2.5 + Math.random() * 2.0); // 水温 2.5-4.5 ℃ + snowMachineDeviceInfo.setGivenAngle(45.0 + Math.random() * 10.0); // 给定开度 45-55° + snowMachineDeviceInfo.setActualAngle(44.0 + Math.random() * 12.0); // 实际开度 44-56° + snowMachineDeviceInfo.setIsNormal(Math.random() > 0.1); // 是否正常 90%概率正常 + snowMachineDeviceInfo.setMode((int)(Math.random() * 3) + 1); // 模式 1-3随机 + snowMachineDeviceInfo.setCompressorStatus((int)(Math.random() * 2) + 1); // 空压机状态 1-2随机 + snowMachineDeviceInfo.setFanStatus((int)(Math.random() * 2) + 1); // 风机状态 1-2随机 + snowMachineDeviceInfo.setHeatingRingStatus((int)(Math.random() * 2) + 1); // 加热环状态 1-2随机 + snowMachineDeviceInfo.setValveStatus((int)(Math.random() * 2) + 1); // 电磁阀状态1 1-2随机 + snowMachineDeviceInfo.setValveStatus2((int)(Math.random() * 2) + 1); // 电磁阀状态2 1-2随机 + snowMachineDeviceInfo.setValveStatus3((int)(Math.random() * 2) + 1); // 电磁阀状态3 1-2随机 + snowMachineDeviceInfo.setPitchMode((int)(Math.random() * 3) + 1); // 俯仰模式 1-3随机 + snowMachineDeviceInfo.setPitchAngle(15.0 + Math.random() * 20.0); // 俯仰角度 15-35° + snowMachineDeviceInfo.setPitchStatus((int)(Math.random() * 2) + 1); // 俯仰状态 1-2随机 + snowMachineDeviceInfo.setYawMode((int)(Math.random() * 3) + 1); // 摆头模式 1-3随机 + snowMachineDeviceInfo.setYawAngle(30.0 + Math.random() * 40.0); // 摆头角度 30-70° + snowMachineDeviceInfo.setYawStatus((int)(Math.random() * 2) + 1); // 摆头状态 1-2随机 + // 造雪机配置 + SnowMachineDeviceParams snowMakerDeviceParams = new SnowMachineDeviceParams(); + snowMakerDeviceParams.setWorkMode((int)(Math.random() * 3) + 1); // 工作模式 1-3随机 + snowMakerDeviceParams.setPriority((int)(Math.random() * 5) + 1); // 优先级 1-5随机 + snowMakerDeviceParams.setSnowQuality((int)(Math.random() * 5) + 1); // 雪质 1-5随机 + snowMakerDeviceParams.setStartTemperature(-5.0 + Math.random() * 10.0); // 启动温度 -5.0 ~ 5.0 + snowMakerDeviceParams.setStopTemperature(-5.0 + Math.random() * 10.0); // 停止温度 -5.0 ~ 5.0 + snowMakerDeviceParams.setFlowAdjustment((int)(Math.random() * 50)); // 流量调节 0 ~ 50 + + // 数据统计 + TrailPositionStatisticsInfo trailPositionStatisticsInfo = new TrailPositionStatisticsInfo(); + trailPositionStatisticsInfo.setRunningDuration(120.5 + Math.random() * 100.0); // 运行时长 120.5-220.5分钟 + trailPositionStatisticsInfo.setFlowStatistics(1000.0 + Math.random() * 500.0); // 流量统计 1000-1500 L/H + trailPositionStatisticsInfo.setSnowArea(500.0 + Math.random() * 300.0); // 造雪面积 500-800 平方米 + + result.setTrailPositionMonitorInfo(trailPositionMonitorInfo); + result.setSnowMakerInfo(snowMachineDeviceInfo); + result.setSnowMakerDeviceParams(snowMakerDeviceParams); + result.setTrailPositionStatisticsInfo(trailPositionStatisticsInfo); + + return responseData.success(result); + } + + @PostMapping("/connectDevice") + @Operation(summary = "雪道造雪机(上机)") + public HttpResponseData connectDevice(Integer trailPositionId, Integer deviceId) { + HttpResponseData responseData = new HttpResponseData<>(); + return responseData.success(trailPositionService.connectDevice(trailPositionId, deviceId)); + } + + @PostMapping("/disconnectDevice") + @Operation(summary = "雪道造雪机(下机)") + public HttpResponseData disconnectDevice(Integer trailPositionId) { + HttpResponseData responseData = new HttpResponseData<>(); + return responseData.success(trailPositionService.disconnectDevice(trailPositionId)); + } + + @PostMapping("/groupControl") + @Operation(summary = "雪道造雪机(群控)") + public HttpResponseData groupControl(@RequestBody GroupControlRequest request) { + HttpResponseData responseData = new HttpResponseData<>(); + + return responseData.success(trailPositionService.groupControl(request.getTrailPositionList(), request.getParams())); + } + + // 俯仰水平控制 + @PostMapping("/pitchHorizontalControl") + @Operation(summary = "雪道造雪机俯仰水平控制") + public HttpResponseData pitchHorizontalControl(@RequestBody PitchHorizontalControlRequest request) { + HttpResponseData responseData = new HttpResponseData<>(); + return responseData.success(trailPositionService.pitchHorizontalControl(request.getTrailPosition(), request.getParams())); + } + + @PostMapping("/saveSnowMakerDeviceParams") + @Operation(summary = "保存造雪机参数") + public HttpResponseData groupControl(@RequestBody GroupControlSingleRequest request) { + HttpResponseData responseData = new HttpResponseData<>(); + return responseData.success(trailPositionService.saveSnowMakerDeviceParams(request.getTrailPosition(), request.getParams())); + } + + @Data + public static class GroupControlRequest { + private List trailPositionList; + private SnowMachineDeviceParams params; + } + + @Data + public static class GroupControlSingleRequest { + private Integer trailPosition; + private SnowMachineDeviceParams params; + } + + @Data + public static class PitchHorizontalControlRequest { + private Integer trailPosition; + private SnowMachineDevicePitchHorizontalParams params; + } } diff --git a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/filter/JwtAuthenticationFilter.java b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/filter/JwtAuthenticationFilter.java index 671c2f8..cf1e69b 100644 --- a/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/filter/JwtAuthenticationFilter.java +++ b/ski-dashboard-admin/src/main/java/com/ski/lichuan/admin/filter/JwtAuthenticationFilter.java @@ -1,10 +1,12 @@ package com.ski.lichuan.admin.filter; import com.ski.lichuan.common.utils.JwtUtils; +import com.ski.lichuan.model.auth.SysUser; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; @@ -15,6 +17,7 @@ import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; +@Slf4j @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired @@ -36,6 +39,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { // 2. 验证 Token 并获取用户名 if (jwtUtils.validateToken(token)) { + // 鉴权通过,从 Token 中获取用户名 String username = jwtUtils.getUsernameFromToken(token); // 3. 加载用户信息并设置认证状态 UserDetails userDetails = userDetailsService.loadUserByUsername(username); diff --git a/ski-dashboard-admin/src/main/resources/application-dev.properties b/ski-dashboard-admin/src/main/resources/application-dev.properties new file mode 100644 index 0000000..68ddf12 --- /dev/null +++ b/ski-dashboard-admin/src/main/resources/application-dev.properties @@ -0,0 +1,21 @@ +spring.application.name=ski-dashboard-admin + +server.port=8080 + +# postgresql +spring.datasource.url=jdbc:postgresql://localhost:5432/ski_dashboard +spring.datasource.username=postgres +spring.datasource.password=tanlifan + + +# Spring Session +spring.session.store-type=jdbc +spring.session.jdbc.initialize-schema=always +spring.session.jdbc.table-name=SPRING_SESSION + +JWT_SECRET=vf4JZhcyfdK7tJs0GZ3Qjf0dSv4BId9ITjsM2fol26gOBxM17nUySiMcV0Lo2u0Y + +# Logging levels for development +logging.level.com.ski.lichuan=DEBUG +logging.level.org.springframework.web=DEBUG +logging.level.org.mybatis=DEBUG \ No newline at end of file diff --git a/ski-dashboard-admin/src/main/resources/application-prod.properties b/ski-dashboard-admin/src/main/resources/application-prod.properties new file mode 100644 index 0000000..803ca15 --- /dev/null +++ b/ski-dashboard-admin/src/main/resources/application-prod.properties @@ -0,0 +1,21 @@ +spring.application.name=ski-dashboard-admin + +server.port=8080 + +# postgresql +spring.datasource.url=jdbc:postgresql://localhost:8081/ski_dashboard +spring.datasource.username=postgres +spring.datasource.password=postgres + + +# Spring Session +spring.session.store-type=jdbc +spring.session.jdbc.initialize-schema=always +spring.session.jdbc.table-name=SPRING_SESSION + +JWT_SECRET=vf4JZhcyfdK7tJs0GZ3Qjf0dSv4BId9ITjsM2fol26gOBxM17nUySiMcV0Lo2u0Y + +# Logging levels for production +logging.level.com.ski.lichuan=INFO +logging.level.org.springframework.web=WARN +logging.level.org.mybatis=WARN \ No newline at end of file diff --git a/ski-dashboard-admin/src/main/resources/application.properties b/ski-dashboard-admin/src/main/resources/application.properties index d187943..a93ca61 100644 --- a/ski-dashboard-admin/src/main/resources/application.properties +++ b/ski-dashboard-admin/src/main/resources/application.properties @@ -1,13 +1,16 @@ spring.application.name=ski-dashboard-admin -# ??????? postgresql -spring.datasource.url=jdbc:postgresql://localhost:5432/ski_dashboard -spring.datasource.username=postgres -spring.datasource.password=tanlifan +spring.profiles.active=prod -# Spring Session?? +# Spring Session spring.session.store-type=jdbc spring.session.jdbc.initialize-schema=always spring.session.jdbc.table-name=SPRING_SESSION -JWT_SECRET=vf4JZhcyfdK7tJs0GZ3Qjf0dSv4BId9ITjsM2fol26gOBxM17nUySiMcV0Lo2u0Y \ No newline at end of file +# MyBatis configuration - enable camel case mapping +mybatis.configuration.map-underscore-to-camel-case=true +mybatis.mapper-locations=classpath*:mapper/*.xml + +thingsboard.client.url=https://tb.ski.bkiiot.com +thingsboard.client.username=tenant@thingsboard.org +thingsboard.client.password=tenant \ No newline at end of file diff --git a/ski-dashboard-admin/src/main/resources/logback-spring.xml b/ski-dashboard-admin/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..3665fc8 --- /dev/null +++ b/ski-dashboard-admin/src/main/resources/logback-spring.xml @@ -0,0 +1,57 @@ + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + + + + + + ${LOG_HOME}/info.log + + INFO + ACCEPT + DENY + + + ${LOG_HOME}/info.%d{yyyy-MM-dd}.%i.log + 10MB + 30 + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + + + + + + ${LOG_HOME}/error.log + + ERROR + ACCEPT + DENY + + + ${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.log + 10MB + 30 + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + + + + + + + + + + + \ No newline at end of file diff --git a/ski-dashboard-common/pom.xml b/ski-dashboard-common/pom.xml index 6acf98d..73a7f0f 100644 --- a/ski-dashboard-common/pom.xml +++ b/ski-dashboard-common/pom.xml @@ -8,8 +8,6 @@ ski-dashboard 0.0.1-SNAPSHOT - - com.ski.lichuan ski-dashboard-common @@ -38,4 +36,16 @@ 3.0.5 + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + \ No newline at end of file diff --git a/ski-dashboard-common/src/main/java/com/ski/lichuan/common/Main.java b/ski-dashboard-common/src/main/java/com/ski/lichuan/common/Main.java deleted file mode 100644 index 327950e..0000000 --- a/ski-dashboard-common/src/main/java/com/ski/lichuan/common/Main.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.ski.lichuan.common; - -//TIP 要运行代码,请按 或 -// 点击装订区域中的 图标。 -public class Main { - public static void main(String[] args) { - //TIP 当文本光标位于高亮显示的文本处时按 - // 查看 IntelliJ IDEA 建议如何修正。 - System.out.printf("Hello and welcome!"); - - for (int i = 1; i <= 5; i++) { - //TIP 按 开始调试代码。我们已经设置了一个 断点 - // 但您始终可以通过按 添加更多断点。 - System.out.println("i = " + i); - } - } -} \ No newline at end of file diff --git a/ski-dashboard-common/src/main/java/com/ski/lichuan/common/exception/BusinessException.java b/ski-dashboard-common/src/main/java/com/ski/lichuan/common/exception/BusinessException.java new file mode 100644 index 0000000..eef62e4 --- /dev/null +++ b/ski-dashboard-common/src/main/java/com/ski/lichuan/common/exception/BusinessException.java @@ -0,0 +1,28 @@ +package com.ski.lichuan.common.exception; + +public class BusinessException extends RuntimeException { + private Integer code; + + public BusinessException(String message) { + super(message); + this.code = 500; + } + + public BusinessException(Integer code, String message) { + super(message); + this.code = code; + } + + public BusinessException(Integer code, String message, Throwable cause) { + super(message, cause); + this.code = code; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } +} diff --git a/ski-dashboard-common/src/main/java/com/ski/lichuan/common/exception/GlobalExceptionHandler.java b/ski-dashboard-common/src/main/java/com/ski/lichuan/common/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..d9de4db --- /dev/null +++ b/ski-dashboard-common/src/main/java/com/ski/lichuan/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,85 @@ +package com.ski.lichuan.common.exception; + +import com.ski.lichuan.model.common.HttpResponseData; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.List; + +/** + * 全局异常处理器 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 处理自定义业务异常 + */ + @ExceptionHandler(BusinessException.class) + public HttpResponseData handleBusinessException(BusinessException e) { + log.error("业务异常: ", e); + return new HttpResponseData<>().error(e.getCode(), e.getMessage()); + } + + /** + * 处理参数验证异常(MethodArgumentNotValidException) + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public HttpResponseData handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + log.error("参数校验异常: ", e); + BindingResult bindingResult = e.getBindingResult(); + StringBuilder errorMsg = new StringBuilder(); + + if (bindingResult.hasErrors()) { + List fieldErrors = bindingResult.getFieldErrors(); + fieldErrors.forEach(error -> + errorMsg.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("; ") + ); + } + + return new HttpResponseData<>().error(400, "参数校验失败: " + errorMsg.toString()); + } + + /** + * 处理参数绑定异常(BindException) + */ + @ExceptionHandler(BindException.class) + public HttpResponseData handleBindException(BindException e) { + log.error("参数绑定异常: ", e); + BindingResult bindingResult = e.getBindingResult(); + StringBuilder errorMsg = new StringBuilder(); + + if (bindingResult.hasErrors()) { + List fieldErrors = bindingResult.getFieldErrors(); + fieldErrors.forEach(error -> + errorMsg.append(error.getField()).append(": ").append(error.getDefaultMessage()).append("; ") + ); + } + + return new HttpResponseData<>().error(400, "参数绑定失败: " + errorMsg.toString()); + } + + /** + * 处理IllegalArgumentException异常 + */ + @ExceptionHandler(IllegalArgumentException.class) + public HttpResponseData handleIllegalArgumentException(IllegalArgumentException e) { + log.error("非法参数异常: ", e); + return new HttpResponseData<>().error(400, e.getMessage()); + } + + /** + * 处理其他所有异常 + */ + @ExceptionHandler(Exception.class) + public HttpResponseData handleException(Exception e) { + log.error("系统异常: ", e); + return new HttpResponseData<>().error(500, "系统内部错误,请联系管理员"); + } +} \ No newline at end of file diff --git a/ski-dashboard-common/src/main/java/com/ski/lichuan/common/utils/MD5Util.java b/ski-dashboard-common/src/main/java/com/ski/lichuan/common/utils/MD5Util.java new file mode 100644 index 0000000..9918b97 --- /dev/null +++ b/ski-dashboard-common/src/main/java/com/ski/lichuan/common/utils/MD5Util.java @@ -0,0 +1,49 @@ +package com.ski.lichuan.common.utils; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * MD5加密工具类 + */ +public class MD5Util { + + /** + * 对字符串进行MD5加密 + * @param input 待加密的字符串 + * @return 加密后的十六进制字符串 + */ + public static String encrypt(String input) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8)); + + BigInteger no = new BigInteger(1, messageDigest); + StringBuilder hashText = new StringBuilder(no.toString(16)); + + // 补齐前面的0,确保是32位 + while (hashText.length() < 32) { + hashText.insert(0, "0"); + } + + return hashText.toString(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 algorithm not found", e); + } + } + + /** + * 验证明文与MD5密文是否匹配 + * @param rawText 明文 + * @param md5Text MD5密文 + * @return 是否匹配 + */ + public static boolean verify(String rawText, String md5Text) { + if (rawText == null || md5Text == null) { + return false; + } + return md5Text.equals(encrypt(rawText)); + } +} diff --git a/ski-dashboard-model/pom.xml b/ski-dashboard-model/pom.xml index 22131ac..0998057 100644 --- a/ski-dashboard-model/pom.xml +++ b/ski-dashboard-model/pom.xml @@ -16,5 +16,26 @@ 21 UTF-8 + + + io.swagger.core.v3 + swagger-annotations-jakarta + 2.2.38 + compile + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + \ No newline at end of file diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/mapper/SnowMachineMapper.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/mapper/SnowMachineMapper.java new file mode 100644 index 0000000..8ac1d17 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/mapper/SnowMachineMapper.java @@ -0,0 +1,30 @@ +package com.ski.lichuan.mapper; + +import com.ski.lichuan.model.device.Impl.SnowMachineDevice; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface SnowMachineMapper { + + int insert(SnowMachineDevice snowMachineDevice); + + int insertById(SnowMachineDevice snowMachineDevice); + + List selectAll(); + + SnowMachineDevice selectById(Integer id); + + SnowMachineDevice selectByLora(String lora); + + List selectUnboundMachines(); + + int count(); + + int updateName(SnowMachineDevice snowMachineDevice); + + int updateNameAndLora(SnowMachineDevice snowMachineDevice); + + int deleteById(Integer id); +} \ No newline at end of file diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/mapper/TrailPositionMapper.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/mapper/TrailPositionMapper.java new file mode 100644 index 0000000..a2b5850 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/mapper/TrailPositionMapper.java @@ -0,0 +1,39 @@ +package com.ski.lichuan.mapper; + +import com.ski.lichuan.model.dashboard.TrailPosition; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TrailPositionMapper { + + List selectByTrailId(Integer trailId); + + TrailPosition selectById(Integer id); + + List selectAll(); + + /** + * 获取所有未绑定雪机的雪道位置信息 + */ + List selectUnboundPositions(); + + List selectBoundPositions(); + + int insert(TrailPosition trailPosition); + + int update(TrailPosition trailPosition); + + int clearSnowMachine(Integer id); + + int updateSnowMachine(Integer id, Integer snowMachineId); + + /** + * 根据雪道位置ID列表查询造雪机设备ID列表 + * + * @param trailPositionIds 雪道位置ID列表 + * @return 造雪机设备ID列表 + */ + List selectDeviceIdsByTrailPositionIds(List trailPositionIds); +} \ No newline at end of file diff --git a/ski-dashboard-common/src/main/java/com/ski/lichuan/mapper/UserMapper.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/mapper/UserMapper.java similarity index 100% rename from ski-dashboard-common/src/main/java/com/ski/lichuan/mapper/UserMapper.java rename to ski-dashboard-model/src/main/java/com/ski/lichuan/mapper/UserMapper.java diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/auth/SysUser.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/auth/SysUser.java index b2263fb..51418b0 100644 --- a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/auth/SysUser.java +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/auth/SysUser.java @@ -7,12 +7,20 @@ import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.Collections; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema @Data public class SysUser implements UserDetails { + @Schema(description = "用户ID") private Long id; + @Schema(description = "用户名") private String username; + @Schema(description = "密码") private String password; + @Schema(description = "昵称") private String nickname; + @Schema(description = "状态") private Integer status; // 1-正常,0-禁用 // 实现 UserDetails 接口方法 diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/common/DeviceEnum.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/common/DeviceEnum.java new file mode 100644 index 0000000..9af0c37 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/common/DeviceEnum.java @@ -0,0 +1,18 @@ +package com.ski.lichuan.model.common; + +import lombok.Getter; + +@Getter +public enum DeviceEnum { + SNOW_MAKER("雪机", 1), + WATER_PUMP("水泵", 2), + ; + private final String name; + private final Integer value; + + DeviceEnum(String name, Integer value) { + this.name = name; + this.value = value; + } + +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/common/HttpResponseData.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/common/HttpResponseData.java index b93e151..7de31bf 100644 --- a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/common/HttpResponseData.java +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/common/HttpResponseData.java @@ -2,7 +2,6 @@ package com.ski.lichuan.model.common; import lombok.Data; -import org.springframework.http.ResponseEntity; @Data public class HttpResponseData { @@ -45,4 +44,17 @@ public class HttpResponseData { response.setMessage(message); return response; } -} + + // 增加静态方法便于使用 + public static HttpResponseData ok(T data) { + return new HttpResponseData<>(data); + } + + public static HttpResponseData fail(String message) { + return new HttpResponseData().error(message); + } + + public static HttpResponseData fail(Integer code, String message) { + return new HttpResponseData().error(code, message); + } +} \ No newline at end of file diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/SnowMachineDeviceInfo.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/SnowMachineDeviceInfo.java new file mode 100644 index 0000000..03c2dc1 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/SnowMachineDeviceInfo.java @@ -0,0 +1,53 @@ +package com.ski.lichuan.model.dashboard; + +import com.ski.lichuan.model.device.Impl.SnowMachineDevice; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "造雪机设备状态信息") +@Data +public class SnowMachineDeviceInfo { + + @Schema(description = "造雪机设备基本信息") + public SnowMachineDevice snowMachineDevice; + @Schema(description = "前端水压") + public Double frontWaterPressure; + @Schema(description = "后端水压") + public Double backWaterPressure; + @Schema(description = "气压") + public Double pressure; + @Schema(description = "水温") + public Double temperature; + @Schema(description = "给定开度") + public Double givenAngle; + @Schema(description = "实际开度") + public Double actualAngle; + @Schema(description = "是否正常") + public Boolean isNormal; + @Schema(description = "模式 关1/开2/自动3") + public Integer mode; + @Schema(description = "空压机状态 关1/开2") + public Integer compressorStatus; + @Schema(description = "风机状态 关1/开2") + public Integer fanStatus; + @Schema(description = "加热环状态 关1/开2") + public Integer heatingRingStatus; + @Schema(description = "电磁阀状态1 关1/开2") + public Integer valveStatus; + @Schema(description = "电磁阀状态2 关1/开2") + public Integer valveStatus2; + @Schema(description = "电磁阀状态3 关1/开2") + public Integer valveStatus3; + @Schema(description = "俯仰模式 关1/开2/自动3") + public Integer pitchMode; + @Schema(description = "俯仰角度") + public Double pitchAngle; + @Schema(description = "俯仰状态 关1/开2") + public Integer pitchStatus; + @Schema(description = "摆头模式 关1/开2/自动3") + public Integer yawMode; + @Schema(description = "摆头角度") + public Double yawAngle; + @Schema(description = "摆头状态 关1/开2") + public Integer yawStatus; +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPosition.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPosition.java new file mode 100644 index 0000000..de4baa1 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPosition.java @@ -0,0 +1,22 @@ +package com.ski.lichuan.model.dashboard; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "雪道位置信息") +@Data +public class TrailPosition { + @Schema(description = "ID") + public Integer id; + @Schema(description = "雪道ID") + public Integer trailId; + @Schema(description = "位置") + public Integer position; + @Schema(description = "名称") + public String name; + @Schema(description = "雪机状态 0无 1正常 2停止") + public Integer snowMachineStatus; + @Schema(description = "造雪机ID") + public Integer snowMachineId; + +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPositionInfo.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPositionInfo.java new file mode 100644 index 0000000..e00dadb --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPositionInfo.java @@ -0,0 +1,23 @@ +package com.ski.lichuan.model.dashboard; + +import com.ski.lichuan.model.device.config.SnowMachineDeviceParams; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "雪道位置状态") +@Data +public class TrailPositionInfo { + + @Schema(description = "雪道位置状态信息") + public TrailPositionMonitorInfo trailPositionMonitorInfo; + // 造雪机信息 + @Schema(description = "造雪机设备状态信息") + public SnowMachineDeviceInfo snowMakerInfo; + // 雪机配置 + @Schema(description = "造雪机配置信息") + public SnowMachineDeviceParams snowMakerDeviceParams; + // 统计信息 + @Schema(description = "统计信息") + public TrailPositionStatisticsInfo trailPositionStatisticsInfo; + +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPositionMonitorInfo.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPositionMonitorInfo.java new file mode 100644 index 0000000..5d19136 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPositionMonitorInfo.java @@ -0,0 +1,55 @@ +package com.ski.lichuan.model.dashboard; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "雪道位置状态信息") +@Data +public class TrailPositionMonitorInfo { + + @Schema(description = "雪道位置信息") + public TrailPosition trailPosition; + + // 造雪机名称 + @Schema(description = "造雪机名称") + public String snowMakerName; + + // 是否有造雪机 + @Schema(description = "是否有造雪机") + public Boolean hasSnowMaker; + // 优先级 + @Schema(description = "优先级") + public Integer priority; + // 环境温度 + @Schema(description = "环境温度") + public Double environmentTemperature; + // 环境湿度 + @Schema(description = "环境湿度") + public Double environmentHumidity; + // 湿球温度 + @Schema(description = "湿球温度") + public Double wetBulbTemperature; + + // 通讯状态 + @Schema(description = "通讯状态") + public Boolean communicationStatus; + // 自动模式 + @Schema(description = "自动模式") + public Boolean autoMode; + // 是否正常 + @Schema(description = "是否正常") + public Boolean isNormal; + // 正常运行 + @Schema(description = "正常运行") + public Boolean isRunning; + // 温度条件满足 + @Schema(description = "温度条件满足") + public Boolean temperatureConditionSatisfied; + // 风速 + @Schema(description = "风速") + public Double windSpeed; + // 风向 + @Schema(description = "风向") + public Double windDirection; + +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPositionStatisticsInfo.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPositionStatisticsInfo.java new file mode 100644 index 0000000..5d814b1 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/dashboard/TrailPositionStatisticsInfo.java @@ -0,0 +1,17 @@ +package com.ski.lichuan.model.dashboard; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "雪道位置统计信息") +@Data +public class TrailPositionStatisticsInfo { + + + @Schema(description = "运行时长") + public Double runningDuration; + @Schema(description = "流量统计") + public Double flowStatistics; + @Schema(description = "造雪面积") + public Double snowArea; +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/Device.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/Device.java new file mode 100644 index 0000000..9f6ff87 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/Device.java @@ -0,0 +1,14 @@ +package com.ski.lichuan.model.device; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "设备") +@Data +public abstract class Device { + @Schema(description = "设备名称") + public String name; + @Schema(description = "设备类型") + public Integer type; + @Schema(description = "设备ID") + public Integer id; +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/Impl/SnowMachineDevice.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/Impl/SnowMachineDevice.java new file mode 100644 index 0000000..e25aa03 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/Impl/SnowMachineDevice.java @@ -0,0 +1,21 @@ +package com.ski.lichuan.model.device.Impl; + +import com.ski.lichuan.model.common.DeviceEnum; +import com.ski.lichuan.model.device.Device; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Data +@Schema(description = "造雪机设备") +public class SnowMachineDevice extends Device { + + // 设备的LORA地址 + @Schema(description = "设备的LORA地址") + public String lora; + + public SnowMachineDevice() { + this.type = DeviceEnum.SNOW_MAKER.getValue(); + } +} + diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/Impl/WaterPumpDevice.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/Impl/WaterPumpDevice.java new file mode 100644 index 0000000..2f80e14 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/Impl/WaterPumpDevice.java @@ -0,0 +1,18 @@ +package com.ski.lichuan.model.device.Impl; + +import com.ski.lichuan.model.common.DeviceEnum; +import com.ski.lichuan.model.device.Device; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "水泵设备") +public class WaterPumpDevice extends Device { + + @Schema(description = "LoRa MAC地址") + public String loraMac; + + public WaterPumpDevice() { + this.type = DeviceEnum.WATER_PUMP.getValue(); + } +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/config/SnowMachineDeviceParams.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/config/SnowMachineDeviceParams.java new file mode 100644 index 0000000..3d77c95 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/config/SnowMachineDeviceParams.java @@ -0,0 +1,28 @@ +package com.ski.lichuan.model.device.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "造雪机设备参数") +public class SnowMachineDeviceParams { + + // 工作模式 关1/开2/自动3 + @Schema(description = "工作模式 关1/开2/自动3") + public Integer workMode; + // 优先级 1-5 + @Schema(description = "优先级 1-5") + public Integer priority; + // 雪质 1-5 + @Schema(description = "雪质 1-5") + public Integer snowQuality; + // 启动温度 -5.0 ~ 5.0 + @Schema(description = "启动温度 -5.0 ~ 5.0") + public Double startTemperature; + // 停止温度 -5.0 ~ 5.0 + @Schema(description = "停止温度 -5.0 ~ 5.0") + public Double stopTemperature; + // 流量调节 0 ~ 50 + @Schema(description = "流量调节 0 ~ 50") + public Integer flowAdjustment; +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/config/SnowMachineDevicePitchHorizontalParams.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/config/SnowMachineDevicePitchHorizontalParams.java new file mode 100644 index 0000000..5fa1d1c --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/device/config/SnowMachineDevicePitchHorizontalParams.java @@ -0,0 +1,21 @@ +package com.ski.lichuan.model.device.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "造雪机俯仰水平控制参数") +public class SnowMachineDevicePitchHorizontalParams { + + + // 雪机工作模式 关1/开2/自动3 + @Schema(description = "工作模式 关1/开2/自动3") + public Integer workMode; + // 俯仰工作模式 关1/开2/自动3 + @Schema(description = "俯仰工作模式 关1/开2/自动3") + public Integer pitchWorkMode; + // 水平工作模式 关1/开2/自动3 + @Schema(description = "水平工作模式 关1/开2/自动3") + public Integer horizontalWorkMode; + +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/log/DeviceWarningRecord.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/log/DeviceWarningRecord.java new file mode 100644 index 0000000..2d157f3 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/log/DeviceWarningRecord.java @@ -0,0 +1,21 @@ +package com.ski.lichuan.model.log; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@Schema(description = "设备警告记录") +public class DeviceWarningRecord { + @Schema(description = "警告记录ID") + public Integer id; + @Schema(description = "警告记录内容") + public String content; + @Schema(description = "警告记录类型") + public String warningType; + @Schema(description = "警告记录设备") + public String warningDevice; + @Schema(description = "警告记录时间") + public LocalDateTime warningTime; +} diff --git a/ski-dashboard-model/src/main/java/com/ski/lichuan/model/log/OperationRecord.java b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/log/OperationRecord.java new file mode 100644 index 0000000..b6ea403 --- /dev/null +++ b/ski-dashboard-model/src/main/java/com/ski/lichuan/model/log/OperationRecord.java @@ -0,0 +1,23 @@ +package com.ski.lichuan.model.log; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@Schema(description = "操作记录") +public class OperationRecord { + + @Schema(description = "操作记录ID") + public Integer id; + @Schema(description = "操作记录操作人") + public String operator; + @Schema(description = "操作记录内容") + public String content; + @Schema(description = "操作记录类型") + public String operationType; + @Schema(description = "操作记录时间") + public LocalDateTime operationTime; + +} diff --git a/ski-dashboard-model/src/main/resources/mapper/SnowMachineMapper.xml b/ski-dashboard-model/src/main/resources/mapper/SnowMachineMapper.xml new file mode 100644 index 0000000..919ecd7 --- /dev/null +++ b/ski-dashboard-model/src/main/resources/mapper/SnowMachineMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + INSERT INTO snow_machine (name, lora) VALUES (#{name}, #{lora}) + + + + + + + + + + + + + + + + + + + + UPDATE snow_machine SET name = #{name} WHERE id = #{id} + + + + + UPDATE snow_machine SET name = #{name}, lora = #{lora} WHERE id = #{id} + + + + + DELETE FROM snow_machine WHERE id = #{id} + + + + + INSERT INTO snow_machine (id, name, lora) VALUES (#{id}, #{name}, #{lora}) + + \ No newline at end of file diff --git a/ski-dashboard-model/src/main/resources/mapper/TrailPositionMapper.xml b/ski-dashboard-model/src/main/resources/mapper/TrailPositionMapper.xml new file mode 100644 index 0000000..48bec6e --- /dev/null +++ b/ski-dashboard-model/src/main/resources/mapper/TrailPositionMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO trail_position (trail_id, position, name) + VALUES (#{trailId}, #{position}, #{name}) + + + + UPDATE trail_position + SET trail_id = #{trailId}, position = #{position}, name = #{name} + WHERE id = #{id} + + + + UPDATE trail_position + SET snow_machine_status = 0, snow_machine_id = NULL + WHERE id = #{id} + + + + UPDATE trail_position + SET snow_machine_id = #{snowMachineId} + WHERE id = #{id} + + + + + + + + UPDATE trail_position + SET snow_machine_status = 0, snow_machine_id = NULL + WHERE id NOT IN + + #{id} + + + + \ No newline at end of file diff --git a/ski-dashboard-service/pom.xml b/ski-dashboard-service/pom.xml index dfcf4ce..8ae20ff 100644 --- a/ski-dashboard-service/pom.xml +++ b/ski-dashboard-service/pom.xml @@ -9,7 +9,6 @@ 0.0.1-SNAPSHOT - com.ski.lichuan ski-dashboard-service @@ -17,6 +16,35 @@ 21 UTF-8 + + + + + thingsboard + ThingsBoard Repository + https://repo.thingsboard.io/artifactory/libs-release-public + + true + + + true + + + + + aliyunmaven + aliyunmaven + https://maven.aliyun.com/repository/public + default + + true + + + true + + + + com.ski @@ -25,11 +53,55 @@ compile - com.ski.lichuan + com.ski ski-dashboard-common 0.0.1-SNAPSHOT compile + + org.thingsboard + rest-client + 4.2.1 + + + + aliyunmaven + aliyunmaven + https://maven.aliyun.com/repository/public + default + + true + + + true + + + + thingsboard + ThingsBoard Repository + https://repo.thingsboard.io/artifactory/libs-release-public + + true + + + true + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + \ No newline at end of file diff --git a/ski-dashboard-service/src/main/java/com/ski/lichuan/Demo.java b/ski-dashboard-service/src/main/java/com/ski/lichuan/Demo.java new file mode 100644 index 0000000..0c92947 --- /dev/null +++ b/ski-dashboard-service/src/main/java/com/ski/lichuan/Demo.java @@ -0,0 +1,119 @@ +package com.ski.lichuan; + +import org.thingsboard.rest.client.RestClient; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.query.*; + +import java.util.ArrayList; +import java.util.List; + +public class Demo { + + public static void main(String[] args) { + + List devicesList = getDevices(); +// List dashboardsList = getDashboards(); + for (Device device : devicesList) { + if (!device.getName().equals("iot_snowmaker_m005")) { + continue; + } + System.out.println("找到指定设备"); + getDataByDevice(device); + } + } + + /** + * 获取ThingsBoard客户端 + * @return + */ + public static List getDevices() { + + String url = "https://tb.ski.bkiiot.com"; + String username = "tenant@thingsboard.org"; + String password = "tenant"; + + List devicesList = new ArrayList<>(); + + RestClient client = new RestClient(url); + client.login(username, password); + + PageData devices; + PageLink pageLink = new PageLink(10); + do { + devices = client.getTenantDevices("", pageLink); + devices.getData().forEach(System.out::println); + devicesList.addAll(devices.getData()); + pageLink = pageLink.nextPageLink(); + } while (devices.hasNext()); + client.logout(); + client.close(); + return devicesList; + } + + /** + * 获取仪表盘 + */ + public static List getDashboards() { + + String url = "https://tb.ski.bkiiot.com"; + String username = "tenant@thingsboard.org"; + String password = "tenant"; + List dashboardsList = new ArrayList<>(); + + RestClient client = new RestClient(url); + client.login(username, password); + PageData dashboards; + PageLink pageLink = new PageLink(10); + do { + dashboards = client.getTenantDashboards(pageLink); + dashboards.getData().forEach(System.out::println); + dashboardsList.addAll(dashboards.getData()); + pageLink = pageLink.nextPageLink(); + } while (dashboards.hasNext()); + client.logout(); + client.close(); + return dashboardsList; + + } + + /** + * 获取设备属性 + */ + public static void getDeviceAttributes(Device device) { + + String url = "https://tb.ski.bkiiot.com"; + String username = "tenant@thingsboard.org"; + String password = "tenant"; + + RestClient client = new RestClient(url); + client.login(username, password); + // 获取属性 + List keys = client.getAttributeKeys(device.getId()); + client.getAttributeKvEntries(device.getId(), keys).forEach(System.out::println); + } + + /** + * 获取设备数据 + */ + public static void getDataByDevice(Device device) { + String url = "https://tb.ski.bkiiot.com"; + String username = "tenant@thingsboard.org"; + String password = "tenant"; + + RestClient client = new RestClient(url); + client.login(username, password); + + client.getLatestTimeseries(device.getId(), List.of()).forEach(System.out::println); + + client.logout(); + client.close(); + + + } + +} diff --git a/ski-dashboard-service/src/main/java/com/ski/lichuan/initializer/AdminUserInitializer.java b/ski-dashboard-service/src/main/java/com/ski/lichuan/initializer/AdminUserInitializer.java index ce02fe3..203635e 100644 --- a/ski-dashboard-service/src/main/java/com/ski/lichuan/initializer/AdminUserInitializer.java +++ b/ski-dashboard-service/src/main/java/com/ski/lichuan/initializer/AdminUserInitializer.java @@ -1,13 +1,16 @@ package com.ski.lichuan.initializer; +import com.ski.lichuan.common.utils.MD5Util; import com.ski.lichuan.mapper.UserMapper; import com.ski.lichuan.model.auth.SysUser; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Component; @Component +@Slf4j public class AdminUserInitializer implements CommandLineRunner { @Autowired @@ -24,15 +27,16 @@ public class AdminUserInitializer implements CommandLineRunner { // 创建默认管理员用户 SysUser adminUser = new SysUser(); adminUser.setUsername("admin"); - // 默认密码为 admin123,实际使用时应修改为更安全的密码 - adminUser.setPassword(passwordEncoder.encode("admin123")); + // 默认密码为 123456,实际使用时应修改为更安全的密码 使用MD5加密 + String text = MD5Util.encrypt("123456"); + adminUser.setPassword(passwordEncoder.encode(text)); adminUser.setNickname("系统管理员"); adminUser.setStatus(1); // 启用状态 // 插入用户到数据库 userMapper.insertUser(adminUser); - System.out.println("默认管理员用户已创建,用户名: admin, 密码: admin123 (请登录后及时修改密码)"); + log.info("默认管理员用户已创建,用户名: admin, 密码: 123456 (请登录后及时修改密码)"); } else { - System.out.println("管理员用户已存在"); + log.info("管理员用户已存在"); } } } \ No newline at end of file diff --git a/ski-dashboard-service/src/main/java/com/ski/lichuan/initializer/TrailInitializer.java b/ski-dashboard-service/src/main/java/com/ski/lichuan/initializer/TrailInitializer.java new file mode 100644 index 0000000..fae654a --- /dev/null +++ b/ski-dashboard-service/src/main/java/com/ski/lichuan/initializer/TrailInitializer.java @@ -0,0 +1,53 @@ +package com.ski.lichuan.initializer; + +import com.ski.lichuan.mapper.TrailPositionMapper; +import com.ski.lichuan.model.dashboard.TrailPosition; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; + +import java.util.List; + +@Slf4j +public class TrailInitializer implements CommandLineRunner { + + @Autowired + private TrailPositionMapper trailPositionMapper; + + @Override + public void run(String... args) throws Exception { + List trailPositions = trailPositionMapper.selectAll(); + if (trailPositions.isEmpty()) { + log.info("数据库中不存在坑位数据,开始初始化..."); + Integer[][] positions = { + {1, 1}, + {1, 2}, + {1, 3}, + {1, 4}, + {2, 1}, + {2, 2}, + {2, 3}, + {2, 4}, + {2, 5}, + {2, 6}, + {3, 1}, + {3, 2}, + {3, 3}, + {3, 4}, + {3, 5}, + {3, 6}, + {4, 1}, + {4, 2}, + {4, 3} + }; + + for (Integer[] position : positions) { + TrailPosition trailPosition = new TrailPosition(); + trailPosition.setTrailId(position[0]); + trailPosition.setPosition(position[1]); + trailPosition.setName(position[0] + "-" + position[1]); + trailPositionMapper.insert(trailPosition); + } + } + } +} diff --git a/ski-dashboard-service/src/main/java/com/ski/lichuan/services/DeviceDataPollingService.java b/ski-dashboard-service/src/main/java/com/ski/lichuan/services/DeviceDataPollingService.java new file mode 100644 index 0000000..7660e71 --- /dev/null +++ b/ski-dashboard-service/src/main/java/com/ski/lichuan/services/DeviceDataPollingService.java @@ -0,0 +1,177 @@ +package com.ski.lichuan.services; + +import com.ski.lichuan.model.dashboard.TrailPosition; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.thingsboard.rest.client.RestClient; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class DeviceDataPollingService { + + // 高优先级查询列表(造雪机) + public static final List HIGH_PRIORITY_SNOWMACHINE_IDS = Arrays.asList(); + // 高优先级查询列表(水泵) + public static final List HIGH_PRIORITY_PUMP_IDS = Arrays.asList(); + @Value("${thingsboard.client.url:https://tb.ski.bkiiot.com}") + private String clientUrl; + @Value("${thingsboard.client.username:tenant@thingsboard.org}") + private String clientUsername; + @Value("${thingsboard.client.password:tenant}") + private String clientPassword; + // 客户端 + private RestClient client; + + @Autowired + private TrailPositionService trailPositionService; + + @Autowired + private SnowMachineDeviceService snowMachineDeviceService; + + /** + * 定时轮询设备状态 + */ + @Scheduled(fixedRate = 60000) + public void pollDeviceStatus() { + /** + * 核心逻辑:每60秒轮询一次所有设备状态,查询所有坑位上的雪机和水泵的状态 + */ + + } + + + /** + * 定时轮询设备数据 + * 每15秒执行一次 + */ + @Scheduled(fixedRate = 15000) + public void pollDeviceData() { + /** + * 核心逻辑:每15秒轮询一次所有设备数据,查询所有坑位上的雪机和水泵的状态 + */ + + } + + /** + * 定时轮询设备列表(造雪机、水泵) + * 每120秒执行一次 + */ + @Scheduled(fixedRate = 120000) + public void pollDevices() { + /** + * 核心逻辑:每120秒轮询一次所有设备列表,更新造雪机设备和水泵 + */ + log.info("开始查询设备列表"); + List devices = getDevices(); + + List trailPositions = trailPositionService.getAllTrailPositionList(); + // 转为Map,方便查询 (TrailId,Position) -> TrailPosition + Map trailPositionMap = trailPositions.stream() + .collect(Collectors.toMap(trailPosition -> trailPosition.getTrailId() * 10000 + trailPosition.getPosition(), trailPosition -> trailPosition)); + + for (Device device : devices) { + //s-{id}-{trailId}-{position}-{disable} + if (device.getLabel() != null && device.getLabel().startsWith("s-")) { + String[] parts = device.getLabel().split("-"); + if (parts.length < 3) { + // 异常的设备 + log.warn("异常的造雪机设备标签格式:{}", device.getLabel()); + continue; + } + if (parts.length >= 4 && "disable".equals(parts[3])) { + // 禁用状态,跳过 + log.info("造雪机设备 {} 已禁用,跳过", device.getLabel()); + continue; + } + // 说明是造雪机设备 + String id = parts[1]; + String trailId = parts[2]; + String position = parts[3]; + + // 判断是数字 + if (!trailId.matches("\\d+") || !position.matches("\\d+")) { + // 不是数字,跳过 + log.warn("造雪机设备 {} 标签格式错误,跳过", device.getLabel()); + continue; + } + // id 不为空 + if (id.isEmpty()) { + // 空id,跳过 + log.warn("造雪机设备 {} 标签格式错误,跳过", device.getLabel()); + continue; + } + + Integer idValue = Integer.parseInt(id); + Integer trailIdValue = Integer.parseInt(trailId); + Integer positionValue = Integer.parseInt(position); + + // 更新雪机信息 + String name = device.getName() != null ? device.getName() : "雪机" + trailId + position; + try { + snowMachineDeviceService.updateSnowMachineDeviceNameAndLora(idValue, name, device.getId().toString()); + log.info("更新造雪机设备信息 {} 成功", device.getLabel()); + } catch (Exception e) { + log.error("更新造雪机设备信息 {} 失败:{}", device.getLabel(), e.getMessage()); + } + + // 检查是否存在对应的雪道位置 + TrailPosition trailPosition = trailPositionMap.get(trailIdValue * 10000 + positionValue); + if (trailPosition == null) { + // 雪道位置不存在,跳过 + log.warn("造雪机设备 {} 对应的雪道位置不存在,跳过", device.getLabel()); + continue; + } + + // 更新雪道位置的造雪机ID + trailPosition.setSnowMachineId(idValue); + trailPositionService.updateTrailPositionSnowMachineId(trailPosition.getId(), idValue); + // 从Map中删除该雪道位置,防止重复处理 + trailPositionMap.remove(trailIdValue * 10000 + positionValue); + + } + } + + // 清楚Map中剩余的雪道位置 + List mapIdList = trailPositionMap.values().stream().map(TrailPosition::getId).collect(Collectors.toList()); + try { + trailPositionService.clearTrailPositionSnowMachineIdBatch(mapIdList); + } catch (Exception e) { + log.error("清除雪道位置 {} 造雪机ID失败:{}", mapIdList, e.getMessage()); + } + } + + private RestClient getClient() { + if (client == null) { + client = new RestClient(clientUrl); + client.login(clientUsername, clientPassword); + } + return client; + } + + public List getDevices() { + RestClient client = getClient(); + List devicesList = new ArrayList<>(); + + PageData devices; + PageLink pageLink = new PageLink(10); + do { + devices = client.getTenantDevices("", pageLink); + devices.getData().forEach(device -> log.info("获取到的造雪机设备:{}", device)); + devicesList.addAll(devices.getData()); + pageLink = pageLink.nextPageLink(); + } while (devices.hasNext()); + return devicesList; + } +} \ No newline at end of file diff --git a/ski-dashboard-service/src/main/java/com/ski/lichuan/services/IoTService.java b/ski-dashboard-service/src/main/java/com/ski/lichuan/services/IoTService.java new file mode 100644 index 0000000..61f838a --- /dev/null +++ b/ski-dashboard-service/src/main/java/com/ski/lichuan/services/IoTService.java @@ -0,0 +1,4 @@ +package com.ski.lichuan.services; + +public class IoTService { +} diff --git a/ski-dashboard-service/src/main/java/com/ski/lichuan/services/SnowMachineDeviceService.java b/ski-dashboard-service/src/main/java/com/ski/lichuan/services/SnowMachineDeviceService.java new file mode 100644 index 0000000..627fba3 --- /dev/null +++ b/ski-dashboard-service/src/main/java/com/ski/lichuan/services/SnowMachineDeviceService.java @@ -0,0 +1,158 @@ +package com.ski.lichuan.services; + +import com.ski.lichuan.mapper.SnowMachineMapper; +import com.ski.lichuan.model.device.Impl.SnowMachineDevice; +import com.ski.lichuan.model.device.config.SnowMachineDeviceParams; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class SnowMachineDeviceService { + + @Autowired + private SnowMachineMapper snowMachineMapper; + + /** + * 新增造雪机设备 + * + * @param snowMachineDevice 造雪机设备 + * @return 新增结果 + */ + public int addSnowMachineDevice(SnowMachineDevice snowMachineDevice) { + // 检查是否已存在相同LORA地址的造雪机设备 + if (snowMachineMapper.selectByLora(snowMachineDevice.getLora()) != null) { + throw new IllegalArgumentException("造雪机设备已存在相同LORA地址"); + } + return snowMachineMapper.insert(snowMachineDevice); + } + + /** + * 查询所有未绑定位置的造雪机设备 + * + * @return 未绑定位置的造雪机设备列表 + */ + public List selectUnboundMachines() { + return snowMachineMapper.selectUnboundMachines(); + } + + + /** + * 查询所有造雪机设备 + * + * @return 所有造雪机设备列表 + */ + public List getAllSnowMachineDevices() { + return snowMachineMapper.selectAll(); + } + + /** + * 根据ID查询造雪机设备 + * + * @param id 造雪机设备ID + * @return 造雪机设备 + */ + public SnowMachineDevice getSnowMachineDeviceById(Integer id) { + // 检查是否存在该ID的造雪机设备 + if (snowMachineMapper.selectById(id) == null) { + throw new IllegalArgumentException("造雪机设备不存在"); + } + return snowMachineMapper.selectById(id); + } + + /** + * 根据LORA地址查询造雪机设备 + * + * @param lora 造雪机设备LORA地址 + * @return 造雪机设备 + */ + public SnowMachineDevice selectByLora(String lora) { + return snowMachineMapper.selectByLora(lora); + } + /** + * 统计造雪机设备数量 + * + * @return 造雪机设备数量 + */ + public int count() { + return snowMachineMapper.count(); + } + + /** + * 更新造雪机设备名称 + * + * @param snowMachineDevice 造雪机设备 + * @return 更新结果 + */ + public int updateSnowMachineDevice(SnowMachineDevice snowMachineDevice) { + if (snowMachineMapper.selectById(snowMachineDevice.getId()) == null) { + throw new IllegalArgumentException("造雪机设备不存在"); + } + return snowMachineMapper.updateName(snowMachineDevice); + } + + /** + * 更新造雪机信息 + * + * @param id 造雪机设备ID + * @param name 造雪机设备名称 + * @param lora 造雪机设备LORA地址 + * @return 更新结果 + */ + public int updateSnowMachineDeviceNameAndLora(Integer id, String name, String lora) { + // 检查是否存在该ID的造雪机设备 + if (snowMachineMapper.selectById(id) == null) { + // 则新增 + SnowMachineDevice newSnowMachineDevice = new SnowMachineDevice(); + newSnowMachineDevice.setId(id); + newSnowMachineDevice.setName(name); + newSnowMachineDevice.setLora(lora); + return snowMachineMapper.insertById(newSnowMachineDevice); + } else { + // 更新造雪机设备信息 + SnowMachineDevice snowMachineDevice = snowMachineMapper.selectById(id); + snowMachineDevice.setName(name); + snowMachineDevice.setLora(lora); + return snowMachineMapper.updateName(snowMachineDevice); + } + } + + /** + * 删除造雪机设备 + * + * @param id 造雪机设备ID + * @return 删除结果 + */ + public int deleteSnowMachineDevice(Integer id) { + return snowMachineMapper.deleteById(id); + } + + /** + * 获取所有雪机设备信息 + * + * @return 所有雪机设备列表 + */ + public List selectAllDevices() { + return snowMachineMapper.selectAll(); + } + + /** + * 群控雪机设备 + * @param deviceList 雪机设备列表 + * @param params 造雪机设备参数 + * @return 群控结果 + */ + public boolean groupControl(List deviceList, SnowMachineDeviceParams params) { + // 检查是否存在该ID的造雪机设备 + if (deviceList.isEmpty()) { + throw new IllegalArgumentException("造雪机设备不存在"); + } + // 群控雪机设备 + for (SnowMachineDevice device : deviceList) { + // 发送群控指令 + // ... + } + return true; + } +} diff --git a/ski-dashboard-service/src/main/java/com/ski/lichuan/services/TrailPositionService.java b/ski-dashboard-service/src/main/java/com/ski/lichuan/services/TrailPositionService.java new file mode 100644 index 0000000..b92fd52 --- /dev/null +++ b/ski-dashboard-service/src/main/java/com/ski/lichuan/services/TrailPositionService.java @@ -0,0 +1,310 @@ +package com.ski.lichuan.services; + +import com.ski.lichuan.mapper.SnowMachineMapper; +import com.ski.lichuan.mapper.TrailPositionMapper; +import com.ski.lichuan.model.common.DeviceEnum; +import com.ski.lichuan.model.dashboard.*; +import com.ski.lichuan.model.device.Impl.SnowMachineDevice; +import com.ski.lichuan.model.device.config.SnowMachineDeviceParams; +import com.ski.lichuan.model.device.config.SnowMachineDevicePitchHorizontalParams; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class TrailPositionService { + + @Autowired + private TrailPositionMapper trailPositionMapper; + + @Autowired + private SnowMachineMapper snowMachineDeviceMapper; + + /** + * 获取所有已绑定雪机的雪道位置信息 + */ + public List getBoundTrailPositions() { + return trailPositionMapper.selectBoundPositions(); + } + + /** + * 获取所有未绑定雪机的雪道位置信息 + */ + public List getUnboundTrailPositions() { + return trailPositionMapper.selectUnboundPositions(); + } + + /** + * 获取所有雪道及位置信息 + */ + public List> getAllTrailPositions() { + List allTrailPositions = trailPositionMapper.selectAll(); + List> result = new ArrayList<>(); + for (int i = 1; i <= 4; i++) { + int finalI = i; + List trailPositions = allTrailPositions.stream() + .filter(trailPosition -> trailPosition.getTrailId().equals(finalI)) + .collect(Collectors.toList()); + result.add(trailPositions); + } + // 遍历result,将每个雪道的位置信息按position排序 + for (List trailPositions : result) { + // 更新trailPosition状态 + for (TrailPosition trailPosition : trailPositions) { + if (trailPosition.getSnowMachineStatus() == 0) { + // 暂时随机 + trailPosition.setSnowMachineStatus((int) (Math.random() * 3)); + } + } + + trailPositions.sort((p1, p2) -> p1.getPosition().compareTo(p2.getPosition())); + } + + return result; + } + + /** + * 获取雪道位置的详细信息(造雪机) + */ + public TrailPositionInfo getTrailPositionInfo(Integer id) { + TrailPosition trailPosition = trailPositionMapper.selectById(id); + if (trailPosition == null) { + return null; + } else { + TrailPositionInfo result = new TrailPositionInfo(); + + Integer trailId = id / 10; + Integer position = id % 10; + + // 雪机设备 + SnowMachineDevice snowMachineDevice = new SnowMachineDevice(); + snowMachineDevice.setLora("1234567890123456"); + snowMachineDevice.setId(1000000000 + id); + snowMachineDevice.setName("造雪机-" + id); + snowMachineDevice.setType(DeviceEnum.SNOW_MAKER.getValue()); + // 雪道位置信息 + trailPosition.setSnowMachineStatus(1); + // 雪道位置状态信息 + TrailPositionMonitorInfo trailPositionMonitorInfo = new TrailPositionMonitorInfo(); + trailPositionMonitorInfo.setTrailPosition(trailPosition); + trailPositionMonitorInfo.setSnowMakerName(snowMachineDevice.getName()); + + trailPositionMonitorInfo.setPriority(1); + trailPositionMonitorInfo.setEnvironmentTemperature(-3.5); + trailPositionMonitorInfo.setEnvironmentHumidity(50.0); + trailPositionMonitorInfo.setWetBulbTemperature(0.5); + trailPositionMonitorInfo.setCommunicationStatus(true); + trailPositionMonitorInfo.setIsNormal(true); + trailPositionMonitorInfo.setIsRunning(true); + trailPositionMonitorInfo.setTemperatureConditionSatisfied(true); + trailPositionMonitorInfo.setWindSpeed(5.0); + trailPositionMonitorInfo.setWindDirection(180.0); + // 造雪机设备状态 + SnowMachineDeviceInfo snowMachineDeviceInfo = new SnowMachineDeviceInfo(); + snowMachineDeviceInfo.setSnowMachineDevice(snowMachineDevice); + snowMachineDeviceInfo.setFrontWaterPressure(0.5 + Math.random() * 0.3); // 前端水压 0.5-0.8 MPa + snowMachineDeviceInfo.setBackWaterPressure(0.4 + Math.random() * 0.2); // 后端水压 0.4-0.6 MPa + snowMachineDeviceInfo.setPressure(0.8 + Math.random() * 0.2); // 气压 0.8-1.0 MPa + snowMachineDeviceInfo.setTemperature(2.5 + Math.random() * 2.0); // 水温 2.5-4.5 ℃ + snowMachineDeviceInfo.setGivenAngle(45.0 + Math.random() * 10.0); // 给定开度 45-55° + snowMachineDeviceInfo.setActualAngle(44.0 + Math.random() * 12.0); // 实际开度 44-56° + snowMachineDeviceInfo.setIsNormal(Math.random() > 0.1); // 是否正常 90%概率正常 + snowMachineDeviceInfo.setMode((int) (Math.random() * 3) + 1); // 模式 1-3随机 + snowMachineDeviceInfo.setCompressorStatus((int) (Math.random() * 2) + 1); // 空压机状态 1-2随机 + snowMachineDeviceInfo.setFanStatus((int) (Math.random() * 2) + 1); // 风机状态 1-2随机 + snowMachineDeviceInfo.setHeatingRingStatus((int) (Math.random() * 2) + 1); // 加热环状态 1-2随机 + snowMachineDeviceInfo.setValveStatus((int) (Math.random() * 2) + 1); // 电磁阀状态1 1-2随机 + snowMachineDeviceInfo.setValveStatus2((int) (Math.random() * 2) + 1); // 电磁阀状态2 1-2随机 + snowMachineDeviceInfo.setValveStatus3((int) (Math.random() * 2) + 1); // 电磁阀状态3 1-2随机 + snowMachineDeviceInfo.setPitchMode((int) (Math.random() * 3) + 1); // 俯仰模式 1-3随机 + snowMachineDeviceInfo.setPitchAngle(15.0 + Math.random() * 20.0); // 俯仰角度 15-35° + snowMachineDeviceInfo.setPitchStatus((int) (Math.random() * 2) + 1); // 俯仰状态 1-2随机 + snowMachineDeviceInfo.setYawMode((int) (Math.random() * 3) + 1); // 摆头模式 1-3随机 + snowMachineDeviceInfo.setYawAngle(30.0 + Math.random() * 40.0); // 摆头角度 30-70° + snowMachineDeviceInfo.setYawStatus((int) (Math.random() * 2) + 1); // 摆头状态 1-2随机 + // 造雪机配置 + SnowMachineDeviceParams snowMakerDeviceParams = new SnowMachineDeviceParams(); + snowMakerDeviceParams.setWorkMode((int) (Math.random() * 3) + 1); // 工作模式 1-3随机 + snowMakerDeviceParams.setPriority((int) (Math.random() * 5) + 1); // 优先级 1-5随机 + snowMakerDeviceParams.setSnowQuality((int) (Math.random() * 5) + 1); // 雪质 1-5随机 + snowMakerDeviceParams.setStartTemperature(-5.0 + Math.random() * 10.0); // 启动温度 -5.0 ~ 5.0 + snowMakerDeviceParams.setStopTemperature(-5.0 + Math.random() * 10.0); // 停止温度 -5.0 ~ 5.0 + snowMakerDeviceParams.setFlowAdjustment((int) (Math.random() * 50)); // 流量调节 0 ~ 50 + + // 数据统计 + TrailPositionStatisticsInfo trailPositionStatisticsInfo = new TrailPositionStatisticsInfo(); + trailPositionStatisticsInfo.setRunningDuration(120.5 + Math.random() * 100.0); // 运行时长 120.5-220.5分钟 + trailPositionStatisticsInfo.setFlowStatistics(1000.0 + Math.random() * 500.0); // 流量统计 1000-1500 L/H + trailPositionStatisticsInfo.setSnowArea(500.0 + Math.random() * 300.0); // 造雪面积 500-800 平方米 + + result.setTrailPositionMonitorInfo(trailPositionMonitorInfo); + result.setSnowMakerInfo(snowMachineDeviceInfo); + result.setSnowMakerDeviceParams(snowMakerDeviceParams); + result.setTrailPositionStatisticsInfo(trailPositionStatisticsInfo); + + return result; + } + + } + + + /** + * 关联造雪机设备 + */ + public boolean connectDevice(Integer trailPositionId, Integer deviceId) { + TrailPosition trailPosition = trailPositionMapper.selectById(trailPositionId); + if (trailPosition == null) { + return false; + } + SnowMachineDevice snowMachineDevice = snowMachineDeviceMapper.selectById(deviceId); + if (snowMachineDevice == null) { + return false; + } + trailPosition.setSnowMachineId(deviceId); + trailPosition.setSnowMachineStatus(1); + trailPositionMapper.updateSnowMachine(trailPositionId, trailPosition.getSnowMachineId()); + return true; + } + + /** + * 取消关联造雪机设备 + */ + public boolean disconnectDevice(Integer trailPositionId) { + TrailPosition trailPosition = trailPositionMapper.selectById(trailPositionId); + if (trailPosition == null) { + return false; + } + trailPosition.setSnowMachineId(null); + trailPosition.setSnowMachineStatus(0); + trailPositionMapper.clearSnowMachine(trailPositionId); + return true; + } + + /** + * 群控造雪机设备 + * + * @param trailPositionIds 雪道位置ID列表 + * @param params 造雪机设备参数 + * @return 群控结果 + */ + public boolean groupControl(List trailPositionIds, SnowMachineDeviceParams params) { + // 检查是否存在该ID的造雪机设备 + if (trailPositionIds.isEmpty()) { + throw new IllegalArgumentException("雪道位置不存在"); + } + // 反查造雪机设备ID + List deviceIds = trailPositionMapper.selectDeviceIdsByTrailPositionIds(trailPositionIds); + if (deviceIds.isEmpty()) { + throw new IllegalArgumentException("造雪机设备不存在"); + } + // 群控雪机设备 + for (Integer deviceId : deviceIds) { + // 发送群控指令 + // ... + } + return true; + } + + + /** + * 俯仰水平控制 + * + * @param trailPositionId 雪道位置ID + * @param params 俯仰水平参数 + * @return 控制结果 + */ + public Boolean pitchHorizontalControl(Integer trailPositionId, SnowMachineDevicePitchHorizontalParams params) { + // 检查是否存在该ID的造雪机设备 + TrailPosition trailPosition = trailPositionMapper.selectById(trailPositionId); + if (trailPosition == null) { + throw new IllegalArgumentException("雪道位置不存在"); + } + // 反查造雪机设备ID + SnowMachineDevice snowMachineDevice = snowMachineDeviceMapper.selectById(trailPosition.getSnowMachineId()); + if (snowMachineDevice == null) { + throw new IllegalArgumentException("造雪机设备不存在"); + } + + // 发送俯仰水平控制指令 + // ... + return true; + } + + /** + * 保存造雪机参数 + * + * @param trailPosition 雪道位置ID + * @param params 造雪机设备参数 + * @return 保存结果 + */ + public Boolean saveSnowMakerDeviceParams(Integer trailPosition, SnowMachineDeviceParams params) { + // 检查是否存在该ID的造雪机设备 + TrailPosition trailPositionEntity = trailPositionMapper.selectById(trailPosition); + if (trailPositionEntity == null) { + throw new IllegalArgumentException("雪道位置不存在"); + } + // 反查造雪机设备ID + SnowMachineDevice snowMachineDevice = snowMachineDeviceMapper.selectById(trailPositionEntity.getSnowMachineId()); + if (snowMachineDevice == null) { + throw new IllegalArgumentException("造雪机设备不存在"); + } + // 保存造雪机参数 + // ... + return true; + } + + /** + * 更新雪道坑位雪机信息 + * + * @param trailPositionId 雪道位置ID + * @param snowMachineId 造雪机设备ID + * @return 更新结果 + */ + public Boolean updateTrailPositionSnowMachineId(Integer trailPositionId, Integer snowMachineId) { + // 检查是否存在该ID的雪道位置 + TrailPosition trailPosition = trailPositionMapper.selectById(trailPositionId); + if (trailPosition == null) { + throw new IllegalArgumentException("雪道位置不存在"); + } + // 更新雪道位置雪机信息 + trailPosition.setSnowMachineId(snowMachineId); + trailPositionMapper.updateSnowMachine(trailPositionId, snowMachineId); + return true; + } + + /** + * 清空雪道位置雪机信息 + * + * @param trailPositionIds 雪道位置ID列表 + * @return 更新结果 + */ + public Boolean clearTrailPositionSnowMachineIdBatch(List trailPositionIds) { + // 检查是否存在该ID的雪道位置 + if (trailPositionIds.isEmpty()) { + throw new IllegalArgumentException("雪道位置不存在"); + } + // 清空雪道位置雪机信息 + for (Integer trailPositionId : trailPositionIds) { + TrailPosition trailPosition = trailPositionMapper.selectById(trailPositionId); + if (trailPosition == null) { + throw new IllegalArgumentException("雪道位置不存在"); + } + trailPosition.setSnowMachineId(null); + trailPosition.setSnowMachineStatus(0); + trailPositionMapper.clearSnowMachine(trailPositionId); + } + return true; + } + + /** + * 平铺返回所有雪道位置信息 + * + * @return 所有雪道位置信息列表 + */ + public List getAllTrailPositionList() { + return trailPositionMapper.selectAll(); + } +} diff --git a/ski-dashboard-service/src/main/resources/sql/sys_user.sql b/ski-dashboard-service/src/main/resources/sql/sys_user.sql index 369c618..dcd3ecb 100644 --- a/ski-dashboard-service/src/main/resources/sql/sys_user.sql +++ b/ski-dashboard-service/src/main/resources/sql/sys_user.sql @@ -10,4 +10,33 @@ CREATE TABLE sys_user ( COMMENT ON COLUMN sys_user.username IS '用户名'; COMMENT ON COLUMN sys_user.password IS '加密后的密码(BCrypt)'; COMMENT ON COLUMN sys_user.nickname IS '昵称'; -COMMENT ON COLUMN sys_user.status IS '状态(1-正常,0-禁用)'; \ No newline at end of file +COMMENT ON COLUMN sys_user.status IS '状态(1-正常,0-禁用)'; + +CREATE TABLE snow_machine ( + id BIGSERIAL PRIMARY KEY, + type SMALLINT DEFAULT 1, + name VARCHAR(255), + lora VARCHAR(255), + CONSTRAINT uk_lora UNIQUE (lora) +); + +COMMENT ON COLUMN snow_machine.type IS '类型1雪机2水泵'; +COMMENT ON COLUMN snow_machine.name IS '名称'; +COMMENT ON COLUMN snow_machine.lora IS 'Lora 地址'; + +CREATE TABLE trail_position ( + id BIGSERIAL PRIMARY KEY, + trail_id BIGINT NOT NULL, + position SMALLINT, + trail_name VARCHAR(255), + snow_machine_id BIGINT, + snow_machine_status SMALLINT DEFAULT 0, +); + +COMMENT ON COLUMN trail_position.trail_id IS '雪道ID'; +COMMENT ON COLUMN trail_position.position IS '位置'; +COMMENT ON COLUMN trail_position.trail_name IS '雪道名称'; +COMMENT ON COLUMN trail_position.snow_machine_id IS '雪机ID'; +COMMENT ON COLUMN trail_position.snow_machine_status IS '雪机状态 0无 1正常 2停止'; + + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index f1f6fbd..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1,7 +0,0 @@ -spring.application.name=ski-dashboard -# ??????? postgresql -spring.datasource.url=jdbc:postgresql://localhost:5432/ski_dashboard -spring.datasource.username=postgres -spring.datasource.password=tanlifan - -JWT_SECRET=vf4JZhcyfdK7tJs0GZ3Qjf0dSv4BId9ITjsM2fol26gOBxM17nUySiMcV0Lo2u0Y