master
shiyi 1 month ago
commit f104f23c66

35
.gitignore vendored

@ -0,0 +1,35 @@
README.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
/log

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.16</version>
</parent>
<groupId>com.htfp.service</groupId>
<artifactId>usm</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>htfp-uav-state-manager</name>
<description>htfp-uav-state-manager</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.7.6</spring-boot.version>
</properties>
<modules>
<module>usm-app</module>
<module>usm-dao</module>
<module>usm-common</module>
<module>usm-biz</module>
</modules>
<!-- 依赖版本声明, 子项目需引入但无需指定版本-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<!-- 合并导入spring-boot-dependencies的dependencies-->
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.htfp.service</groupId>
<artifactId>usm-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.htfp.service</groupId>
<artifactId>usm-dao</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.htfp.service</groupId>
<artifactId>usm-biz</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--公共依赖引入, 子项目直接继承,无需引入-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.htfp.service.ssm.HtfpUavStateManagerApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.htfp.service</groupId>
<artifactId>usm</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.htfp.service</groupId>
<artifactId>usm-app</artifactId>
<packaging>jar</packaging>
<name>usm-app</name>
<description>usm-app</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<!-- 内部依赖-->
<dependency>
<groupId>com.htfp.service</groupId>
<artifactId>usm-biz</artifactId>
</dependency>
<dependency>
<groupId>com.htfp.service</groupId>
<artifactId>usm-common</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,17 @@
package com.htfp.service.usm.app;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(value = {"com.htfp.service.usm"})
@MapperScan(value = "com.htfp.service.usm.dao.mapper")
public class UsmAppApplication {
public static void main(String[] args) {
SpringApplication.run(UsmAppApplication.class, args);
}
}

@ -0,0 +1,37 @@
package com.htfp.service.usm.app.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-09 18:21
**/
@Configuration
public class AppCorsFilter {
@Bean
public CorsFilter corsFilter() {
// 1.创建 CORS 配置对象
CorsConfiguration config = new CorsConfiguration();
// 支持域
config.addAllowedOriginPattern("*");
// 是否发送 Cookie
config.setAllowCredentials(true);
// 支持请求方式
config.addAllowedMethod("*");
// 允许的原始请求头部信息
config.addAllowedHeader("*");
// 暴露的头部信息
config.addExposedHeader("*");
// 2.添加地址映射
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**", config);
// 3.返回 CorsFilter 对象
return new CorsFilter(corsConfigurationSource);
}
}

@ -0,0 +1,76 @@
package com.htfp.service.usm.app.controller.manage;
import com.htfp.service.usm.app.model.BaseHttpResponse;
import com.htfp.service.usm.biz.model.request.BatchDeleteLabelRequest;
import com.htfp.service.usm.biz.model.request.InsertLabelRequest;
import com.htfp.service.usm.biz.model.request.UpdateLabelRequest;
import com.htfp.service.usm.biz.model.response.LabelVO;
import com.htfp.service.usm.biz.service.ILabelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 09:00
**/
/**
*
*/
@Slf4j
@RestController
@RequestMapping("/manage/label")
public class LabelController {
@Resource
private ILabelService labelService;
/**
*
*/
@RequestMapping(value = "/queryAllLabels", method = RequestMethod.POST)
public BaseHttpResponse<List<LabelVO>> queryAllLabels() {
List<LabelVO> labelList= labelService.queryAllLabels();
log.info("查询所有标签, 结果数量: {}", labelList.size());
return BaseHttpResponse.success(labelList);
}
/**
*
*/
@RequestMapping(value = "/insertLabel", method = RequestMethod.POST)
public BaseHttpResponse<Integer> insertLabel(@RequestBody @Valid InsertLabelRequest insertLabelRequest) {
log.info("新增标签: {}", insertLabelRequest);
Integer id = labelService.insertLabel(insertLabelRequest);
return BaseHttpResponse.success(id);
}
/**
*
*/
@RequestMapping(value = "/batchDeleteLabel", method = RequestMethod.POST)
public BaseHttpResponse<Void> batchDeleteLabel(@RequestBody @Valid BatchDeleteLabelRequest batchDeleteLabelRequest) {
List<Integer> labelIdList = batchDeleteLabelRequest.getLabelIdList();
log.info("批量删除标签: {}", labelIdList);
labelService.batchDeleteLabels(labelIdList);
return BaseHttpResponse.success();
}
/**
*
*/
@RequestMapping(value = "/updateLabel", method = RequestMethod.POST)
public BaseHttpResponse<Void> updateLabel(@RequestBody @Validated UpdateLabelRequest updateLabelRequest) {
log.info("修改标签: {}", updateLabelRequest);
labelService.updateLabel(updateLabelRequest);
return BaseHttpResponse.success();
}
}

@ -0,0 +1,70 @@
package com.htfp.service.usm.app.controller.manage;
import com.htfp.service.usm.app.model.BaseHttpResponse;
import com.htfp.service.usm.biz.model.request.InsertUavInfoRequest;
import com.htfp.service.usm.biz.model.request.QueryUavInfoByIdRequest;
import com.htfp.service.usm.biz.service.IUavInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-09 18:17
**/
/**
*
*/
@Slf4j
@RestController
@RequestMapping("/manage/uav")
public class UavInfoController {
@Autowired
private IUavInfoService uavInfoService;
/**
* iduav
* @param insertUavInfoRequest uav
* @return uav
*/
@RequestMapping(value = "/queryUavInfoById", method = RequestMethod.POST)
public BaseHttpResponse queryUavInfoById(@RequestBody @Validated QueryUavInfoByIdRequest insertUavInfoRequest) {
log.info("新增无人机: {}", insertUavInfoRequest);
Integer uavId = insertUavInfoRequest.getUavId();
uavInfoService.queryUavInfoById(uavId);
return BaseHttpResponse.success();
}
// /**
// * 根据id查询uav信息
// * @param insertUavInfoRequest 录入uav信息请求体
// * @return 录入uav信息响应体
// */
// @RequestMapping(value = "/queryUavInfoBySn", method = RequestMethod.POST)
// public BaseHttpResponse queryUavInfoBySn(@RequestBody @Validated QueryUavInfoBySnRequest insertUavInfoRequest) {
// log.info("新增无人机: {}", insertUavInfoRequest);
// return BaseHttpResponse.success();
// }
//
//
/**
* uav
* @param insertUavInfoRequest uav
* @return uav
*/
@RequestMapping(value = "/insertUavInfo", method = RequestMethod.POST)
public BaseHttpResponse insertUavInfo(@RequestBody InsertUavInfoRequest insertUavInfoRequest) {
log.info("新增无人机: {}", insertUavInfoRequest);
uavInfoService.insertUavInfo(insertUavInfoRequest);
return BaseHttpResponse.success();
}
}

@ -0,0 +1,94 @@
package com.htfp.service.usm.app.handler;
import com.htfp.service.usm.app.model.BaseHttpResponse;
import com.htfp.service.usm.biz.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
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 javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.sql.SQLException;
import java.util.stream.Collectors;
import com.htfp.service.usm.common.enums.ErrorCodeEnum;
import org.springframework.web.method.HandlerMethod;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-11 11:33
**/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 参数校验失败:@Valid/@Validated + @RequestBody
@ExceptionHandler(MethodArgumentNotValidException.class)
public BaseHttpResponse<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HandlerMethod handlerMethod) {
StringBuilder errMsgBuilder = new StringBuilder();
for (FieldError error : ex.getBindingResult().getFieldErrors()) {
errMsgBuilder
// .append(error.getField())
// .append(": ")
.append(error.getDefaultMessage()).append("; ");
}
// 获取 controller 类名 + 方法名
String beanName = handlerMethod.getBeanType().getName();
String methodName = handlerMethod.getMethod().getName();
String errMsg = errMsgBuilder.toString();
log.warn("参数校验失败:\n Path: {} - {},\n 错误信息: {}", beanName, methodName, errMsg);
return BaseHttpResponse.fail(ErrorCodeEnum.PARAM_VALIDATION_ERROR, errMsg);
}
// 参数校验失败:@Validated + @RequestParam / @PathVariable
@ExceptionHandler(ConstraintViolationException.class)
public BaseHttpResponse<Void> handleConstraintViolationException(ConstraintViolationException ex, HandlerMethod handlerMethod) {
String message = ex.getConstraintViolations()
.stream()
.map(v -> v.getPropertyPath() + ": " + v.getMessage())
.collect(Collectors.joining("; "));
String beanName = handlerMethod.getBeanType().getName();
String methodName = handlerMethod.getMethod().getName();
log.warn("路径参数校验失败:\n Path: {} - {},\n 错误信息: {}", beanName, methodName, message);
return BaseHttpResponse.fail(ErrorCodeEnum.PARAM_VALIDATION_ERROR, message);
}
@ExceptionHandler(BusinessException.class)
public BaseHttpResponse<Void> handleBusinessException(BusinessException ex, HandlerMethod handlerMethod) {
ErrorCodeEnum codeEnum = ex.getErrorCodeEnum();
String content = ex.getcontent();
String finalMessage;
String beanName = handlerMethod.getBeanType().getName();
String methodName = handlerMethod.getMethod().getName();
BaseHttpResponse<Void> response;
if (!StringUtils.hasLength(content)) {
response = BaseHttpResponse.fail(codeEnum);
} else {
finalMessage = String.format("%s: %s", codeEnum.getDesc(), content);
response = BaseHttpResponse.fail(codeEnum, finalMessage);
}
log.warn("业务异常:\n Path: {} - {},\n 错误信息: {}", beanName, methodName, response);
return response;
}
@ExceptionHandler(SQLException.class)
public BaseHttpResponse<Void> handleSQLException(SQLException ex, HandlerMethod handlerMethod) {
String beanName = handlerMethod.getBeanType().getName();
String methodName = handlerMethod.getMethod().getName();
log.error("数据库异常:\n Path: {} - {},\n 错误信息: {}", beanName, methodName, ex.getMessage());
return BaseHttpResponse.fail(ErrorCodeEnum.SQL_ERROR, "服务器内部错误,请联系管理员");
}
// 系统异常兜底
@ExceptionHandler(Exception.class)
public BaseHttpResponse<Void> handleSystemException(Exception ex, HttpServletRequest request) {
log.error("系统异常, path: {}, error: {}", request.getRequestURI(), ex.getMessage(), ex);
return BaseHttpResponse.fail(ErrorCodeEnum.SYSTEM_ERROR, ex.getMessage());
}
}

@ -0,0 +1,85 @@
package com.htfp.service.usm.app.model;
import com.htfp.service.usm.common.enums.ErrorCodeEnum;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.apache.ibatis.annotations.Select;
/**
* @Author shi_y
* @Date 2025-06-11
* @Description http, Controller
*/
@Data
public class BaseHttpResponse<T> {
private boolean success;
private Integer code;
private String message;
private T data;
public BaseHttpResponse() {
}
public BaseHttpResponse(boolean success, Integer code, String message, T data) {
this.success = success;
this.code = code;
this.message = message;
this.data = data;
}
public BaseHttpResponse(boolean success, Integer code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
public BaseHttpResponse(boolean success, ErrorCodeEnum errorCodeEnum) {
this.success = success;
this.code = errorCodeEnum.getCode();
this.message = errorCodeEnum.getDesc();
}
public BaseHttpResponse(boolean success, ErrorCodeEnum errorCodeEnum, T data) {
this.success = success;
this.code = errorCodeEnum.getCode();
this.message = errorCodeEnum.getDesc();
this.data = data;
}
public BaseHttpResponse(boolean success, T data) {
this.success = success;
this.data = data;
}
public BaseHttpResponse(boolean success) {
this.success = success;
}
public static BaseHttpResponse<Void> success() {
return new BaseHttpResponse<>(true, 0, "成功", null);
}
public static <T> BaseHttpResponse<T> success(T data) {
return new BaseHttpResponse<>(true, 0, "成功", data);
}
public static BaseHttpResponse<Void> fail(ErrorCodeEnum errorCodeEnum) {
return new BaseHttpResponse<>(false, errorCodeEnum);
}
public static BaseHttpResponse<Void> fail(ErrorCodeEnum errorCodeEnum, String msg) {
return new BaseHttpResponse<>(false, errorCodeEnum.getCode(), msg);
}
public static <T> BaseHttpResponse<T> unknownError(T data) {
return new BaseHttpResponse<>(false, ErrorCodeEnum.UNKNOWN_ERROR, data);
}
}

@ -0,0 +1,16 @@
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/usm?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&rewriteBatchedStatements=true
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
mapper-locations:
- classpath*:com/htfp/service/dao/mapper/*.xml
type-aliases-package: com.htfp.service.usm.dao.model
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug

@ -0,0 +1,29 @@
spring:
application:
name: htfp-uav-state-manager
datasource:
url: jdbc:mysql://127.0.0.1:3306/usm?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&rewriteBatchedStatements=true
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
mapper-locations:
- classpath*:com/htfp/service/dao/mapper/*.xml
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
#aliyun:
# oss:
# endpoint: "https://oss-cn-shanghai.aliyuncs.com"
# bucketName: "java-ai-test-sy"
# region: "cn-shanghai

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度%logger显示日志记录器的名称 %msg表示日志消息%n表示换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %magenta([%thread]) %cyan(%logger{36})-%msg%n</pattern>
</encoder>
</appender>
<!-- 系统文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 日志文件输出的文件名, %i表示序号 -->
<FileNamePattern>./log/usm-%d{yyyy-MM-dd}-%i.log</FileNamePattern>
<!-- 最多保留的历史日志文件数量 -->
<MaxHistory>30</MaxHistory>
<!-- 最大文件大小,超过这个大小会触发滚动到新文件,默认为 10MB -->
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度%msg表示日志消息%n表示换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{25}-%msg%n</pattern>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>

@ -0,0 +1,13 @@
package com.htfp.service.usm.app;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class UsmAppApplicationTests {
@Test
void contextLoads() {
}
}

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.htfp.service</groupId>
<artifactId>usm</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.htfp.service</groupId>
<artifactId>usm-biz</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.htfp.service</groupId>
<artifactId>usm-common</artifactId>
</dependency>
<dependency>
<groupId>com.htfp.service</groupId>
<artifactId>usm-dao</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,43 @@
package com.htfp.service.usm.biz.exception;
import com.htfp.service.usm.common.enums.ErrorCodeEnum;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 11:18
**/
public class BusinessException extends RuntimeException {
private final ErrorCodeEnum errorCodeEnum;
private final String content;
// 场景1仅消息
public BusinessException(String content) {
super(content);
this.content = content;
this.errorCodeEnum = ErrorCodeEnum.UNKNOWN_ERROR;
}
// 场景2仅枚举
public BusinessException(ErrorCodeEnum errorCodeEnum) {
super(errorCodeEnum.getDesc());
this.content = null;
this.errorCodeEnum = errorCodeEnum;
}
// 场景3枚举 + 自定义信息
public BusinessException(ErrorCodeEnum errorCodeEnum, String content) {
super(content);
this.errorCodeEnum = errorCodeEnum;
this.content = content;
}
public ErrorCodeEnum getErrorCodeEnum() {
return errorCodeEnum;
}
public String getcontent() {
return content;
}
}

@ -0,0 +1,20 @@
package com.htfp.service.usm.biz.model.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 09:04
**/
@Data
public class BatchDeleteLabelRequest {
@NotNull(message = "标签列表不能为空")
@Size(min = 1, message = "请选择要删除的标签")
private List<Integer> labelIdList;
}

@ -0,0 +1,18 @@
package com.htfp.service.usm.biz.model.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 09:04
**/
@Data
public class InsertLabelRequest {
@NotBlank @Size(max = 16, message = "标签名称不能超过16个字符")
private String labelName;
private String labelInfo;
}

@ -0,0 +1,101 @@
package com.htfp.service.usm.biz.model.request;
import com.htfp.service.usm.biz.validator.ValidEnum;
import com.htfp.service.usm.common.enums.UavMarkerEnum;
import com.htfp.service.usm.common.enums.UavUsageStatusEnum;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-11 10:54
**/
@Data
public class InsertUavInfoRequest {
/**
* uavId,
*/
private Integer id;
/**
*
*/
@NotBlank(message = "无人机唯一识别码不能为空")
private String uavSn;
/**
* , 1, 2...
*/
@NotNull(message = "无人机编号不能为空")
private Integer uavNumber;
/**
* : enum
*/
@NotNull(message = "无人机型号不能为空")
private Integer uavModel;
/**
*
*/
private String configurationInfo;
/**
*
*/
@NotBlank(message = "无人机生产厂家不能为空")
private String manufacturer;
/**
*
*/
@NotNull(message = "无人机生产日期不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate productionDate;
/**
*
*/
@NotBlank(message = "无人机生产地点不能为空")
private String productionSite;
/**
*
*/
@NotNull(message = "无人机出厂批次不能为空")
private Integer productionBatch;
/**
* : enum
*/
@NotNull(message = "无人机涂装标识不能为空")
@ValidEnum(enumClass = UavMarkerEnum.class, message = "无人机涂装类型枚举错误")
private Integer paintMark;
/**
* 使: 0-, 1-, 2-, 3-, 4-, 5-, 6-
*/
@NotNull(message = "无人机使用状态不能为空")
@ValidEnum(enumClass = UavUsageStatusEnum.class, message = "无人机使用类型枚举错误")
private Integer usageStatus;
/**
*
*/
@NotBlank(message = "无人机当前所在地不能为空")
private String currentLocation;
/**
*
*/
private List<Integer> labelIdList;
}

@ -0,0 +1,16 @@
package com.htfp.service.usm.biz.model.request;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-11 15:05
**/
@Data
public class QueryUavInfoByIdRequest {
@NotNull(message = "无人机id不能为空")
private Integer uavId;
}

@ -0,0 +1,16 @@
package com.htfp.service.usm.biz.model.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-11 15:07
**/
@Data
public class QueryUavInfoBySnRequest {
@NotBlank(message = "无人机sn不能为空")
private String uavSn;
}

@ -0,0 +1,22 @@
package com.htfp.service.usm.biz.model.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 10:33
**/
@Data
public class UpdateLabelRequest {
@NotNull(message = "标签id不能为空")
private Integer id;
@NotBlank
@Size(max = 16, message = "标签名称不能超过16个字符")
private String labelName;
private String labelInfo;
}

@ -0,0 +1,17 @@
package com.htfp.service.usm.biz.model.response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 10:20
**/
@Data @NoArgsConstructor @AllArgsConstructor
public class LabelVO {
private Long id;
private String labelName;
private String labelInfo;
}

@ -0,0 +1,60 @@
package com.htfp.service.usm.biz.service;
import com.htfp.service.usm.biz.model.request.InsertLabelRequest;
import com.htfp.service.usm.biz.model.request.UpdateLabelRequest;
import com.htfp.service.usm.biz.model.response.LabelVO;
import com.htfp.service.usm.dao.model.entity.LabelDO;
import java.util.List;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 09:11
**/
public interface ILabelService {
/**
*
* @param insertLabelRequest
* @return
*/
Integer insertLabel(InsertLabelRequest insertLabelRequest);
/**
*
* @return
*/
List<LabelVO> queryAllLabels();
/**
* id
* @return
*/
List<Long> queryAllLabelsId();
/**
* ID
* @param id ID
* @return
*/
LabelVO queryLabelById(Integer id);
/**
*
* @param labelName
* @return
*/
LabelVO queryLabelByName(String labelName);
/**
*
* @param ids ID
*/
void batchDeleteLabels(List<Integer> ids);
/**
*
* @param updateLabelRequest
*/
void updateLabel(UpdateLabelRequest updateLabelRequest);
}

@ -0,0 +1,50 @@
package com.htfp.service.usm.biz.service;
import com.htfp.service.usm.biz.model.request.InsertUavInfoRequest;
import com.htfp.service.usm.dao.model.entity.UavInfoDO;
import com.htfp.service.usm.dao.model.param.QueryUavInfoParam;
import java.util.List;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-10 17:33
**/
public interface IUavInfoService {
/**
* id
* @param uavId
* @return
*/
UavInfoDO queryUavInfoById(Integer uavId);
/**
* sn
* @param uavSn
* @return
*/
UavInfoDO queryUavInfoBySn(String uavSn);
/**
*
* @param queryUavInfoParam
* @return List<<UavInfoDO>UavInfoDO</UavInfoDO>>
*/
List<UavInfoDO> queryUavInfoPageByCondition(QueryUavInfoParam queryUavInfoParam);
/**
*
* @param insertUavInfoRequest
* @return Integer id
*/
Integer insertUavInfo(InsertUavInfoRequest insertUavInfoRequest);
/**
*
* @param uavId id
* @param labelIdList id
*/
void setUavLabelMapping(Integer uavId, List<Integer> labelIdList);
}

@ -0,0 +1,17 @@
package com.htfp.service.usm.biz.service;
import java.util.List;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 16:28
**/
public interface IUavLabelMappingService {
int removeAllLabelsByUavId(Long uavId);
int removeAllLabelsByLabelIds(List<Long> labelIdList);
void addLabels(Long uavId, List<Long> labelIdList);
}

@ -0,0 +1,48 @@
package com.htfp.service.usm.biz.service.impl;
import com.htfp.service.usm.biz.service.IUavLabelMappingService;
import com.htfp.service.usm.dao.mapper.UavLabelMappingMapper;
import com.htfp.service.usm.dao.model.mapping.UavLabelMappingDO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 16:29
**/
@Service
public class IUavLabelMappingServiceImpl implements IUavLabelMappingService {
@Resource
UavLabelMappingMapper uavLabelMappingMapper;
@Transactional
@Override
public int removeAllLabelsByUavId(Long uavId) {
return uavLabelMappingMapper.deleteByUavId(uavId);
}
@Override
public int removeAllLabelsByLabelIds(List<Long> labelIdList) {
return uavLabelMappingMapper.deleteByLabelIds(labelIdList);
}
@Transactional
@Override
public void addLabels(Long uavId, List<Long> labelIdList) {
List<UavLabelMappingDO> uavLabelMappingDOList = new ArrayList<>();
for (Long labelId : labelIdList) {
UavLabelMappingDO uavLabelMappingDO = new UavLabelMappingDO(null, uavId, labelId, 0, LocalDateTime.now(), LocalDateTime.now());
uavLabelMappingDOList.add(uavLabelMappingDO);
}
uavLabelMappingMapper.batchInsert(uavLabelMappingDOList);
}
}

@ -0,0 +1,98 @@
package com.htfp.service.usm.biz.service.impl;
import com.htfp.service.usm.biz.exception.BusinessException;
import com.htfp.service.usm.biz.model.request.InsertLabelRequest;
import com.htfp.service.usm.biz.model.request.UpdateLabelRequest;
import com.htfp.service.usm.biz.model.response.LabelVO;
import com.htfp.service.usm.biz.service.ILabelService;
import com.htfp.service.usm.biz.service.IUavLabelMappingService;
import com.htfp.service.usm.common.enums.ErrorCodeEnum;
import com.htfp.service.usm.dao.mapper.LabelMapper;
import com.htfp.service.usm.dao.model.entity.LabelDO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 09:12
**/
@Service
public class LabelServiceImpl implements ILabelService {
@Resource
private LabelMapper labelMapper;
@Resource
private IUavLabelMappingService uavLabelMappingService;
/*InsertRequest -> DO
* createTime updateTime
*/
private LabelDO buildLabelDOFromInsertRequest(InsertLabelRequest insertLabelRequest) {
return new LabelDO(null, insertLabelRequest.getLabelName(), insertLabelRequest.getLabelInfo(), 0, LocalDateTime.now(), LocalDateTime.now());
}
/*UpdateRequest -> DO
* updateTime
*/
private LabelDO buildLabelDOFromUpdateRequest(UpdateLabelRequest updateLabelRequest) {
return new LabelDO(Long.valueOf(updateLabelRequest.getId()), updateLabelRequest.getLabelName(), updateLabelRequest.getLabelInfo(), 0, null, LocalDateTime.now());
}
private LabelVO buildLabelVOFromDO(LabelDO labelDO) {
return new LabelVO(labelDO.getId(), labelDO.getLabelName(), labelDO.getLabelInfo());
}
@Override
public Integer insertLabel(InsertLabelRequest insertLabelRequest) {
String labelName = insertLabelRequest.getLabelName();
if (labelMapper.selectLabelByName(labelName)!=null) {
throw new BusinessException(ErrorCodeEnum.DUPLICATE_ENTRY, "标签名称已存在");
}
LabelDO labelDO = buildLabelDOFromInsertRequest(insertLabelRequest);
labelMapper.insertLabel(labelDO);
return labelDO.getId().intValue();
}
@Override
public List<LabelVO> queryAllLabels() {
List<LabelDO> labelDOList = labelMapper.selectAll();
return labelDOList.stream().map(this::buildLabelVOFromDO).collect(Collectors.toList());
}
@Override
public List<Long> queryAllLabelsId() {
return labelMapper.selectAllIds();
}
@Override
public LabelVO queryLabelById(Integer id) {
LabelDO labelDO = labelMapper.selectLabelById(id);
return buildLabelVOFromDO(labelDO);
}
@Override
public LabelVO queryLabelByName(String labelName) {
LabelDO labelDO = labelMapper.selectLabelByName(labelName);
return buildLabelVOFromDO(labelDO);
}
@Override
public void batchDeleteLabels(List<Integer> labelIdList) {
// 删除labelId对应的映射关系
List<Long> labelIdListLong = labelIdList.stream().map(Long::valueOf).collect(Collectors.toList());
uavLabelMappingService.removeAllLabelsByLabelIds(labelIdListLong);
labelMapper.deleteLabels(labelIdList);
}
@Override
public void updateLabel(UpdateLabelRequest updateLabelRequest) {
LabelDO labelDO = buildLabelDOFromUpdateRequest(updateLabelRequest);
labelMapper.updateLabel(labelDO);
}
}

@ -0,0 +1,94 @@
package com.htfp.service.usm.biz.service.impl;
import com.htfp.service.usm.biz.exception.BusinessException;
import com.htfp.service.usm.biz.service.ILabelService;
import com.htfp.service.usm.biz.service.IUavInfoService;
import com.htfp.service.usm.biz.model.request.InsertUavInfoRequest;
import com.htfp.service.usm.biz.service.IUavLabelMappingService;
import com.htfp.service.usm.common.enums.ErrorCodeEnum;
import com.htfp.service.usm.dao.mapper.UavInfoMapper;
import com.htfp.service.usm.dao.model.entity.UavInfoDO;
import com.htfp.service.usm.dao.model.param.QueryUavInfoParam;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import javax.validation.constraints.NotEmpty;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-10 17:33
**/
@Service
public class UavInfoServiceImpl implements IUavInfoService {
@Resource
UavInfoMapper uavInfoMapper;
@Resource
ILabelService labelService;
@Resource
IUavLabelMappingService uavLabelMappingService;
@Override
public UavInfoDO queryUavInfoById(Integer uavId) {
return uavInfoMapper.selectByUavId(Long.valueOf(uavId));
}
@Override
public UavInfoDO queryUavInfoBySn(String uavSn) {
return uavInfoMapper.selectByUavSn(uavSn);
}
@Override
public List<UavInfoDO> queryUavInfoPageByCondition(QueryUavInfoParam queryUavInfoParam) {
return uavInfoMapper.selectByCondition(queryUavInfoParam);
}
@Transactional
@Override
public Integer insertUavInfo(InsertUavInfoRequest insertUavInfoRequest) {
// TODO 2025/6/11: 校验相关数据是否已存在
if (queryUavInfoBySn(insertUavInfoRequest.getUavSn()) != null) {
throw new BusinessException(ErrorCodeEnum.DUPLICATE_ENTRY, "无人机sn重复请重新录入");
}
UavInfoDO uavInfoDO = buildUavInfoDOFromInsertRequest(insertUavInfoRequest);
uavInfoDO.setCreateTime(LocalDateTime.now());
uavInfoDO.setUpdateTime(LocalDateTime.now());
uavInfoMapper.insertUavInfo(uavInfoDO);
int uavId = uavInfoDO.getId().intValue();
List<Integer> labelIdList = insertUavInfoRequest.getLabelIdList();
if (!CollectionUtils.isEmpty(labelIdList)) {
setUavLabelMapping(uavId, labelIdList);
}
return uavId;
}
/** 设置无人机标签*/
@Override
public void setUavLabelMapping(Integer uavId, @NotEmpty List<Integer> labelIdList) {
// Set<Integer> existLabelIdList = uavLabelMappingMapper.selectLabelIdsByUavId(Long.valueOf(uavId));
uavLabelMappingService.removeAllLabelsByUavId(Long.valueOf(uavId));
List<Long> labelIdListLong = labelIdList.stream().map(Long::valueOf).collect(Collectors.toList());
// 筛选不存在的标签
List<Long> allLabelsInDB = labelService.queryAllLabelsId();
List<Long> labelIdNotExist = labelIdListLong.stream()
.filter(labelId -> !allLabelsInDB.contains((labelId))).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(labelIdNotExist)) {
throw new BusinessException(ErrorCodeEnum.DATA_NOT_EXIST, "标签id="+labelIdNotExist.toString()+"不存在");
}
uavLabelMappingService.addLabels(Long.valueOf(uavId), labelIdListLong);
}
private UavInfoDO buildUavInfoDOFromInsertRequest(InsertUavInfoRequest insertUavInfoRequest) {
return new UavInfoDO(null, insertUavInfoRequest.getUavSn(), insertUavInfoRequest.getUavNumber(), insertUavInfoRequest.getUavModel(), insertUavInfoRequest.getConfigurationInfo(), insertUavInfoRequest.getManufacturer(), insertUavInfoRequest.getProductionDate(), insertUavInfoRequest.getProductionSite(), insertUavInfoRequest.getProductionBatch(), insertUavInfoRequest.getPaintMark(), insertUavInfoRequest.getUsageStatus(), insertUavInfoRequest.getCurrentLocation(), 0, null, null);
}
}

@ -0,0 +1,16 @@
package com.htfp.service.usm.biz.validator;
/**
* @Author sunjipeng
* @Date 2022-05-18 15:52
* @Description
*/
public interface BaseValidate<R, T> {
/**
*
*
* @param data
* @return
*/
R validate(T data);
}

@ -0,0 +1,43 @@
package com.htfp.service.usm.biz.validator;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
@Documented
@Constraint(validatedBy = EnumValueValidator.class)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidEnum {
String message() default "Invalid enum value";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends Enum<?>> enumClass();
}
class EnumValueValidator implements ConstraintValidator<ValidEnum, Object> {
private List<Object> enumValues;
@Override
public void initialize(ValidEnum annotation) {
try {
Method method = annotation.enumClass().getMethod("values");
enumValues = Arrays.asList((Enum[]) method.invoke(null));
} catch (Exception e) {
throw new IllegalArgumentException("Failed to initialize enum validator.");
}
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) return true;
return enumValues.stream().anyMatch(v -> v.toString().equals(value.toString()));
}
}

@ -0,0 +1,26 @@
package com.htfp.service.usm.biz.service.impl;
import com.htfp.service.usm.dao.mapper.LabelMapper;
import org.junit.jupiter.api.Test;
import javax.annotation.Resource;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 13:42
**/
class LabelServiceImplTest {
@Resource
private LabelMapper labelMapper;
@Test
void queryLabelById() {
}
@Test
void queryLabelByName() {
}
}

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.htfp.service</groupId>
<artifactId>usm</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>usm-common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,48 @@
package com.htfp.service.usm.common.enums;
/**
* @Author sunjipeng
* @Date 2022-05-18 16:39
* @Description
*/
public enum ErrorCodeEnum {
SUCCESS(0, "成功"),
// 系统错误
SYSTEM_ERROR(500, "系统异常"),
// 业务错误
// 校验错误
PARAM_VALIDATION_ERROR(1001, "参数校验失败"),
SQL_ERROR(3000, "数据库错误"),
DUPLICATE_ENTRY(3001, "数据重复"),
DATA_NOT_EXIST(3002, "数据不存在"),
UNKNOWN_ERROR(9999, "未知错误"),
;
public final Integer code;
public final String desc;
ErrorCodeEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public String getDesc() {
return desc;
}
public static ErrorCodeEnum getFromCode(Integer code) {
for (ErrorCodeEnum errorCodeEnum : values()) {
if (errorCodeEnum.code.equals(code)) {
return errorCodeEnum;
}
}
return null;
}
}

@ -0,0 +1,38 @@
package com.htfp.service.usm.common.enums;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-11 10:58
**/
public enum UavMarkerEnum {
MARKER1(0, "涂装标识1"),
OTHERS(99, "其他"),
;
public final Integer code;
public final String desc;
UavMarkerEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public String getDesc() {
return desc;
}
public static UavMarkerEnum getFromCode(Integer code) {
for (UavMarkerEnum enumItem : values()) {
if (enumItem.code.equals(code)) {
return enumItem;
}
}
return null;
}
}

@ -0,0 +1,38 @@
package com.htfp.service.usm.common.enums;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-11 10:58
**/
public enum UavModelEnum {
FP98(0, "FP98"),
OTHERS(99, "其他"),
;
public final Integer code;
public final String desc;
UavModelEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public String getDesc() {
return desc;
}
public static UavModelEnum getFromCode(Integer code) {
for (UavModelEnum enumItem : values()) {
if (enumItem.code.equals(code)) {
return enumItem;
}
}
return null;
}
}

@ -0,0 +1,44 @@
package com.htfp.service.usm.common.enums;
/**
* @author : shi_y
* @description : 使
* @createTime : 2025-06-11 10:58
**/
public enum UavUsageStatusEnum {
// 0-闲置, 1-维修, 2-任务中, 3-试验, 4-交付, 5-报废, 6-库房中
IDLE(0, "闲置"),
REPAIRING(1, "维修"),
IN_MISSION(2, "任务中"),
TESTING(3, "试验"),
DELIVERED(4, "交付"),
SCRAPED(5, "报废"),
IN_STORAGE(6, "库房中"),
OTHERS(99, "其他"),
;
public final Integer code;
public final String desc;
UavUsageStatusEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public String getDesc() {
return desc;
}
public static UavUsageStatusEnum getFromCode(Integer code) {
for (UavUsageStatusEnum enumItem : values()) {
if (enumItem.code.equals(code)) {
return enumItem;
}
}
return null;
}
}

@ -0,0 +1,215 @@
package com.htfp.service.usm.common.utils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* JSON Jackson /
*/
public class JsonUtils {
private static final ObjectMapper objectMapper = new ObjectMapper();
// 默认配置
static {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 是否忽略未知字段
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 序列化时忽略null字段
objectMapper.enable(SerializationFeature.INDENT_OUTPUT); // 输出带格式的JSON
objectMapper.enable(SerializationFeature.FAIL_ON_SELF_REFERENCES);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
private JsonUtils() {}
/**
* ObjectMapper
*/
public static ObjectMapper getInstance() {
return objectMapper;
}
/**
* JSON
*
* @param obj
* @return JSON null null
* @throws JsonSerializeException
*/
public static String obj2json(@NotNull Object obj) {
if (obj == null) return null;
if (obj instanceof Map && mapSelfContain((Map) obj)){
// 循环检测
throw new JsonSerializeException("对象序列化失败,对象中包含自身引用");
}
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw new JsonSerializeException("对象序列化为 JSON 字符串失败", e);
}
}
private static boolean mapSelfContain(Map obj) {
return obj.containsValue(obj);
}
/**
* JSON
*
* @param jsonStr JSON
* @param clazz
* @param <T>
* @return jsonStr null
* @throws IllegalArgumentException
* @throws JsonDeserializeException
*/
public static <T> T json2obj(@NotNull String jsonStr, @NotNull Class<T> clazz) {
if (clazz == null) throw new IllegalArgumentException("参数 clazz 不能为空");
if (jsonStr == null || jsonStr.trim().isEmpty()) return null;
try {
return objectMapper.readValue(jsonStr, clazz);
} catch (IOException e) {
throw new JsonDeserializeException("JSON 字符串转对象失败: " + clazz.getSimpleName(), e);
}
}
/**
* JSON
*
* @param jsonStr JSON
* @param typeReference
* @param <T>
* @return jsonStr null
* @throws IllegalArgumentException
* @throws JsonDeserializeException
*/
public static <T> T json2obj(@NotNull String jsonStr, @NotNull TypeReference<T> typeReference) {
if (typeReference == null) throw new IllegalArgumentException("参数 typeReference 不能为空");
if (jsonStr == null || jsonStr.trim().isEmpty()) return null;
try {
return objectMapper.readValue(jsonStr, typeReference);
} catch (IOException e) {
throw new JsonDeserializeException("JSON 字符串转泛型对象失败", e);
}
}
/**
* JSON Map<String, Object>
*
* @param jsonStr JSON
* @return Map jsonStr null
* @throws JsonDeserializeException
*/
public static Map<String, Object> json2map(@NotNull String jsonStr) {
return json2obj(jsonStr, new TypeReference<Map<String, Object>>() {});
}
/**
* JSON Map<String, T>
*
* @param jsonStr JSON
* @param clazz
* @param <T>
* @return Map jsonStr null
* @throws IllegalArgumentException
* @throws JsonDeserializeException
*/
public static <T> Map<String, T> json2map(@NotNull String jsonStr, @NotNull Class<T> clazz) {
if (clazz == null) throw new IllegalArgumentException("参数 clazz 不能为空");
if (jsonStr == null || jsonStr.trim().isEmpty()) return null;
try {
JavaType type = objectMapper.getTypeFactory().constructMapType(Map.class, String.class, clazz);
return objectMapper.readValue(jsonStr, type);
} catch (IOException e) {
throw new JsonDeserializeException("JSON 字符串转 Map<String, " + clazz.getSimpleName() + "> 失败", e);
}
}
/**
* JSON List<T>
*
* @param jsonArrayStr JSON
* @param clazz
* @param <T>
* @return List null
* @throws IllegalArgumentException
* @throws JsonDeserializeException
*/
public static <T> List<T> json2list(@NotNull String jsonArrayStr, @NotNull Class<T> clazz) {
if (clazz == null) throw new IllegalArgumentException("参数 clazz 不能为空");
if (jsonArrayStr == null || jsonArrayStr.trim().isEmpty()) return null;
try {
JavaType type = objectMapper.getTypeFactory().constructCollectionType(List.class, clazz);
return objectMapper.readValue(jsonArrayStr, type);
} catch (IOException e) {
throw new JsonDeserializeException("JSON 数组转 List<" + clazz.getSimpleName() + "> 失败", e);
}
}
/**
* Map
*
* @param map Map
* @param clazz
* @param <T>
* @return map null null
* @throws IllegalArgumentException
*/
public static <T> T map2obj(@NotNull Map<String, ?> map, @NotNull Class<T> clazz) {
if (map == null) return null;
if (clazz == null) throw new IllegalArgumentException("参数 clazz 不能为空");
return objectMapper.convertValue(map, clazz);
}
/**
* JSON
*
* @param obj
* @param filePath
* @throws IllegalArgumentException
* @throws JsonSerializeException
*/
public static void obj2jsonFile(@NotNull Object obj, @NotNull String filePath) {
if (obj == null) throw new IllegalArgumentException("obj 不能为空");
if (filePath == null || filePath.trim().isEmpty()) {
throw new IllegalArgumentException("filePath 不能为空");
}
try {
objectMapper.writeValue(new File(filePath), obj);
} catch (IOException e) {
throw new JsonSerializeException("对象写入 JSON 文件失败: " + filePath, e);
}
}
/**
* JSON
*/
public static class JsonSerializeException extends RuntimeException {
public JsonSerializeException(String msg, Throwable cause) {
super(msg, cause);
}
public JsonSerializeException(String msg) {
super(msg);
}
}
/**
* JSON
*/
public static class JsonDeserializeException extends RuntimeException {
public JsonDeserializeException(String msg, Throwable cause) {
super(msg, cause);
}
public JsonDeserializeException(String msg) {
super(msg);
}
}
}

@ -0,0 +1,277 @@
package com.htfp.service.usm.common.utils;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.type.TypeReference;
import com.htfp.service.usm.common.utils.JsonUtils.JsonDeserializeException;
import com.htfp.service.usm.common.utils.JsonUtils.JsonSerializeException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
class JsonUtilsTest {
@Data @NoArgsConstructor@AllArgsConstructor
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
private static class TestBean {
private String name;
private int age;
// 省略 getter/setter 以保持示例简洁
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestBean testBean = (TestBean) o;
if (age != testBean.age) return false;
return Objects.equals(name, testBean.name);
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
@Data
private static class TestBeanWithList {
private String name;
private int age;
private List<String> hobbies;
// 省略 getter/setter 以保持示例简洁
}
// 测试泛型结构
@Data
private static class TestBeanWithGeneric <K> {
private K key;
// 省略 getter/setter 以保持示例简洁
}
// 测试循环引用
@Data
private static class CircularReference {
private CircularReference self;
// 省略 getter/setter 以保持示例简洁
}
@Data @NoArgsConstructor @AllArgsConstructor
private static class TestChildBeanA extends TestBean {
private String fieldChildA;
}
@Data @NoArgsConstructor @AllArgsConstructor
private static class TestChildBeanB extends TestBean {
private String fieldChildB;
}
// 测试 obj2json 正常序列化
@Test
void testObj2json_Normal() {
TestBean bean = new TestBean("Alice", 30);
String json = JsonUtils.obj2json(bean);
assertNotNull(json);
assertTrue(json.contains("name") && json.contains("age"));
}
// 测试 obj2json 对 null 的处理
@Test
void testObj2json_Null() {
assertNull(JsonUtils.obj2json(null));
}
// 测试 obj2json 抛出异常(当输入对象包含循环引用时)
@Test
void testObj2json_Exception() {
// 创建一个带有循环引用的对象
Map<String, Object> map = new HashMap<>();
map.put("map", map); // 循环引用
assertThrows(JsonSerializeException.class, () -> JsonUtils.obj2json(map));
}
// 测试继承关系序列化
@Test
void testObj2json_ParentChild() {
TestChildBeanA childA = new TestChildBeanA();
childA.setName("childA");
childA.setAge(40);
childA.setFieldChildA("fieldChildA");
TestChildBeanB childB = new TestChildBeanB();
childB.setName("childB");
childB.setAge(30);
childB.setFieldChildB("fieldChildB");
List<TestBean> beanList = Arrays.asList(childA, childB);
String json = JsonUtils.obj2json(beanList);
System.out.println(json);
assertNotNull(json);
assertTrue(json.contains("childA") && json.contains("childB"));
assertTrue(json.contains("fieldChildA") && json.contains("fieldChildB"));
}
// 测试继承关系反序列化
// @Test
// void testJson2obj_ParentChild() {
// String json = "[{\"name\":\"childA\",\"age\":40,\"fieldChildA\":\"fieldChildA\"},{\"name\":\"childB\",\"age\":30,\"fieldChildB\":\"fieldChildB\"}]";
//
// List<TestBean> beanList = JsonUtils.json2list(json, TestBean.class);
// assertNotNull(beanList);
// assertEquals(2, beanList.size());
// assertEquals("childA", beanList.get(0).getName());
// assertEquals(40, beanList.get(0).getAge());
// assertEquals("fieldChildA", ((TestChildBeanA) beanList.get(0)).getFieldChildA());
// assertEquals("childB", beanList.get(1).getName());
// }
@Test
void testObj2json_SelfReference() {
// 创建一个带有循环引用的对象
CircularReference circularReference = new CircularReference();
circularReference.self = circularReference;
assertThrows(JsonSerializeException.class, () -> JsonUtils.obj2json(circularReference));
}
// 测试 json2obj 正常反序列化
@Test
void testJson2obj_StringClass_Normal() {
String json = "{\"name\":\"Bob\",\"age\":25}";
TestBean bean = JsonUtils.json2obj(json, TestBean.class);
assertNotNull(bean);
assertEquals("Bob", bean.name);
assertEquals(25, bean.age);
}
// 测试 json2obj 对空字符串的处理
@Test
void testJson2obj_StringClass_EmptyString() {
assertNull(JsonUtils.json2obj("", TestBean.class));
}
// 测试 json2obj 对 null 的处理
@Test
void testJson2obj_StringClass_Null() {
assertNull(JsonUtils.json2obj(null, TestBean.class));
}
// 测试 json2obj 泛型版本正常反序列化
@Test
void testJson2obj_TypeReference_Normal() {
String json = "{\"name\":\"Charlie\",\"age\":40}";
TestBean bean = JsonUtils.json2obj(json, new TypeReference<TestBean>() {});
assertNotNull(bean);
assertEquals("Charlie", bean.name);
assertEquals(40, bean.age);
}
// 测试 json2obj 泛型版本对嵌套结构的处理
@Test
void testJson2obj_TypeReference_Nested() {
String jsonString = "{\"key\":\"ok\"}";
TestBeanWithGeneric<String> bean = JsonUtils.json2obj(jsonString, new TypeReference<TestBeanWithGeneric<String>>() {});
assertNotNull(bean);
assertEquals("ok", bean.key);
String jsonInteger = "{\"key\":10}";
TestBeanWithGeneric<Integer> beanWithInt = JsonUtils.json2obj(jsonInteger, new TypeReference<TestBeanWithGeneric<Integer>>() {});
assertNotNull(beanWithInt);
}
// 测试 json2map 反序列化
@Test
void testJson2map() {
String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
Map<String, Object> map = JsonUtils.json2map(json);
assertNotNull(map);
assertEquals(2, map.size());
assertEquals("value1", map.get("key1"));
assertEquals("value2", map.get("key2"));
}
// 测试 json2map 带类型参数
@Test
void testJson2map_Class() {
String json = "{\"key1\":20,\"key2\":30}";
Map<String, Integer> map = JsonUtils.json2map(json, Integer.class);
assertNotNull(map);
assertEquals(2, map.size());
assertEquals(Integer.valueOf(20), map.get("key1"));
assertEquals(Integer.valueOf(30), map.get("key2"));
}
// 测试 json2list 反序列化
@Test
void testJson2list() {
String json = "[\"item1\",\"item2\",\"item3\"]";
List<String> list = JsonUtils.json2list(json, String.class);
assertNotNull(list);
assertEquals(3, list.size());
assertEquals("item1", list.get(0));
assertEquals("item2", list.get(1));
assertEquals("item3", list.get(2));
}
// 测试objList反序列化
@Test
void testJson2objList() {
// List<TestBean>
String json = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";
List<TestBean> list = JsonUtils.json2list(json, TestBean.class);
assertNotNull(list);
assertEquals(2, list.size());
assertEquals("Alice", list.get(0).name);
assertEquals(30, list.get(0).age);
assertEquals("Bob", list.get(1).name);
assertEquals(25, list.get(1).age);
}
@Test
void testJson2objListUseTypeReference() {
// List<TestBean>
String json = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";
List<TestBean> list = JsonUtils.json2obj(json, new TypeReference<List<TestBean>>() {});
assertNotNull(list);
assertEquals(2, list.size());
assertEquals("Alice", list.get(0).name);
assertEquals(30, list.get(0).age);
assertEquals("Bob", list.get(1).name);
assertEquals(25, list.get(1).age);
}
// 测试 map2obj 转换
@Test
void testMap2obj() {
Map<String, Object> map = new HashMap<>();
map.put("name", "David");
map.put("age", 35);
TestBean bean = JsonUtils.map2obj(map, TestBean.class);
assertNotNull(bean);
assertEquals("David", bean.name);
assertEquals(35, bean.age);
}
// 测试 map2obj 对 null 的处理
@Test
void testMap2obj_NullMap() {
assertNull(JsonUtils.map2obj(null, TestBean.class));
}
// 测试 map2obj 对 null 类的处理
@Test
void testMap2obj_NullClass() {
assertThrows(IllegalArgumentException.class, () -> {
JsonUtils.map2obj(new HashMap<>(), null);
});
}
}

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.htfp.service</groupId>
<artifactId>usm</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.htfp.service</groupId>
<artifactId>usm-dao</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>2.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,23 @@
# 设备清单主表
create table uav_device_list
(
id int primary key auto_increment comment '主键id',
device_sn varchar(64) not null comment '设备唯一识别码, 对外使用, 由业务员录入',
uav_id int not null comment '无人机id',
subsystem int not null comment '所属子系统, enum: 0-飞行器平台子系统, 1-动力子系统, 2-飞行控制子系统, 3-电气子系统, 4-机载数据链子系统',
device_type_id int not null comment '设备类型id',
device_name varchar(30) not null comment '设备名, 同一类设备可能有多个, 可用别名区分',
device_model varchar(30) comment '型号',
manufacturer varchar(100) comment '生产厂家',
install_date date not null comment '装机日期',
utilized_time int not null default 0 comment '已使用时间 (小时)',
expected_life int comment '预计寿命(年)',
status tinyint not null comment '设备状态, enum: 0-停用, 1-在用',
note text comment '备注',
create_time datetime,
update_time datetime,
key idx_uav_id (uav_id),
key idx_subsystem (subsystem),
unique key uk_uav_device (uav_id, device_sn) comment '同一飞机的设备sn不能重复',
unique key uk_device_status (device_sn, status) comment '一个设备只能处于一个状态'
) comment '无人机设备清单表';

@ -0,0 +1,14 @@
# 无人机与标签的映射表
drop table if exists uav_label_mapping;
create table if not exists uav_label_mapping
(
id bigint unsigned not null primary key auto_increment comment '主键id',
uav_id bigint unsigned not null comment '无人机id',
label_id bigint unsigned not null comment '标签id',
is_delete tinyint not null default 0 comment '是否删除, 0-否, 1-是',
create_time datetime comment '打标签时间',
update_time datetime comment '更新时间',
# foreign key (uav_basic_info_id) references uav_basic_info (id), # TODO : 删除
# foreign key (label_id) references label (id), # TODO : 删除
unique key unique_uav_label (uav_id, label_id, is_delete)
) comment '无人机与标签的映射表';

@ -0,0 +1,16 @@
# 设备信息主表
create table device_info
(
id int primary key auto_increment comment '主键id',
device_sn varchar(64) not null comment '设备唯一识别码, 对外使用, 由业务员录入',
device_type_id int not null comment '设备类型id',
device_name varchar(30) not null comment '设备名, 同一类设备可能有多个, 可用别名区分',
device_model varchar(30) comment '型号',
manufacturer varchar(100) comment '生产厂家',
install_date date not null comment '装机日期',
total_utilized_time int not null default 0 comment '累计已使用时间 (小时)',
expected_life int comment '预计寿命(年)',
note text comment '备注',
create_time datetime,
update_time datetime
) comment '无人机设备信息表';

@ -0,0 +1,10 @@
# 无人机设备类型定义
create table device_type (
id int primary key auto_increment comment '主键id',
uav_model int not null comment '无人机型号: enum',
subsystem tinyint not null comment '所属子系统: enum',
name varchar(20) not null comment '设备类型名称',
key idx_uav_model (uav_model),
key idx_subsystem (subsystem),
unique key uk_uav_model_subsystem_name (uav_model, subsystem, name)
) comment '无人机设备类型表';

@ -0,0 +1,11 @@
# 标签表
create table if not exists label
(
id bigint unsigned not null primary key auto_increment comment 'Id,主键',
label_name varchar(16) unique not null comment '标签名称',
label_info varchar(128) comment '标签描述',
is_delete tinyint not null default 0 comment '是否删除,0:未删除,1:已删除',
create_time datetime comment '创建时间',
update_time datetime comment '更新时间',
unique key uk_label_name (label_name, is_delete)
) comment '标签表';

@ -0,0 +1,23 @@
# 无人机基本信息表
create table if not exists uav_info
(
id bigint unsigned not null primary key auto_increment comment 'uavId, 主键',
uav_sn varchar(64) not null comment '无人机唯一识别码',
uav_number int not null comment '无人机编号, 例如1号机, 2号机...',
uav_model int not null comment '无人机型号: enum',
configuration_info varchar(128) comment '无人机构型说明',
manufacturer varchar(64) comment '无人机生产厂家',
production_date date comment '无人机生产日期',
production_site varchar(64) comment '无人机生产地点',
production_batch tinyint comment '无人机出厂批次',
paint_mark tinyint comment '无人机涂装标识: enum',
usage_status tinyint not null comment '无人机使用状态: 0-闲置, 1-维修, 2-任务中, 3-试验, 4-交付, 5-报废, 6-库房中',
current_location varchar(64) comment '无人机当前所在地',
is_delete tinyint not null default 0 comment '是否删除,0:未删除,1:已删除',
create_time datetime comment '创建时间',
update_time datetime comment '更新时间',
unique key uniq_sn_active (uav_sn, is_delete)
# foreign key (label_id) references label (uavId)
) comment '无人机基础信息表'
;

@ -0,0 +1,16 @@
# 设备拆装记录
create table device_mount_record
(
id int primary key auto_increment comment '主键id',
device_id varchar(64) not null comment '设备id',
uav_id bigint not null comment '无人机id',
mount_date date not null comment '装机日期',
unmount_date date comment '拆卸日期',
utilized_time int not null default 0 comment '已使用时间 (小时)',
expected_life int comment '预计寿命(年)',
record_status tinyint not null comment '设备状态, enum: 0-失效, 1-生效',
note text comment '备注',
create_time datetime,
update_time datetime,
index idx_uav_status (uav_id, record_status) comment '无人机所有的在用设备'
) comment '无人机设备清单表';

@ -0,0 +1,18 @@
# 无人机维修设备更换记录
create table uav_device_replace_record
(
id int primary key auto_increment comment '主键id',
repair_record_id int not null comment '维修记录id',
old_device_id int not null comment '旧设备id',
new_device_id int not null comment '新设备id',
is_replace tinyint not null comment '是否更换设备: 0-否, 1-是',
create_time datetime,
update_time datetime,
key idx_repair_id (repair_record_id),
# // TODO 2025/6/4: 删除物理外键
foreign key (repair_record_id) references device_repair_record (id),
foreign key (old_device_id) references device_info (id),
foreign key (new_device_id) references device_info (id)
) comment '维修更换设备记录表';

@ -0,0 +1,16 @@
# 维修记录附件表
create table uav_repair_attachment
(
id int primary key auto_increment comment '主键id',
repair_uni_id int not null comment '维修记录工单号',
attachment_type tinyint unsigned not null comment '附件类型, enum: 0-维修现场照片, 1-更换部件信息图片, 2-维修测试报告, 3-纸质维修记录表单图片, 255-其他',
file_name varchar(128) not null comment '文件名',
file_download_path varchar(200) not null comment '文件路径',
file_size int comment '文件大小(字节)',
upload_time datetime not null comment '上传时间',
note varchar(200) comment '备注',
create_time datetime,
update_time datetime,
index idx_repair_id (repair_uni_id),
foreign key (repair_uni_id) references uav_repair_record (id)
) comment '维修记录附件表';

@ -0,0 +1,28 @@
# 维修记录主表, 关联设备更换记录表
create table uav_maintenance_recordrepair_record
(
id int primary key auto_increment comment '主键id',
uniId varchar(32) not null comment '工单号',
uav_id int not null comment '无人机id',
uav_sn varchar(32) not null comment '飞机唯一识别码',
uav_model int not null comment '飞机型号',
repair_date date not null comment '维修日期',
repair_location varchar(64) comment '维修地点',
repair_type tinyint unsigned not null comment '维修类型, enum: 0-计划性维修, 2-非计划性维修, 3-缺陷修复, 255-其他',
support_doc tinyint unsigned not null comment '依据文件, enum: 0-问题报告单, 1-偏离单, 255-其他',
fault_description varchar(255) not null comment '故障/缺陷描述',
repair_measures varchar(255) not null comment '维修措施',
test_result tinyint unsigned not null comment '测试结果, enum: 0-未通过, 1-已通过, 2-待检验',
repair_person varchar(50) not null comment '维修负责人员',
quality_inspector varchar(50) not null comment '质检人员',
authorized_release_person varchar(50) not null comment '授权放行人员',
status tinyint not null default 'draft' comment '工单状态, enum: 0-草稿, 1-已提交, 2-已撤销, 3-已关闭',
create_time datetime,
update_time datetime,
# create_by varchar(50),
# update_by varchar(50),
# is_deleted tinyint(1) default 0,
unique key uk_uni_id (uniId),
index idx_uav_id (uav_id),
foreign key (uav_id) references uav_basic_info (id) # todo 记得删除
) comment '维修记录主表';

@ -0,0 +1,51 @@
# 飞行记录单主表
create table uav_flight_record
(
id int not null auto_increment primary key comment 'uavId, 主键',
task_uni_id varchar(64) not null unique comment '飞行记录唯一识别码',
task_name varchar(128) not null comment '任务名称',
pilot_name varchar(128) not null comment '执飞人员列表', # 是否需要管理飞手
uav_sn varchar(64) not null unique comment '无人机唯一识别码',
uav_id int not null comment '无人机信息id, uav_basic_info.uavId',
flight_date date not null comment '飞行日期',
start_time time not null comment '开始时间',
end_time time not null comment '结束时间',
flight_location varchar(128) comment '飞行地点',
flight_distance int comment '飞行距离km',
flight_mileage int comment '飞行里程km',
flight_duration int comment '飞行时长(秒)',
flight_altitude int comment '平均飞行高度(米)',
flight_speed_avg int comment '平均飞行速度m/s',
takeoff_airport_id int comment '起飞机场id',
landing_airport_id int comment '降落机场id',
takeoff_airport_temperature int comment '起飞机场温度°c',
landing_airport_temperature int comment '降落机场温度°c',
takeoff_wind_speed int comment '起飞风速(m/s), 1位小数',
takeoff_wind_direction int comment '起飞风向(°), 整数',
landing_wind_speed int comment '降落风速(m/s), 1位小数',
landing_wind_direction int comment '降落风向(°), 整数',
accumulation_flight_time int comment '累计飞行时长(秒)',
accumulation_flight_count int comment '累计飞行次数',
raw_data_form_id varchar(64) comment '原始数据表单号',
telemetry_data_path varchar(256) comment '遥测 dat 文件路径',
telemetry_command_path varchar(256) comment '遥控指令 dat 文件路径',
flight_param_path varchar(256) comment '飞参 txt 文件路径',
record_by varchar(32) comment '记录人',
create_time datetime comment '创建时间',
update_time datetime comment '更新时间',
# create_by varchar(32) comment '创建人',
# update_by varchar(32) comment '更新人',
foreign key (uav_id) references uav_basic_info (id) # todo 记得删除
) comment '无人机飞行任务记录表';

@ -0,0 +1,12 @@
package com.htfp.service.usm.dao.constant;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-11 17:03
**/
public final class TableName {
public static final String UAV_INFO = "uav_info";
public static final String LABEL = "label";
public static final String UAV_LABEL_MAPPING = "uav_label_mapping";
}

@ -0,0 +1,56 @@
package com.htfp.service.usm.dao.mapper;
import com.htfp.service.usm.dao.constant.TableName;
import com.htfp.service.usm.dao.model.entity.LabelDO;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* @author shi_y
* @description label()Mapper
* @createDate 2025-06-12 08:59:49
* @Entity com.htfp.service.usm.dao.model.entity.LabelDO
*/
@Mapper
public interface LabelMapper {
String TABLE_NAME = TableName.LABEL;
/**插入标签*/
@Insert("insert into "+TABLE_NAME+ "(id, label_name, label_info, is_delete, create_time, update_time) values (#{id}, #{labelName}, #{labelInfo}, #{isDelete}, #{createTime}, #{updateTime})")
@Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
void insertLabel(LabelDO labelDO);
/**根据id查询标签*/
@Select("select * from " + TABLE_NAME + " where id=#{id} and is_delete=0")
LabelDO selectLabelById(@Param("id") Integer id);
/**根据标签名称精确查询标签*/
@Select("select * from " + TABLE_NAME + " where label_name=#{labelName} and is_delete=0")
LabelDO selectLabelByName(@Param("labelName") String labelName);
/**查询所有标签*/
@Select("select * from "+TABLE_NAME+" where is_delete=0")
List<LabelDO> selectAll();
/**查询所有标签id*/
@Select("select id from " + TABLE_NAME + " where is_delete=0")
List<Long> selectAllIds();
/**批量删除标签*/
@Update("<script>" +
"update "+TABLE_NAME+" set is_delete=1, update_time=NOW()" +
" where id in" +
" <foreach collection='ids' item='id' open='(' separator=',' close=')'>#{id}</foreach>" +
"</script>")
void deleteLabels(List<Integer> ids);
/**更新标签*/
@Update("update "+TABLE_NAME+" set label_name=#{labelName}, label_info=#{labelInfo}, update_time=#{updateTime} where id=#{id} and is_delete=0")
void updateLabel(LabelDO labelDO);
}

@ -0,0 +1,47 @@
package com.htfp.service.usm.dao.mapper;
import com.htfp.service.usm.dao.constant.TableName;
import com.htfp.service.usm.dao.model.entity.UavInfoDO;
import com.htfp.service.usm.dao.model.param.QueryUavInfoParam;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* @author shi_y
* @description uav_info()Mapper
* @createDate 2025-06-11 16:59:09
* @Entity com.htfp.service.usm.dao.model.entity.UavInfoDO
*/
@Mapper
public interface UavInfoMapper {
String TABLE_NAME = TableName.UAV_INFO;
/**根据id查询UavInfo*/
@Select("select * from "+TABLE_NAME+" where id=#{id} and is_delete=0")
UavInfoDO selectByUavId(@Param("id") Long id);
/**根据sn查询UavInfo*/
@Select("select * from "+TABLE_NAME+" where uav_sn=#{uavSn} and is_delete=0")
UavInfoDO selectByUavSn(@Param("uavSn") String uavSn);
/**根据条件查询UavInfo*/
List<UavInfoDO> selectByCondition(QueryUavInfoParam queryUavInfoParam);
/**插入UavInfo*/
@Insert("insert into " + TABLE_NAME +
" (uav_sn, uav_number, uav_model, configuration_info, manufacturer, production_date, production_site," +
" production_batch, paint_mark, usage_status, current_location, is_delete, create_time, update_time)" +
" values (#{uavSn}, #{uavNumber}, #{uavModel}, #{configurationInfo}, #{manufacturer}," +
" #{productionDate}, #{productionSite}, #{productionBatch}, #{paintMark}, #{usageStatus}, #{currentLocation}," +
" #{isDelete}, #{createTime}, #{updateTime})"
)
@Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
Long insertUavInfo(UavInfoDO uavInfoDO);
}

@ -0,0 +1,60 @@
package com.htfp.service.usm.dao.mapper;
import com.htfp.service.usm.dao.constant.TableName;
import com.htfp.service.usm.dao.model.mapping.UavLabelMappingDO;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import java.util.List;
import java.util.Set;
/**
* @author shi_y
* @description uav_label_mapping()Mapper
* @createDate 2025-06-12 11:00:52
* @Entity com.htfp.service.usm.dao.model.mapping.UavLabelMappingDO
*/
@Mapper
public interface UavLabelMappingMapper {
String TABLE_NAME = TableName.UAV_LABEL_MAPPING;
@Insert("<script>" +
"insert into " + TABLE_NAME + "(uav_id, label_id, create_time, update_time) values " +
"<foreach collection='uavLabelMappingDOList' item='item' separator=','>" +
"(#{item.uavId}, #{item.labelId}, #{item.createTime}, #{item.updateTime})" +
"</foreach>" +
"</script>")
int batchInsert(List<UavLabelMappingDO> uavLabelMappingDOList);
@Insert("select id from uav_label_mapping where uav_id=#{uavId} and is_delete=0")
Set<Integer> selectLabelIdsByUavId(@Param("uavId") Long uavId);
/**删除uavId对应的所有标签*/
@Update("update " + TABLE_NAME + " set is_delete = 1, update_time = NOW() where uav_id = #{uavId} and is_delete = 0")
int deleteByUavId(Long uavId);
/**删除指定标签*/
@Update("<script>" +
"update " + TABLE_NAME + " set is_delete = 1, update_time = NOW() " +
"where is_delete=0 and label_id in " +
"<foreach collection='labelIdList' item='id' open='(' separator=',' close=')'>#{id}</foreach>" +
"</script>"
)
int deleteByLabelIds(List<Long> labelIdList);
/**删除uavId的指定标签*/
@Update("<script>" +
"update " + TABLE_NAME + " set is_delete = 1, update_time=NOW() " +
"where uav_id = #{uavId} and is_delete=0 and label_id in " +
"<foreach collection='labelIdList' item='id' open='(' separator=',' close=')'>#{id}</foreach>" +
"</script>")
int deleteLabelMapping(Long uavId, List<Long> labelIdList);
}

@ -0,0 +1,45 @@
package com.htfp.service.usm.dao.model.entity;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @TableName label
*/
@Data @NoArgsConstructor @AllArgsConstructor
public class LabelDO {
/**
* Id,
*/
private Long id;
/**
*
*/
private String labelName;
/**
*
*/
private String labelInfo;
/**
* ,0:,1:
*/
private Integer isDelete;
/**
*
*/
private LocalDateTime createTime;
/**
*
*/
private LocalDateTime updateTime;
}

@ -0,0 +1,91 @@
package com.htfp.service.usm.dao.model.entity;
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @TableName uav_info
*/
@Data @NoArgsConstructor @AllArgsConstructor
public class UavInfoDO {
/**
* uavId,
*/
private Long id;
/**
*
*/
private String uavSn;
/**
* , 1, 2...
*/
private Integer uavNumber;
/**
* : enum
*/
private Integer uavModel;
/**
*
*/
private String configurationInfo;
/**
*
*/
private String manufacturer;
/**
*
*/
private LocalDate productionDate;
/**
*
*/
private String productionSite;
/**
*
*/
private Integer productionBatch;
/**
* : enum
*/
private Integer paintMark;
/**
* 使: 0-, 1-, 2-, 3-, 4-, 5-, 6-
*/
private Integer usageStatus;
/**
*
*/
private String currentLocation;
/**
* ,0:,1:
*/
private Integer isDelete;
/**
*
*/
private LocalDateTime createTime;
/**
*
*/
private LocalDateTime updateTime;
}

@ -0,0 +1,44 @@
package com.htfp.service.usm.dao.model.mapping;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @TableName uav_label_mapping
*/
@Data @AllArgsConstructor @NoArgsConstructor
public class UavLabelMappingDO {
/**
* id
*/
private Long id;
/**
* id
*/
private Long uavId;
/**
* id
*/
private Long labelId;
/**
* , 0-, 1-
*/
private Integer isDelete;
/**
*
*/
private LocalDateTime createTime;
/**
*
*/
private LocalDateTime updateTime;
}

@ -0,0 +1,20 @@
package com.htfp.service.usm.dao.model.param;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @author shi_y
* @createDate 2023/9/7
* @description
*/
@Data
public class QueryPageInfoParam {
private Integer pageNum; // 当前页码
private Integer pageSize; // 每页数量
private LocalDateTime createStartTime;
private LocalDateTime createEndTime;
private Integer type;
private Integer status;
}

@ -0,0 +1,84 @@
package com.htfp.service.usm.dao.model.param;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-11 16:02
**/
@EqualsAndHashCode(callSuper = true)
@Data @NoArgsConstructor @AllArgsConstructor
public class QueryUavInfoParam extends QueryPageInfoParam {
/**
* id,
*/
private Integer id;
/**
*
*/
private String uavSn;
/**
* , 1, 2...
*/
private Integer uavNumber;
/**
* : enum
*/
private Integer uavModel;
/**
*
*/
private String configurationInfo;
/**
*
*/
private String manufacturer;
/**
*
*/
private LocalDate productionDate;
/**
*
*/
private String productionSite;
/**
*
*/
private Integer productionBatch;
/**
* : enum
*/
private Integer paintMark;
/**
* 使: 0-, 1-, 2-, 3-, 4-, 5-, 6-
*/
private Integer usageStatus;
/**
*
*/
private String currentLocation;
/**
* ,0:,1:
*/
private Integer isDelete = 0; // 默认查询未删除参数
}

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.htfp.service.usm.dao.mapper.LabelMapper">
<resultMap id="BaseResultMap" type="com.htfp.service.usm.dao.model.entity.LabelDO">
<id property="id" column="id" />
<result property="labelName" column="label_name" />
<result property="labelInfo" column="label_info" />
<result property="isDelete" column="is_delete" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="Base_Column_List">
id,label_name,label_info,is_delete,create_time,update_time
</sql>
</mapper>

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.htfp.service.usm.dao.mapper.UavInfoMapper">
<resultMap id="BaseResultMap" type="com.htfp.service.usm.dao.model.entity.UavInfoDO">
<id property="id" column="id"/>
<result property="uavSn" column="uav_sn"/>
<result property="uavNumber" column="uav_number"/>
<result property="uavModel" column="uav_model"/>
<result property="configurationInfo" column="configuration_info"/>
<result property="manufacturer" column="manufacturer"/>
<result property="productionDate" column="production_date"/>
<result property="productionSite" column="production_site"/>
<result property="productionBatch" column="production_batch"/>
<result property="paintMark" column="paint_mark"/>
<result property="usageStatus" column="usage_status"/>
<result property="currentLocation" column="current_location"/>
<result property="isDelete" column="is_delete"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<sql id="Base_Column_List">
id,uav_sn,uav_number,uav_model,configuration_info,manufacturer,
production_date,production_site,production_batch,paint_mark,usage_status,
current_location,is_delete,create_time,update_time
</sql>
<select id="selectByCondition" resultType="com.htfp.service.usm.dao.model.entity.UavInfoDO">
select * from uav_info
<where>
<!-- 字符串类型字段 -->
<if test="uavSn != null and uavSn != ''">
and uav_sn = #{uavSn}
</if>
<if test="manufacturer != null and manufacturer != ''">
and manufacturer = #{manufacturer}
</if>
<if test="currentLocation != null and currentLocation != ''">
and current_location = #{currentLocation}
</if>
<!-- 数值/枚举类型字段 -->
<if test="id != null">
and id = #{id}
</if>
<if test="uavNumber != null">
and uav_number = #{uavNumber}
</if>
<if test="uavModel != null">
and uav_model = #{uavModel}
</if>
<if test="configurationInfo != null and configurationInfo != ''">
and configuration_info = #{configurationInfo}
</if>
<if test="paintMark != null">
and paint_mark = #{paintMark}
</if>
<if test="usageStatus != null">
and usage_status = #{usageStatus}
</if>
<!-- 日期类型字段 -->
<if test="productionDate != null">
and production_date = #{productionDate}
</if>
<if test="productionSite != null and productionSite != ''">
and production_site = #{productionSite}
</if>
<if test="productionBatch != null">
and production_batch = #{productionBatch}
</if>
<!-- 固定条件:未删除 -->
<if test="createStartTime != null">
and create_time >= #{createStartTime}
</if>
<if test="createEndTime != null">
and create_time &lt;= #{createEndTime}
</if>
and is_delete = #{isDelete}
</where>
</select>
</mapper>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.htfp.service.usm.dao.mapper.UavLabelMappingMapper">
<resultMap id="BaseResultMap" type="com.htfp.service.usm.dao.model.mapping.UavLabelMappingDO">
<id property="id" column="id" />
<result property="uavId" column="uav_id" />
<result property="labelId" column="label_id" />
<result property="isDeleted" column="is_delet" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="Base_Column_List">
id,uav_id,label_id,is_deleted,create_time,update_time
</sql>
</mapper>

@ -0,0 +1,41 @@
package com.htfp.service.usm.dao.mapper;
import com.htfp.service.usm.dao.model.entity.LabelDO;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.boot.test.autoconfigure.MybatisTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 14:00
**/
@MybatisTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ContextConfiguration(classes = TestConfig.class) // 加载指定的配置类
// @TestPropertySource("classpath:application-test.yml") @TestPropertySource 默认只支持 .properties 文件。如果使用 .yml需要结合 @ContextConfiguration + 自定义加载器,较为复杂。
@ActiveProfiles("test")
class LabelMapperTest {
@Autowired
LabelMapper labelMapper;
@Test
void verifyDriver() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
}
@Test
void selectLabelById() {
Object labelDO = labelMapper.selectLabelById(5);
}
@Test
void selectLabelByName() {
}
}

@ -0,0 +1,14 @@
package com.htfp.service.usm.dao.mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
/**
* @author : shi_y
* @description :
* @createTime : 2025-06-12 14:04
**/
@Configuration
@MapperScan("com.htfp.service.usm.dao.mapper")
public class TestConfig {
}

@ -0,0 +1,16 @@
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/usm?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&rewriteBatchedStatements=true
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
mapper-locations:
- classpath*:com/htfp/service/dao/mapper/*.xml
type-aliases-package: com.htfp.service.usm.dao.model
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
Loading…
Cancel
Save